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