1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.objects; 27 28 import java.lang.invoke.MethodHandle; 29 import java.util.function.Consumer; 30 import jdk.nashorn.internal.objects.annotations.Attribute; 31 import jdk.nashorn.internal.objects.annotations.Function; 32 import jdk.nashorn.internal.objects.annotations.ScriptClass; 33 import jdk.nashorn.internal.runtime.JSType; 34 import jdk.nashorn.internal.runtime.PropertyMap; 35 import jdk.nashorn.internal.runtime.ScriptObject; 36 import jdk.nashorn.internal.runtime.ScriptRuntime; 37 import jdk.nashorn.internal.runtime.linker.Bootstrap; 38 import jdk.nashorn.internal.runtime.linker.InvokeByName; 39 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 40 41 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 42 43 /** 44 * ECMA6 25.1.2 The %IteratorPrototype% Object 45 */ 46 @ScriptClass("Iterator") 47 public abstract class AbstractIterator extends ScriptObject { 48 49 // initialized by nasgen 50 private static PropertyMap $nasgenmap$; 51 52 private final static Object ITERATOR_INVOKER_KEY = new Object(); 53 private final static Object NEXT_INVOKER_KEY = new Object(); 54 private final static Object DONE_INVOKER_KEY = new Object(); 55 private final static Object VALUE_INVOKER_KEY = new Object(); 56 57 /** ECMA6 iteration kinds */ 58 enum IterationKind { 59 /** key iteration */ 60 KEY, 61 /** value iteration */ 62 VALUE, 63 /** key+value iteration */ 64 KEY_VALUE 65 } 66 67 /** 68 * Create an abstract iterator object with the given prototype and property map. 69 * 70 * @param prototype the prototype 71 * @param map the property map 72 */ 73 protected AbstractIterator(final ScriptObject prototype, final PropertyMap map) { 74 super(prototype, map); 75 } 76 77 /** 78 * 25.1.2.1 %IteratorPrototype% [ @@iterator ] ( ) 79 * 80 * @param self the self object 81 * @return this iterator 82 */ 83 @Function(attributes = Attribute.NOT_ENUMERABLE, name = "@@iterator") 84 public static Object getIterator(final Object self) { 85 return self; 86 } 87 88 @Override 89 public String getClassName() { 90 return "Iterator"; 91 } 92 93 /** 94 * ES6 25.1.1.2 The Iterator Interface 95 * 96 * @param arg argument 97 * @return next iterator result 98 */ 99 protected abstract IteratorResult next(final Object arg); 100 101 /** 102 * ES6 25.1.1.3 The IteratorResult Interface 103 * 104 * @param value result value 105 * @param done result status 106 * @param global the global object 107 * @return result object 108 */ 109 protected IteratorResult makeResult(final Object value, final Boolean done, final Global global) { 110 return new IteratorResult(value, done, global); 111 } 112 113 /** 114 * ES6 7.4.1 GetIterator abstract operation 115 * 116 * @param iterable an object 117 * @param global the global object 118 * @return the iterator 119 */ 120 public static Object getIterator(final Object iterable, final Global global) { 121 final Object object = Global.toObject(iterable); 122 123 if (object instanceof ScriptObject) { 124 // TODO we need to implement fast property access for Symbol keys in order to use InvokeByName here. 125 final Object getter = ((ScriptObject) object).get(NativeSymbol.iterator); 126 127 if (Bootstrap.isCallable(getter)) { 128 try { 129 final MethodHandle invoker = global.getDynamicInvoker(ITERATOR_INVOKER_KEY, 130 () -> Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Object.class)); 131 132 final Object value = invoker.invokeExact(getter, iterable); 133 if (JSType.isPrimitive(value)) { 134 throw typeError("not.an.object", ScriptRuntime.safeToString(value)); 135 } 136 return value; 137 138 } catch (final Throwable t) { 139 throw new RuntimeException(t); 140 } 141 } 142 throw typeError("not.a.function", ScriptRuntime.safeToString(getter)); 143 } 144 145 throw typeError("cannot.get.iterator", ScriptRuntime.safeToString(iterable)); 146 } 147 148 /** 149 * Iterate over an iterable object, passing every value to {@code consumer}. 150 * 151 * @param iterable an iterable object 152 * @param global the current global 153 * @param consumer the value consumer 154 */ 155 public static void iterate(final Object iterable, final Global global, final Consumer<Object> consumer) { 156 157 final Object iterator = AbstractIterator.getIterator(Global.toObject(iterable), global); 158 159 final InvokeByName nextInvoker = global.getInvokeByName(AbstractIterator.NEXT_INVOKER_KEY, 160 () -> new InvokeByName("next", Object.class, Object.class, Object.class)); 161 final MethodHandle doneInvoker = global.getDynamicInvoker(AbstractIterator.DONE_INVOKER_KEY, 162 () -> Bootstrap.createDynamicInvoker("done", NashornCallSiteDescriptor.GET_PROPERTY, Object.class, Object.class)); 163 final MethodHandle valueInvoker = global.getDynamicInvoker(AbstractIterator.VALUE_INVOKER_KEY, 164 () -> Bootstrap.createDynamicInvoker("value", NashornCallSiteDescriptor.GET_PROPERTY, Object.class, Object.class)); 165 166 try { 167 do { 168 final Object next = nextInvoker.getGetter().invokeExact(iterator); 169 if (!Bootstrap.isCallable(next)) { 170 break; 171 } 172 173 final Object result = nextInvoker.getInvoker().invokeExact(next, iterator, (Object) null); 174 if (!(result instanceof ScriptObject)) { 175 break; 176 } 177 178 final Object done = doneInvoker.invokeExact(result); 179 if (JSType.toBoolean(done)) { 180 break; 181 } 182 183 consumer.accept(valueInvoker.invokeExact(result)); 184 185 } while (true); 186 187 } catch (final RuntimeException r) { 188 throw r; 189 } catch (final Throwable t) { 190 throw new RuntimeException(t); 191 } 192 193 } 194 } 195 196