/** * 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.runtime.internal; import static com.github.anba.es6draft.runtime.AbstractOperations.*; import static com.github.anba.es6draft.runtime.objects.iteration.GeneratorAbstractOperations.GeneratorResume; import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED; import java.util.Iterator; import java.util.Map.Entry; import org.mozilla.javascript.ConsString; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.objects.ArrayIteratorObject; import com.github.anba.es6draft.runtime.objects.ArrayIteratorPrototype; import com.github.anba.es6draft.runtime.objects.ArrayPrototype; import com.github.anba.es6draft.runtime.objects.binary.TypedArrayObject; import com.github.anba.es6draft.runtime.objects.binary.TypedArrayPrototypePrototype; import com.github.anba.es6draft.runtime.objects.collection.MapIteratorObject; import com.github.anba.es6draft.runtime.objects.collection.MapIteratorPrototype; import com.github.anba.es6draft.runtime.objects.collection.MapObject; import com.github.anba.es6draft.runtime.objects.collection.MapPrototype; import com.github.anba.es6draft.runtime.objects.collection.SetIteratorObject; import com.github.anba.es6draft.runtime.objects.collection.SetIteratorPrototype; import com.github.anba.es6draft.runtime.objects.collection.SetObject; import com.github.anba.es6draft.runtime.objects.collection.SetPrototype; import com.github.anba.es6draft.runtime.objects.iteration.GeneratorObject; import com.github.anba.es6draft.runtime.objects.iteration.GeneratorPrototype; import com.github.anba.es6draft.runtime.objects.iteration.IteratorPrototype; import com.github.anba.es6draft.runtime.objects.iteration.ListIterator; import com.github.anba.es6draft.runtime.objects.text.StringIteratorPrototype; import com.github.anba.es6draft.runtime.objects.text.StringPrototype; import com.github.anba.es6draft.runtime.types.BuiltinSymbol; import com.github.anba.es6draft.runtime.types.Callable; import com.github.anba.es6draft.runtime.types.Intrinsics; import com.github.anba.es6draft.runtime.types.Property; import com.github.anba.es6draft.runtime.types.ScriptObject; import com.github.anba.es6draft.runtime.types.Symbol; import com.github.anba.es6draft.runtime.types.builtins.ArgumentsObject; import com.github.anba.es6draft.runtime.types.builtins.ArrayObject; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject; /** * */ public final class ScriptIterators { private ScriptIterators() { } /** * * @param cx * the execution context * @param map * the map object * @return {@code true} */ public static boolean isBuiltinIterator(ExecutionContext cx, MapObject map) { return MapScriptIterator.isBuiltinIterator(cx, map); } /** * * @param cx * the execution context * @param set * the set object * @return {@code true} */ public static boolean isBuiltinIterator(ExecutionContext cx, SetObject set) { return SetScriptIterator.isBuiltinIterator(cx, set); } /** * Returns a {@link ScriptIterator} for {@code iterable}. * * @param cx * the execution context * @param iterable * the iterable object * @return the iterator object */ public static ScriptIterator<?> GetScriptIterator(ExecutionContext cx, Object iterable) { if (iterable instanceof ArrayObject) { ArrayObject array = (ArrayObject) iterable; if (ArrayScriptIterator.isBuiltinIterator(cx, array)) { return new ArrayScriptIterator(cx, array); } } else if (iterable instanceof ArrayIteratorObject) { ArrayIteratorObject arrayIterator = (ArrayIteratorObject) iterable; if (ArrayIteratorScriptIterator.isBuiltinIterator(cx, arrayIterator)) { return new ArrayIteratorScriptIterator(cx, arrayIterator); } } else if (iterable instanceof TypedArrayObject) { TypedArrayObject typedArray = (TypedArrayObject) iterable; if (TypedArrayScriptIterator.isBuiltinIterator(cx, typedArray)) { return new TypedArrayScriptIterator(cx, typedArray); } } else if (iterable instanceof String || iterable instanceof ConsString) { if (StringScriptIterator.isBuiltinIterator(cx)) { return new StringScriptIterator(cx, iterable.toString()); } } else if (iterable instanceof MapObject) { MapObject map = (MapObject) iterable; if (MapScriptIterator.isBuiltinIterator(cx, map)) { return new MapScriptIterator(cx, map); } } else if (iterable instanceof MapIteratorObject) { MapIteratorObject mapIterator = (MapIteratorObject) iterable; if (MapIteratorScriptIterator.isBuiltinIterator(cx, mapIterator)) { return new MapIteratorScriptIterator(cx, mapIterator); } } else if (iterable instanceof SetObject) { SetObject set = (SetObject) iterable; if (SetScriptIterator.isBuiltinIterator(cx, set)) { return new SetScriptIterator(cx, set); } } else if (iterable instanceof SetIteratorObject) { SetIteratorObject setIterator = (SetIteratorObject) iterable; if (SetIteratorScriptIterator.isBuiltinIterator(cx, setIterator)) { return new SetIteratorScriptIterator(cx, setIterator); } } else if (iterable instanceof ArgumentsObject) { ArgumentsObject arguments = (ArgumentsObject) iterable; if (ArgumentsScriptIterator.isBuiltinIterator(cx, arguments)) { return new ArgumentsScriptIterator(cx, arguments); } } else if (iterable instanceof ListIterator<?>) { ListIterator<?> listIterator = (ListIterator<?>) iterable; if (ListIteratorScriptIterator.isBuiltinIterator(cx, listIterator)) { return new ListIteratorScriptIterator(cx, listIterator); } } else if (iterable instanceof GeneratorObject) { GeneratorObject generator = (GeneratorObject) iterable; if (GeneratorScriptIterator.isBuiltinIterator(cx, generator)) { return new GeneratorScriptIterator(cx, generator); } } return new ScriptIteratorImpl(cx, GetIterator(cx, iterable)); } /** * Returns a {@link ScriptIterator} for {@code iterable}. * * @param cx * the execution context * @param iterable * the iterable object * @param method * the iterator method * @return the iterator object */ public static ScriptIterator<?> GetScriptIterator(ExecutionContext cx, Object iterable, Callable method) { if (iterable instanceof ArrayObject) { ArrayObject array = (ArrayObject) iterable; if (ArrayScriptIterator.isBuiltinIterator(cx, array, method)) { return new ArrayScriptIterator(cx, array); } } else if (iterable instanceof TypedArrayObject) { TypedArrayObject typedArray = (TypedArrayObject) iterable; if (TypedArrayScriptIterator.isBuiltinIterator(cx, typedArray, method)) { return new TypedArrayScriptIterator(cx, typedArray); } } else if (iterable instanceof String || iterable instanceof ConsString) { if (StringScriptIterator.isBuiltinIterator(cx, method)) { return new StringScriptIterator(cx, iterable.toString()); } } else if (iterable instanceof MapObject) { MapObject map = (MapObject) iterable; if (MapScriptIterator.isBuiltinIterator(cx, map, method)) { return new MapScriptIterator(cx, map); } } else if (iterable instanceof SetObject) { SetObject set = (SetObject) iterable; if (SetScriptIterator.isBuiltinIterator(cx, set, method)) { return new SetScriptIterator(cx, set); } } else if (iterable instanceof ArgumentsObject) { ArgumentsObject arguments = (ArgumentsObject) iterable; if (ArgumentsScriptIterator.isBuiltinIterator(cx, arguments, method)) { return new ArgumentsScriptIterator(cx, arguments); } } return new ScriptIteratorImpl(cx, GetIterator(cx, iterable, method)); } /** * Returns a {@link ScriptIterator} for {@code iterator}. {@code iterator} is expected to comply to the * <code>"25.1.2 The Iterator Interface"</code>. * * @param cx * the execution context * @param iterator * the script iterator object * @return the iterator object */ public static ScriptIterator<?> ToScriptIterator(ExecutionContext cx, ScriptObject iterator) { return new ScriptIteratorImpl(cx, iterator); } private static final class ScriptIteratorImpl extends SimpleIterator<Object> implements ScriptIterator<Object> { private final ExecutionContext cx; private final ScriptObject iterator; private boolean done = false; ScriptIteratorImpl(ExecutionContext cx, ScriptObject iterator) { this.cx = cx; this.iterator = iterator; } @Override protected Object findNext() throws ScriptException { if (!done) { try { ScriptObject next = IteratorStep(cx, iterator); if (next != null) { return IteratorValue(cx, next); } } catch (ScriptException e) { done = true; throw e; } done = true; } return null; } @Override public void close() throws ScriptException { if (!done) { IteratorClose(cx, iterator); } } @Override public void close(Throwable cause) throws ScriptException { if (!done) { IteratorClose(cx, iterator, cause); } } } private static abstract class BuiltinScriptIterator extends SimpleIterator<Object> implements ScriptIterator<Object> { protected final ExecutionContext cx; protected ScriptObject iteratorObject; protected boolean done = false; protected BuiltinScriptIterator(ExecutionContext cx) { this.cx = cx; } protected static final Property findNextProperty(OrdinaryObject object) { final int MAX_PROTO_CHAIN_LENGTH = 5; for (int i = 0; i < MAX_PROTO_CHAIN_LENGTH; ++i) { Property iterProp = object.lookupOwnProperty("next"); if (iterProp != null) { return iterProp; } ScriptObject proto = object.getPrototype(); if (!(proto instanceof OrdinaryObject)) { break; } object = (OrdinaryObject) proto; } return null; } protected static final Property findIteratorProperty(OrdinaryObject object) { final int MAX_PROTO_CHAIN_LENGTH = 5; Symbol name = BuiltinSymbol.iterator.get(); for (int i = 0; i < MAX_PROTO_CHAIN_LENGTH; ++i) { Property iterProp = object.lookupOwnProperty(name); if (iterProp != null) { return iterProp; } ScriptObject proto = object.getPrototype(); if (!(proto instanceof OrdinaryObject)) { break; } object = (OrdinaryObject) proto; } return null; } /** * Returns the intrinsic iterator prototype. * * @return the iterator prototype */ protected abstract Intrinsics getIntrinsic(); /** * Creates an iterator object. * * @return the new iterator object */ protected abstract OrdinaryObject createIteratorObject(); /** * Slow path: Perform iteration with an iterator object. * * @return the new iterator result or {@code null} */ protected final Object slowNext() { assert !done; try { ScriptObject next = IteratorStep(cx, getScriptObject()); if (next != null) { return IteratorValue(cx, next); } } catch (ScriptException e) { done = true; throw e; } done = true; return null; } @Override public final void close() throws ScriptException { if (!done && hasReturn()) { IteratorClose(cx, getScriptObject()); } } @Override public final void close(Throwable cause) throws ScriptException { if (!done && hasReturn()) { IteratorClose(cx, getScriptObject(), cause); } } private ScriptObject getScriptObject() { if (iteratorObject == null) { iteratorObject = createIteratorObject(); } return iteratorObject; } private boolean hasReturn() { if (iteratorObject != null) { return true; } OrdinaryObject iterProto = cx.getIntrinsic(getIntrinsic()); for (;;) { if (iterProto.lookupOwnProperty("return") != null) { return true; } ScriptObject proto = iterProto.getPrototype(); if (proto == null) { return false; } if (!(proto instanceof OrdinaryObject)) { return true; } iterProto = (OrdinaryObject) proto; } } } private static final class ArrayScriptIterator extends BuiltinScriptIterator { private final ArrayObject array; private long index; ArrayScriptIterator(ExecutionContext cx, ArrayObject array) { super(cx); this.array = array; this.index = 0; } static boolean isBuiltinIterator(ExecutionContext cx, ArrayObject array) { Property iterProp = findIteratorProperty(array); return iterProp != null && isBuiltinIterator(cx, array, iterProp.getValue()); } static boolean isBuiltinIterator(ExecutionContext cx, ArrayObject array, Object method) { // Test 1: Is array[Symbol.iterator] == %ArrayPrototype%.values? // Test 2: Is %ArrayIteratorPrototype%.next the built-in next method? return array.isDenseArray() && ArrayPrototype.isBuiltinValues(cx.getRealm(), method) && isBuiltinNext(cx); } private static boolean isBuiltinNext(ExecutionContext cx) { Property iterNextProp = cx.getIntrinsic(Intrinsics.ArrayIteratorPrototype).lookupOwnProperty("next"); return iterNextProp != null && ArrayIteratorPrototype.isBuiltinNext(cx.getRealm(), iterNextProp.getValue()); } @Override protected OrdinaryObject createIteratorObject() { return ArrayIteratorPrototype.CreateArrayIterator(cx, array, index, ArrayIteratorObject.ArrayIterationKind.Value); } @Override protected Intrinsics getIntrinsic() { return Intrinsics.ArrayIteratorPrototype; } @Override protected Object findNext() throws ScriptException { if (!done) { if (iteratorObject == null && isBuiltinNext(cx) && array.isDenseArray()) { if (index < array.getLength()) { return array.getDenseElement(index++); } done = true; return null; } return slowNext(); } return null; } } private static final class ArrayIteratorScriptIterator extends BuiltinScriptIterator { private final ArrayIteratorObject arrayIterator; ArrayIteratorScriptIterator(ExecutionContext cx, ArrayIteratorObject arrayIterator) { super(cx); this.arrayIterator = arrayIterator; this.iteratorObject = arrayIterator; } static boolean isBuiltinIterator(ExecutionContext cx, ArrayIteratorObject arrayIterator) { Property iterProp = findIteratorProperty(arrayIterator); return iterProp != null && isBuiltinIterator(cx, arrayIterator, iterProp.getValue()); } static boolean isBuiltinIterator(ExecutionContext cx, ArrayIteratorObject arrayIterator, Object method) { // Test 1: Is arrayIterator[Symbol.iterator] == %IteratorPrototype%[Symbol.iterator]? // Test 2: Is %ArrayIteratorPrototype%.next the built-in next method? return IteratorPrototype.isBuiltinIterator(cx.getRealm(), method) && isBuiltinNext(cx, arrayIterator); } private static boolean isBuiltinNext(ExecutionContext cx, ArrayIteratorObject arrayIterator) { Property iterNextProp = findNextProperty(arrayIterator); return iterNextProp != null && ArrayIteratorPrototype.isBuiltinNext(cx.getRealm(), iterNextProp.getValue()); } @Override protected OrdinaryObject createIteratorObject() { throw new AssertionError(); } @Override protected Intrinsics getIntrinsic() { return Intrinsics.ArrayIteratorPrototype; } @Override protected Object findNext() throws ScriptException { if (!done) { if (isBuiltinNext(cx, arrayIterator)) { try { ArrayIteratorObject iter = arrayIterator; ScriptObject array = iter.getIteratedObject(); if (array == null) { done = true; return null; } long index = iter.getNextIndex(); long len; if (array instanceof TypedArrayObject) { len = ((TypedArrayObject) array).getArrayLength(); } else if (array instanceof ArrayObject) { len = ((ArrayObject) array).getLength(); } else { len = ToLength(cx, Get(cx, array, "length")); } if (index >= len) { iter.setIteratedObject(null); done = true; return null; } iter.setNextIndex(index + 1); switch (iter.getIterationKind()) { case Key: return index; case KeyValue: return CreateArrayFromList(cx, index, Get(cx, array, index)); case Value: return Get(cx, array, index); default: throw new AssertionError(); } } catch (ScriptException e) { // Don't call `iter.setIteratedObject(null)`, other iterations may reuse the iterator. done = true; throw e; } } return slowNext(); } return null; } } private static final class TypedArrayScriptIterator extends BuiltinScriptIterator { private final TypedArrayObject typedArray; private long index; TypedArrayScriptIterator(ExecutionContext cx, TypedArrayObject typedArray) { super(cx); this.typedArray = typedArray; this.index = 0; } static boolean isBuiltinIterator(ExecutionContext cx, TypedArrayObject typedArray) { Property iterProp = findIteratorProperty(typedArray); return iterProp != null && isBuiltinIterator(cx, typedArray, iterProp.getValue()); } static boolean isBuiltinIterator(ExecutionContext cx, TypedArrayObject typedArray, Object method) { // Test 1: Is typedArray[Symbol.iterator] == %TypedArrayPrototype%.values? // Test 2: Is %ArrayIteratorPrototype%.next the built-in next method? return !typedArray.getBuffer().isDetached() && TypedArrayPrototypePrototype.isBuiltinValues(cx.getRealm(), method) && isBuiltinNext(cx); } private static boolean isBuiltinNext(ExecutionContext cx) { Property iterNextProp = cx.getIntrinsic(Intrinsics.ArrayIteratorPrototype).lookupOwnProperty("next"); return iterNextProp != null && ArrayIteratorPrototype.isBuiltinNext(cx.getRealm(), iterNextProp.getValue()); } @Override protected OrdinaryObject createIteratorObject() { return ArrayIteratorPrototype.CreateArrayIterator(cx, typedArray, index, ArrayIteratorObject.ArrayIterationKind.Value); } @Override protected Intrinsics getIntrinsic() { return Intrinsics.ArrayIteratorPrototype; } @Override protected Object findNext() throws ScriptException { if (!done) { if (iteratorObject == null && isBuiltinNext(cx) && !typedArray.getBuffer().isDetached()) { if (index < typedArray.getArrayLength()) { return typedArray.get(cx, index++, typedArray); } done = true; return null; } return slowNext(); } return null; } } private static final class ArgumentsScriptIterator extends BuiltinScriptIterator { private final ArgumentsObject arguments; private long index; ArgumentsScriptIterator(ExecutionContext cx, ArgumentsObject arguments) { super(cx); this.arguments = arguments; this.index = 0; } static boolean isBuiltinIterator(ExecutionContext cx, ArgumentsObject arguments) { Property iterProp = findIteratorProperty(arguments); return iterProp != null && isBuiltinIterator(cx, arguments, iterProp.getValue()); } static boolean isBuiltinIterator(ExecutionContext cx, ArgumentsObject arguments, Object method) { // Test 1: Is arguments[Symbol.iterator] == %ArrayPrototype%.values? // Test 2: Is %ArrayIteratorPrototype%.next the built-in next method? return arguments.isDenseArray() && ArrayPrototype.isBuiltinValues(cx.getRealm(), method) && isBuiltinNext(cx); } private static boolean isBuiltinNext(ExecutionContext cx) { Property iterNextProp = cx.getIntrinsic(Intrinsics.ArrayIteratorPrototype).lookupOwnProperty("next"); return iterNextProp != null && ArrayIteratorPrototype.isBuiltinNext(cx.getRealm(), iterNextProp.getValue()); } @Override protected OrdinaryObject createIteratorObject() { return ArrayIteratorPrototype.CreateArrayIterator(cx, arguments, index, ArrayIteratorObject.ArrayIterationKind.Value); } @Override protected Intrinsics getIntrinsic() { return Intrinsics.ArrayIteratorPrototype; } @Override protected Object findNext() throws ScriptException { if (!done) { if (iteratorObject == null && isBuiltinNext(cx)) { long length = arguments.getLength(); if (arguments.isDenseArray(length)) { if (index < length) { return arguments.get(cx, index++, arguments); } done = true; return null; } } return slowNext(); } return null; } } private static final class StringScriptIterator extends BuiltinScriptIterator { private final String string; private int index; StringScriptIterator(ExecutionContext cx, String string) { super(cx); this.string = string; this.index = 0; } static boolean isBuiltinIterator(ExecutionContext cx) { Property iterProp = findIteratorProperty(cx.getIntrinsic(Intrinsics.StringPrototype)); return iterProp != null && isBuiltinIterator(cx, iterProp.getValue()); } static boolean isBuiltinIterator(ExecutionContext cx, Object method) { // Test 1: Is string[Symbol.iterator] == %StringPrototype%.iterator? // Test 2: Is %StringIteratorPrototype%.next the built-in next method? return StringPrototype.isBuiltinIterator(cx.getRealm(), method) && isBuiltinNext(cx); } private static boolean isBuiltinNext(ExecutionContext cx) { Property iterNextProp = cx.getIntrinsic(Intrinsics.StringIteratorPrototype).lookupOwnProperty("next"); return iterNextProp != null && StringIteratorPrototype.isBuiltinNext(cx.getRealm(), iterNextProp.getValue()); } @Override protected OrdinaryObject createIteratorObject() { return StringIteratorPrototype.CreateStringIterator(cx, string, index); } @Override protected Intrinsics getIntrinsic() { return Intrinsics.StringIteratorPrototype; } @Override protected Object findNext() throws ScriptException { if (!done) { if (iteratorObject == null && isBuiltinNext(cx)) { if (index < string.length()) { int cp = string.codePointAt(index); index += Character.charCount(cp); return Strings.fromCodePoint(cp); } done = true; return null; } return slowNext(); } return null; } } private static final class MapScriptIterator extends BuiltinScriptIterator { private final Iterator<Entry<Object, Object>> iterator; MapScriptIterator(ExecutionContext cx, MapObject map) { super(cx); this.iterator = map.getMapData().iterator(); } static boolean isBuiltinIterator(ExecutionContext cx, MapObject map) { Property iterProp = findIteratorProperty(map); return iterProp != null && isBuiltinIterator(cx, map, iterProp.getValue()); } static boolean isBuiltinIterator(ExecutionContext cx, MapObject map, Object method) { // Test 1: Is map[Symbol.iterator] == %MapPrototype%.entries? // Test 2: Is %MapIteratorPrototype%.next the built-in next method? return MapPrototype.isBuiltinEntries(cx.getRealm(), method) && isBuiltinNext(cx); } private static boolean isBuiltinNext(ExecutionContext cx) { Property iterNextProp = cx.getIntrinsic(Intrinsics.MapIteratorPrototype).lookupOwnProperty("next"); return iterNextProp != null && MapIteratorPrototype.isBuiltinNext(cx.getRealm(), iterNextProp.getValue()); } @Override protected OrdinaryObject createIteratorObject() { return MapIteratorPrototype.CreateMapIterator(cx, iterator, MapIteratorObject.MapIterationKind.KeyValue); } @Override protected Intrinsics getIntrinsic() { return Intrinsics.MapIteratorPrototype; } @Override protected Object findNext() throws ScriptException { if (!done) { if (iteratorObject == null && isBuiltinNext(cx)) { if (iterator.hasNext()) { Entry<Object, Object> e = iterator.next(); return CreateArrayFromList(cx, e.getKey(), e.getValue()); } done = true; return null; } return slowNext(); } return null; } } private static final class MapIteratorScriptIterator extends BuiltinScriptIterator { private final MapIteratorObject mapIterator; MapIteratorScriptIterator(ExecutionContext cx, MapIteratorObject mapIterator) { super(cx); this.mapIterator = mapIterator; this.iteratorObject = mapIterator; } static boolean isBuiltinIterator(ExecutionContext cx, MapIteratorObject mapIterator) { Property iterProp = findIteratorProperty(mapIterator); return iterProp != null && isBuiltinIterator(cx, mapIterator, iterProp.getValue()); } static boolean isBuiltinIterator(ExecutionContext cx, MapIteratorObject mapIterator, Object method) { // Test 1: Is mapIterator[Symbol.iterator] == %IteratorPrototype%[Symbol.iterator]? // Test 2: Is %MapIteratorPrototype%.next the built-in next method? return IteratorPrototype.isBuiltinIterator(cx.getRealm(), method) && isBuiltinNext(cx, mapIterator); } private static boolean isBuiltinNext(ExecutionContext cx, MapIteratorObject mapIterator) { Property iterNextProp = findNextProperty(mapIterator); return iterNextProp != null && MapIteratorPrototype.isBuiltinNext(cx.getRealm(), iterNextProp.getValue()); } @Override protected OrdinaryObject createIteratorObject() { throw new AssertionError(); } @Override protected Intrinsics getIntrinsic() { return Intrinsics.MapIteratorPrototype; } @Override protected Object findNext() throws ScriptException { if (!done) { if (isBuiltinNext(cx, mapIterator)) { MapIteratorObject mapIter = mapIterator; Iterator<Entry<Object, Object>> iter = mapIter.getIterator(); if (iter == null) { done = true; return null; } if (iter.hasNext()) { Entry<Object, Object> e = iter.next(); switch (mapIter.getIterationKind()) { case Key: return e.getKey(); case Value: return e.getValue(); case KeyValue: return CreateArrayFromList(cx, e.getKey(), e.getValue()); default: throw new AssertionError(); } } mapIter.setIterator(null); done = true; return null; } return slowNext(); } return null; } } private static final class SetScriptIterator extends BuiltinScriptIterator { private final Iterator<Entry<Object, Void>> iterator; SetScriptIterator(ExecutionContext cx, SetObject set) { super(cx); this.iterator = set.getSetData().iterator(); } static boolean isBuiltinIterator(ExecutionContext cx, SetObject set) { Property iterProp = findIteratorProperty(set); return iterProp != null && isBuiltinIterator(cx, set, iterProp.getValue()); } static boolean isBuiltinIterator(ExecutionContext cx, SetObject set, Object method) { // Test 1: Is set[Symbol.iterator] == %SetPrototype%.values? // Test 2: Is %SetIteratorPrototype%.next the built-in next method? return SetPrototype.isBuiltinValues(cx.getRealm(), method) && isBuiltinNext(cx); } private static boolean isBuiltinNext(ExecutionContext cx) { Property iterNextProp = cx.getIntrinsic(Intrinsics.SetIteratorPrototype).lookupOwnProperty("next"); return iterNextProp != null && SetIteratorPrototype.isBuiltinNext(cx.getRealm(), iterNextProp.getValue()); } @Override protected OrdinaryObject createIteratorObject() { return SetIteratorPrototype.CreateSetIterator(cx, iterator, SetIteratorObject.SetIterationKind.Value); } @Override protected Intrinsics getIntrinsic() { return Intrinsics.SetIteratorPrototype; } @Override protected Object findNext() throws ScriptException { if (!done) { if (iteratorObject == null && isBuiltinNext(cx)) { if (iterator.hasNext()) { Entry<Object, Void> e = iterator.next(); return e.getKey(); } done = true; return null; } return slowNext(); } return null; } } private static final class SetIteratorScriptIterator extends BuiltinScriptIterator { private final SetIteratorObject setIterator; SetIteratorScriptIterator(ExecutionContext cx, SetIteratorObject setIterator) { super(cx); this.setIterator = setIterator; this.iteratorObject = setIterator; } static boolean isBuiltinIterator(ExecutionContext cx, SetIteratorObject setIterator) { Property iterProp = findIteratorProperty(setIterator); return iterProp != null && isBuiltinIterator(cx, setIterator, iterProp.getValue()); } static boolean isBuiltinIterator(ExecutionContext cx, SetIteratorObject setIterator, Object method) { // Test 1: Is setIterator[Symbol.iterator] == %IteratorPrototype%[Symbol.iterator]? // Test 2: Is %SetIteratorPrototype%.next the built-in next method? return IteratorPrototype.isBuiltinIterator(cx.getRealm(), method) && isBuiltinNext(cx, setIterator); } private static boolean isBuiltinNext(ExecutionContext cx, SetIteratorObject setIterator) { Property iterNextProp = findNextProperty(setIterator); return iterNextProp != null && SetIteratorPrototype.isBuiltinNext(cx.getRealm(), iterNextProp.getValue()); } @Override protected OrdinaryObject createIteratorObject() { throw new AssertionError(); } @Override protected Intrinsics getIntrinsic() { return Intrinsics.SetIteratorPrototype; } @Override protected Object findNext() throws ScriptException { if (!done) { if (isBuiltinNext(cx, setIterator)) { SetIteratorObject setIter = setIterator; Iterator<Entry<Object, Void>> iter = setIter.getIterator(); if (iter == null) { done = true; return null; } if (iter.hasNext()) { Entry<Object, Void> e = iter.next(); switch (setIter.getIterationKind()) { case Key: case Value: return e.getKey(); case KeyValue: return CreateArrayFromList(cx, e.getKey(), e.getKey()); default: throw new AssertionError(); } } setIter.setIterator(null); done = true; return null; } return slowNext(); } return null; } } private static final class ListIteratorScriptIterator extends BuiltinScriptIterator { private final ListIterator<?> listIterator; ListIteratorScriptIterator(ExecutionContext cx, ListIterator<?> listIterator) { super(cx); this.listIterator = listIterator; this.iteratorObject = listIterator; } static boolean isBuiltinIterator(ExecutionContext cx, ListIterator<?> listIterator) { Property iterProp = findIteratorProperty(listIterator); return iterProp != null && isBuiltinIterator(cx, listIterator, iterProp.getValue()); } static boolean isBuiltinIterator(ExecutionContext cx, ListIterator<?> listIterator, Object method) { // Test 1: Is listIterator[Symbol.iterator] == %IteratorPrototype%[Symbol.iterator]? // Test 2: Is listIterator.next the built-in next method? return IteratorPrototype.isBuiltinIterator(cx.getRealm(), method) && isBuiltinNext(cx, listIterator); } private static boolean isBuiltinNext(ExecutionContext cx, ListIterator<?> listIterator) { Property iterNextProp = listIterator.lookupOwnProperty("next"); return iterNextProp != null && iterNextProp.getValue() == listIterator.getIteratorNext(); } @Override protected OrdinaryObject createIteratorObject() { throw new AssertionError(); } @Override protected Intrinsics getIntrinsic() { return Intrinsics.IteratorPrototype; } @Override protected Object findNext() throws ScriptException { if (!done) { if (isBuiltinNext(cx, listIterator)) { Iterator<?> iterator = listIterator.getIterator(); if (iterator.hasNext()) { return iterator.next(); } done = true; return null; } return slowNext(); } return null; } } private static final class GeneratorScriptIterator extends BuiltinScriptIterator { private final GeneratorObject generator; GeneratorScriptIterator(ExecutionContext cx, GeneratorObject generator) { super(cx); this.generator = generator; this.iteratorObject = generator; } static boolean isBuiltinIterator(ExecutionContext cx, GeneratorObject generator) { Property iterProp = findIteratorProperty(generator); return iterProp != null && isBuiltinIterator(cx, generator, iterProp.getValue()); } static boolean isBuiltinIterator(ExecutionContext cx, GeneratorObject generator, Object method) { // Test 1: Is generator[Symbol.iterator] == %IteratorPrototype%[Symbol.iterator]? // Test 2: Is %GeneratorPrototype%.next the built-in next method? return IteratorPrototype.isBuiltinIterator(cx.getRealm(), method) && isBuiltinNext(cx, generator); } private static boolean isBuiltinNext(ExecutionContext cx, GeneratorObject generator) { Property iterNextProp = findNextProperty(generator); return iterNextProp != null && GeneratorPrototype.isBuiltinNext(cx.getRealm(), iterNextProp.getValue()); } @Override protected OrdinaryObject createIteratorObject() { throw new AssertionError(); } @Override protected Intrinsics getIntrinsic() { return Intrinsics.GeneratorPrototype; } @Override protected Object findNext() throws ScriptException { if (!done) { if (isBuiltinNext(cx, generator)) { GeneratorObject gen = generator; if (gen.getState() != GeneratorObject.GeneratorState.Completed) { // TODO: Remove iterator result boxing. try { ScriptObject result = GeneratorResume(cx, gen, UNDEFINED); if (!IteratorComplete(cx, result)) { return IteratorValue(cx, result); } } catch (ScriptException e) { done = true; throw e; } } done = true; return null; } return slowNext(); } return null; } } }