8151700: Add support for ES6 for-of
Reviewed-by: attila, sundar
This commit is contained in:
parent
666f0e49f6
commit
d50a34f94f
nashorn
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal
codegen
AssignSymbols.javaCodeGenerator.javaLocalVariableTypesCalculator.javaLower.javaOptimisticTypesCalculator.java
ir
objects
parser
runtime
test/script/basic
@ -803,7 +803,7 @@ final class AssignSymbols extends SimpleNodeVisitor implements Loggable {
|
||||
|
||||
@Override
|
||||
public Node leaveForNode(final ForNode forNode) {
|
||||
if (forNode.isForIn()) {
|
||||
if (forNode.isForInOrOf()) {
|
||||
return forNode.setIterator(lc, newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73
|
||||
}
|
||||
|
||||
|
@ -1753,7 +1753,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
return false;
|
||||
}
|
||||
enterStatement(forNode);
|
||||
if (forNode.isForIn()) {
|
||||
if (forNode.isForInOrOf()) {
|
||||
enterForIn(forNode);
|
||||
} else {
|
||||
final Expression init = forNode.getInit();
|
||||
@ -1768,7 +1768,15 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
private void enterForIn(final ForNode forNode) {
|
||||
loadExpression(forNode.getModify(), TypeBounds.OBJECT);
|
||||
method.invoke(forNode.isForEach() ? ScriptRuntime.TO_VALUE_ITERATOR : ScriptRuntime.TO_PROPERTY_ITERATOR);
|
||||
if (forNode.isForEach()) {
|
||||
method.invoke(ScriptRuntime.TO_VALUE_ITERATOR);
|
||||
} else if (forNode.isForIn()) {
|
||||
method.invoke(ScriptRuntime.TO_PROPERTY_ITERATOR);
|
||||
} else if (forNode.isForOf()) {
|
||||
method.invoke(ScriptRuntime.TO_ES6_ITERATOR);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unexpected for node");
|
||||
}
|
||||
final Symbol iterSymbol = forNode.getIterator();
|
||||
final int iterSlot = iterSymbol.getSlot(Type.OBJECT);
|
||||
method.store(iterSymbol, ITERATOR_TYPE);
|
||||
@ -3318,7 +3326,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
if (needsScope && varNode.isLet()) {
|
||||
method.loadCompilerConstant(SCOPE);
|
||||
method.loadUndefined(Type.OBJECT);
|
||||
final int flags = getScopeCallSiteFlags(identSymbol) | (varNode.isBlockScoped() ? CALLSITE_DECLARE : 0);
|
||||
final int flags = getScopeCallSiteFlags(identSymbol) | CALLSITE_DECLARE;
|
||||
assert isFastScope(identSymbol);
|
||||
storeFastScopeVar(identSymbol, flags);
|
||||
}
|
||||
|
@ -595,7 +595,7 @@ final class LocalVariableTypesCalculator extends SimpleNodeVisitor {
|
||||
}
|
||||
|
||||
final Expression init = forNode.getInit();
|
||||
if(forNode.isForIn()) {
|
||||
if(forNode.isForInOrOf()) {
|
||||
final JoinPredecessorExpression iterable = forNode.getModify();
|
||||
visitExpression(iterable);
|
||||
enterTestFirstLoop(forNode, null, init,
|
||||
|
@ -254,12 +254,12 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
|
||||
ForNode newForNode = forNode;
|
||||
|
||||
final Expression test = forNode.getTest();
|
||||
if (!forNode.isForIn() && isAlwaysTrue(test)) {
|
||||
if (!forNode.isForInOrOf() && isAlwaysTrue(test)) {
|
||||
newForNode = forNode.setTest(lc, null);
|
||||
}
|
||||
|
||||
newForNode = checkEscape(newForNode);
|
||||
if(!es6 && newForNode.isForIn()) {
|
||||
if(!es6 && newForNode.isForInOrOf()) {
|
||||
// Wrap it in a block so its internally created iterator is restricted in scope, unless we are running
|
||||
// in ES6 mode, in which case the parser already created a block to capture let/const declarations.
|
||||
addStatementEnclosedInBlock(newForNode);
|
||||
|
@ -130,7 +130,7 @@ final class OptimisticTypesCalculator extends SimpleNodeVisitor {
|
||||
|
||||
@Override
|
||||
public boolean enterForNode(final ForNode forNode) {
|
||||
if(forNode.isForIn()) {
|
||||
if(forNode.isForInOrOf()) {
|
||||
// for..in has the iterable in its "modify"
|
||||
tagNeverOptimistic(forNode.getModify());
|
||||
} else {
|
||||
|
@ -51,8 +51,11 @@ public final class ForNode extends LoopNode {
|
||||
/** Is this a normal for each in loop? */
|
||||
public static final int IS_FOR_EACH = 1 << 1;
|
||||
|
||||
/** Is this a ES6 for-of loop? */
|
||||
public static final int IS_FOR_OF = 1 << 2;
|
||||
|
||||
/** Does this loop need a per-iteration scope because its init contain a LET declaration? */
|
||||
public static final int PER_ITERATION_SCOPE = 1 << 2;
|
||||
public static final int PER_ITERATION_SCOPE = 1 << 3;
|
||||
|
||||
private final int flags;
|
||||
|
||||
@ -127,6 +130,10 @@ public final class ForNode extends LoopNode {
|
||||
init.toString(sb, printTypes);
|
||||
sb.append(" in ");
|
||||
modify.toString(sb, printTypes);
|
||||
} else if (isForOf()) {
|
||||
init.toString(sb, printTypes);
|
||||
sb.append(" of ");
|
||||
modify.toString(sb, printTypes);
|
||||
} else {
|
||||
if (init != null) {
|
||||
init.toString(sb, printTypes);
|
||||
@ -146,12 +153,12 @@ public final class ForNode extends LoopNode {
|
||||
|
||||
@Override
|
||||
public boolean hasGoto() {
|
||||
return !isForIn() && test == null;
|
||||
return !isForInOrOf() && test == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mustEnter() {
|
||||
if (isForIn()) {
|
||||
if (isForInOrOf()) {
|
||||
return false; //may be an empty set to iterate over, then we skip the loop
|
||||
}
|
||||
return test == null;
|
||||
@ -185,6 +192,23 @@ public final class ForNode extends LoopNode {
|
||||
public boolean isForIn() {
|
||||
return (flags & IS_FOR_IN) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a for-of loop?
|
||||
* @return true if this is a for-of loop
|
||||
*/
|
||||
public boolean isForOf() {
|
||||
return (flags & IS_FOR_OF) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a for-in or for-of statement?
|
||||
* @return true if this is a for-in or for-of loop
|
||||
*/
|
||||
public boolean isForInOrOf() {
|
||||
return isForIn() || isForOf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a for each construct, known from e.g. Rhino. This will be a for of construct
|
||||
* in ECMAScript 6
|
||||
@ -283,6 +307,6 @@ public final class ForNode extends LoopNode {
|
||||
* @return true if the containing block's scope object creator is required in codegen
|
||||
*/
|
||||
public boolean needsScopeCreator() {
|
||||
return isForIn() && hasPerIterationScope();
|
||||
return isForInOrOf() && hasPerIterationScope();
|
||||
}
|
||||
}
|
||||
|
47
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/AbstractIterator.java
47
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/AbstractIterator.java
@ -110,6 +110,41 @@ public abstract class AbstractIterator extends ScriptObject {
|
||||
return new IteratorResult(value, done, global);
|
||||
}
|
||||
|
||||
static MethodHandle getIteratorInvoker(final Global global) {
|
||||
return global.getDynamicInvoker(ITERATOR_INVOKER_KEY,
|
||||
() -> Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Object.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the invoker for the ES6 iterator {@code next} method.
|
||||
* @param global the global object
|
||||
* @return the next invoker
|
||||
*/
|
||||
public static InvokeByName getNextInvoker(final Global global) {
|
||||
return global.getInvokeByName(AbstractIterator.NEXT_INVOKER_KEY,
|
||||
() -> new InvokeByName("next", Object.class, Object.class, Object.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the invoker for the ES6 iterator result {@code done} property.
|
||||
* @param global the global object
|
||||
* @return the done invoker
|
||||
*/
|
||||
public static MethodHandle getDoneInvoker(final Global global) {
|
||||
return global.getDynamicInvoker(AbstractIterator.DONE_INVOKER_KEY,
|
||||
() -> Bootstrap.createDynamicInvoker("done", NashornCallSiteDescriptor.GET_PROPERTY, Object.class, Object.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the invoker for the ES6 iterator result {@code value} property.
|
||||
* @param global the global object
|
||||
* @return the value invoker
|
||||
*/
|
||||
public static MethodHandle getValueInvoker(final Global global) {
|
||||
return global.getDynamicInvoker(AbstractIterator.VALUE_INVOKER_KEY,
|
||||
() -> Bootstrap.createDynamicInvoker("value", NashornCallSiteDescriptor.GET_PROPERTY, Object.class, Object.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* ES6 7.4.1 GetIterator abstract operation
|
||||
*
|
||||
@ -126,8 +161,7 @@ public abstract class AbstractIterator extends ScriptObject {
|
||||
|
||||
if (Bootstrap.isCallable(getter)) {
|
||||
try {
|
||||
final MethodHandle invoker = global.getDynamicInvoker(ITERATOR_INVOKER_KEY,
|
||||
() -> Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Object.class));
|
||||
final MethodHandle invoker = getIteratorInvoker(global);
|
||||
|
||||
final Object value = invoker.invokeExact(getter, iterable);
|
||||
if (JSType.isPrimitive(value)) {
|
||||
@ -156,12 +190,9 @@ public abstract class AbstractIterator extends ScriptObject {
|
||||
|
||||
final Object iterator = AbstractIterator.getIterator(Global.toObject(iterable), global);
|
||||
|
||||
final InvokeByName nextInvoker = global.getInvokeByName(AbstractIterator.NEXT_INVOKER_KEY,
|
||||
() -> new InvokeByName("next", Object.class, Object.class, Object.class));
|
||||
final MethodHandle doneInvoker = global.getDynamicInvoker(AbstractIterator.DONE_INVOKER_KEY,
|
||||
() -> Bootstrap.createDynamicInvoker("done", NashornCallSiteDescriptor.GET_PROPERTY, Object.class, Object.class));
|
||||
final MethodHandle valueInvoker = global.getDynamicInvoker(AbstractIterator.VALUE_INVOKER_KEY,
|
||||
() -> Bootstrap.createDynamicInvoker("value", NashornCallSiteDescriptor.GET_PROPERTY, Object.class, Object.class));
|
||||
final InvokeByName nextInvoker = getNextInvoker(global);
|
||||
final MethodHandle doneInvoker = getDoneInvoker(global);
|
||||
final MethodHandle valueInvoker = getValueInvoker(global);
|
||||
|
||||
try {
|
||||
do {
|
||||
|
@ -892,7 +892,7 @@ loop:
|
||||
block();
|
||||
break;
|
||||
case VAR:
|
||||
variableStatement(type, true);
|
||||
variableStatement(type);
|
||||
break;
|
||||
case SEMICOLON:
|
||||
emptyStatement();
|
||||
@ -946,11 +946,11 @@ loop:
|
||||
if (singleStatement) {
|
||||
throw error(AbstractParser.message("expected.stmt", type.getName() + " declaration"), token);
|
||||
}
|
||||
variableStatement(type, true);
|
||||
variableStatement(type);
|
||||
break;
|
||||
}
|
||||
if (env._const_as_var && type == CONST) {
|
||||
variableStatement(TokenType.VAR, true);
|
||||
variableStatement(TokenType.VAR);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1047,7 +1047,7 @@ loop:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* VariableStatement :
|
||||
* var VariableDeclarationList ;
|
||||
*
|
||||
@ -1066,8 +1066,8 @@ loop:
|
||||
* Parse a VAR statement.
|
||||
* @param isStatement True if a statement (not used in a FOR.)
|
||||
*/
|
||||
private List<VarNode> variableStatement(final TokenType varType, final boolean isStatement) {
|
||||
return variableStatement(varType, isStatement, -1);
|
||||
private List<VarNode> variableStatement(final TokenType varType) {
|
||||
return variableStatement(varType, true, -1);
|
||||
}
|
||||
|
||||
private List<VarNode> variableStatement(final TokenType varType, final boolean isStatement, final int sourceOrder) {
|
||||
@ -1215,6 +1215,7 @@ loop:
|
||||
*
|
||||
* Parse a FOR statement.
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
private void forStatement() {
|
||||
final long forToken = token;
|
||||
final int forLine = line;
|
||||
@ -1235,6 +1236,7 @@ loop:
|
||||
JoinPredecessorExpression modify = null;
|
||||
|
||||
int flags = 0;
|
||||
boolean isForOf = false;
|
||||
|
||||
try {
|
||||
// FOR tested in caller.
|
||||
@ -1292,8 +1294,17 @@ loop:
|
||||
}
|
||||
break;
|
||||
|
||||
case IDENT:
|
||||
if (env._es6 && "of".equals(getValue())) {
|
||||
isForOf = true;
|
||||
// fall through
|
||||
} else {
|
||||
expect(SEMICOLON); // fail with expected message
|
||||
break;
|
||||
}
|
||||
case IN:
|
||||
flags |= ForNode.IS_FOR_IN;
|
||||
|
||||
flags |= isForOf ? ForNode.IS_FOR_OF : ForNode.IS_FOR_IN;
|
||||
test = new JoinPredecessorExpression();
|
||||
if (vars != null) {
|
||||
// for (var i in obj)
|
||||
@ -1301,32 +1312,31 @@ loop:
|
||||
init = new IdentNode(vars.get(0).getName());
|
||||
} else {
|
||||
// for (var i, j in obj) is invalid
|
||||
throw error(AbstractParser.message("many.vars.in.for.in.loop"), vars.get(1).getToken());
|
||||
throw error(AbstractParser.message("many.vars.in.for.in.loop", isForOf ? "of" : "in"), vars.get(1).getToken());
|
||||
}
|
||||
|
||||
} else {
|
||||
// for (expr in obj)
|
||||
assert init != null : "for..in init expression can not be null here";
|
||||
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)) {
|
||||
throw error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
|
||||
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"), init.getToken());
|
||||
throw error(AbstractParser.message("not.lvalue.for.in.loop", isForOf ? "of" : "in"), init.getToken());
|
||||
}
|
||||
verifyStrictIdent((IdentNode)init, "for-in iterator");
|
||||
verifyStrictIdent((IdentNode)init, isForOf ? "for-of iterator" : "for-in iterator");
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
|
||||
// Get the collection expression.
|
||||
modify = joinPredecessorExpression();
|
||||
// For-of only allows AssignmentExpression.
|
||||
modify = isForOf ? new JoinPredecessorExpression(assignmentExpression(false)) : joinPredecessorExpression();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -52,10 +52,12 @@ import jdk.nashorn.internal.codegen.ApplySpecialization;
|
||||
import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
|
||||
import jdk.nashorn.internal.ir.debug.JSONWriter;
|
||||
import jdk.nashorn.internal.objects.AbstractIterator;
|
||||
import jdk.nashorn.internal.objects.Global;
|
||||
import jdk.nashorn.internal.objects.NativeObject;
|
||||
import jdk.nashorn.internal.parser.Lexer;
|
||||
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||
import jdk.nashorn.internal.runtime.linker.InvokeByName;
|
||||
|
||||
/**
|
||||
* Utilities to be called by JavaScript runtime API and generated classes.
|
||||
@ -102,6 +104,11 @@ public final class ScriptRuntime {
|
||||
*/
|
||||
public static final Call TO_VALUE_ITERATOR = staticCallNoLookup(ScriptRuntime.class, "toValueIterator", Iterator.class, Object.class);
|
||||
|
||||
/**
|
||||
* Return an appropriate iterator for the elements in a ES6 for-of loop
|
||||
*/
|
||||
public static final Call TO_ES6_ITERATOR = staticCallNoLookup(ScriptRuntime.class, "toES6Iterator", Iterator.class, Object.class);
|
||||
|
||||
/**
|
||||
* Method handle for apply. Used from {@link ScriptFunction} for looking up calls to
|
||||
* call sites that are known to be megamorphic. Using an invoke dynamic here would
|
||||
@ -365,6 +372,77 @@ public final class ScriptRuntime {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over property values used in the {@code for ... of} statement. The iterator uses the
|
||||
* Iterator interface defined in version 6 of the ECMAScript specification.
|
||||
*
|
||||
* @param obj object to iterate on.
|
||||
* @return iterator based on the ECMA 6 Iterator interface.
|
||||
*/
|
||||
public static Iterator<?> toES6Iterator(final Object obj) {
|
||||
final Global global = Global.instance();
|
||||
final Object iterator = AbstractIterator.getIterator(Global.toObject(obj), global);
|
||||
|
||||
final InvokeByName nextInvoker = AbstractIterator.getNextInvoker(global);
|
||||
final MethodHandle doneInvoker = AbstractIterator.getDoneInvoker(global);
|
||||
final MethodHandle valueInvoker = AbstractIterator.getValueInvoker(global);
|
||||
|
||||
return new Iterator<Object>() {
|
||||
|
||||
private Object nextResult = nextResult();
|
||||
|
||||
private Object nextResult() {
|
||||
try {
|
||||
final Object next = nextInvoker.getGetter().invokeExact(iterator);
|
||||
if (Bootstrap.isCallable(next)) {
|
||||
return nextInvoker.getInvoker().invokeExact(next, iterator, (Object) null);
|
||||
}
|
||||
} catch (final RuntimeException|Error r) {
|
||||
throw r;
|
||||
} catch (final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (nextResult == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
final Object done = doneInvoker.invokeExact(nextResult);
|
||||
return !JSType.toBoolean(done);
|
||||
} catch (final RuntimeException|Error r) {
|
||||
throw r;
|
||||
} catch (final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object next() {
|
||||
if (nextResult == null) {
|
||||
return Undefined.getUndefined();
|
||||
}
|
||||
try {
|
||||
final Object result = nextResult;
|
||||
nextResult = nextResult();
|
||||
return valueInvoker.invokeExact(result);
|
||||
} catch (final RuntimeException|Error r) {
|
||||
throw r;
|
||||
} catch (final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("remove");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge a scope into its prototype's map.
|
||||
* Merge a scope into its prototype.
|
||||
|
@ -52,8 +52,9 @@ parser.error.no.func.decl.here.warn=Function declarations should only occur at p
|
||||
parser.error.property.redefinition=Property "{0}" already defined
|
||||
parser.error.unexpected.token=Unexpected token: {0}
|
||||
parser.error.for.each.without.in=for each can only be used with for..in
|
||||
parser.error.many.vars.in.for.in.loop=Only one variable allowed in for..in loop
|
||||
parser.error.not.lvalue.for.in.loop=Invalid left side value of for..in loop
|
||||
parser.error.many.vars.in.for.in.loop=Only one variable allowed in for..{0} loop
|
||||
parser.error.not.lvalue.for.in.loop=Invalid left side value of for..{0} loop
|
||||
parser.error.for.in.loop.initializer=for..{0] loop declaration must not have an initializer
|
||||
parser.error.missing.catch.or.finally=Missing catch or finally after try
|
||||
parser.error.regex.unsupported.flag=Unsupported RegExp flag: {0}
|
||||
parser.error.regex.repeated.flag=Repeated RegExp flag: {0}
|
||||
|
@ -64,3 +64,8 @@ expectError('`text`', 'template literal', 'SyntaxError');
|
||||
expectError('`${ x }`', 'template literal', 'SyntaxError');
|
||||
expectError('`text ${ x } text`', 'template literal', 'SyntaxError');
|
||||
expectError('f`text`', 'template literal', 'SyntaxError');
|
||||
expectError('for (a of [1, 2, 3]) print(a)', 'for-of', 'SyntaxError');
|
||||
expectError('for (var a of [1, 2, 3]) print(a)', 'for-of', 'SyntaxError');
|
||||
expectError('for (let a of [1, 2, 3]) print(a)', 'for-of', 'SyntaxError');
|
||||
expectError('for (const a of [1, 2, 3]) print(a)', 'for-of', 'SyntaxError');
|
||||
|
||||
|
196
nashorn/test/script/basic/es6/for-of.js
Normal file
196
nashorn/test/script/basic/es6/for-of.js
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* JDK-8151700: Add support for ES6 for-of
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
* @option --language=es6
|
||||
*/
|
||||
|
||||
let result = "";
|
||||
for (let a of [1, 2, "foo"]) {
|
||||
result += a;
|
||||
}
|
||||
|
||||
if (result !== "12foo") {
|
||||
throw new Error("unexpcected result: " + result);
|
||||
}
|
||||
|
||||
let sum = 0;
|
||||
let numbers = [1, 2, 3, 4];
|
||||
numbers.ten = 10; // not iterated over
|
||||
|
||||
for (let n of numbers) {
|
||||
sum += n;
|
||||
}
|
||||
|
||||
if (sum !== 10) {
|
||||
throw new Error("unexpected sum: " + sum);;
|
||||
}
|
||||
|
||||
if (typeof n !== "undefined") {
|
||||
throw new Error("n is visible outside of for-of");
|
||||
}
|
||||
|
||||
let message = "Hello";
|
||||
result = "";
|
||||
|
||||
for(const c of message) {
|
||||
result += c;
|
||||
}
|
||||
|
||||
if (result !== "Hello") {
|
||||
throw new Error("unexpected result: " + result);
|
||||
}
|
||||
|
||||
if (typeof c !== "undefined") {
|
||||
throw new Error("c is visible outside of for-of")
|
||||
}
|
||||
|
||||
// Callbacks with per-iteration scope
|
||||
|
||||
result = "";
|
||||
let funcs = [];
|
||||
|
||||
for (let a of [1, 2, "foo"]) {
|
||||
funcs.push(function() { result += a; });
|
||||
}
|
||||
|
||||
funcs.forEach(function(f) { f(); });
|
||||
if (result !== "12foo") {
|
||||
throw new Error("unexpcected result: " + result);
|
||||
}
|
||||
|
||||
result = "";
|
||||
funcs = [];
|
||||
|
||||
for (const a of [1, 2, "foo"]) {
|
||||
funcs.push(function() { result += a; });
|
||||
}
|
||||
|
||||
funcs.forEach(function(f) { f(); });
|
||||
if (result !== "12foo") {
|
||||
throw new Error("unexpcected result: " + result);
|
||||
}
|
||||
|
||||
// Set
|
||||
var set = new Set(["foo", "bar", "foo"]);
|
||||
result = "";
|
||||
|
||||
for (var w of set) {
|
||||
result += w;
|
||||
}
|
||||
|
||||
if (result !== "foobar") {
|
||||
throw new Error("unexpected result: " + result);
|
||||
}
|
||||
|
||||
// Maps
|
||||
var map = new Map([["a", 1], ["b", 2]]);
|
||||
result = "";
|
||||
|
||||
for (var entry of map) {
|
||||
result += entry;
|
||||
}
|
||||
|
||||
if (result !== "a,1b,2") {
|
||||
throw new Error("unexpected result: " + result);
|
||||
}
|
||||
|
||||
// per-iteration scope
|
||||
|
||||
let array = ["a", "b", "c"];
|
||||
funcs = [];
|
||||
|
||||
for (let i of array) {
|
||||
for (let j of array) {
|
||||
for (let k of array) {
|
||||
funcs.push(function () {
|
||||
return i + j + k;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(funcs.length, 3 * 3 * 3);
|
||||
let count = 0;
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
for (let j = 0; j < 3; j++) {
|
||||
for (let k = 0; k < 3; k++) {
|
||||
Assert.assertEquals(funcs[count++](), array[i] + array[j] + array[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// per-iteration scope with const declaration
|
||||
|
||||
funcs = [];
|
||||
|
||||
for (const i of array) {
|
||||
for (const j of array) {
|
||||
for (const k of array) {
|
||||
funcs.push(function () {
|
||||
return i + j + k;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(funcs.length, 3 * 3 * 3);
|
||||
count = 0;
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
for (let j = 0; j < 3; j++) {
|
||||
for (let k = 0; k < 3; k++) {
|
||||
Assert.assertEquals(funcs[count++](), array[i] + array[j] + array[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fibonacci iterator
|
||||
|
||||
let fibonacci = {};
|
||||
|
||||
fibonacci[Symbol.iterator] = function() {
|
||||
let previous = 0, current = 1;
|
||||
return {
|
||||
next: function() {
|
||||
let tmp = current;
|
||||
current = previous + current;
|
||||
previous = tmp;
|
||||
return { done: false, value: current };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (f of fibonacci) {
|
||||
if (f > 100000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertTrue(f === 121393);
|
||||
|
Loading…
x
Reference in New Issue
Block a user