AbstractIterator.java revision 1626:d99fa86747ee
1221828Sgrehan/*
2221828Sgrehan * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3221828Sgrehan * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4221828Sgrehan *
5221828Sgrehan * This code is free software; you can redistribute it and/or modify it
6221828Sgrehan * under the terms of the GNU General Public License version 2 only, as
7221828Sgrehan * published by the Free Software Foundation.  Oracle designates this
8221828Sgrehan * particular file as subject to the "Classpath" exception as provided
9221828Sgrehan * by Oracle in the LICENSE file that accompanied this code.
10221828Sgrehan *
11221828Sgrehan * This code is distributed in the hope that it will be useful, but WITHOUT
12221828Sgrehan * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13221828Sgrehan * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14221828Sgrehan * version 2 for more details (a copy is included in the LICENSE file that
15221828Sgrehan * accompanied this code).
16221828Sgrehan *
17221828Sgrehan * You should have received a copy of the GNU General Public License version
18221828Sgrehan * 2 along with this work; if not, write to the Free Software Foundation,
19221828Sgrehan * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20221828Sgrehan *
21221828Sgrehan * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22221828Sgrehan * or visit www.oracle.com if you need additional information or have any
23221828Sgrehan * questions.
24221828Sgrehan */
25221828Sgrehan
26221828Sgrehanpackage jdk.nashorn.internal.objects;
27221828Sgrehan
28221828Sgrehanimport java.lang.invoke.MethodHandle;
29221828Sgrehanimport java.util.function.Consumer;
30221828Sgrehanimport jdk.nashorn.internal.objects.annotations.Attribute;
31221828Sgrehanimport jdk.nashorn.internal.objects.annotations.Function;
32221828Sgrehanimport jdk.nashorn.internal.objects.annotations.ScriptClass;
33221828Sgrehanimport jdk.nashorn.internal.runtime.JSType;
34221828Sgrehanimport jdk.nashorn.internal.runtime.PropertyMap;
35221828Sgrehanimport jdk.nashorn.internal.runtime.ScriptObject;
36221828Sgrehanimport jdk.nashorn.internal.runtime.ScriptRuntime;
37221828Sgrehanimport jdk.nashorn.internal.runtime.linker.Bootstrap;
38221828Sgrehanimport jdk.nashorn.internal.runtime.linker.InvokeByName;
39221828Sgrehanimport jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
40221828Sgrehan
41221828Sgrehanimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
42221828Sgrehan
43221828Sgrehan/**
44221828Sgrehan * ECMA6 25.1.2 The %IteratorPrototype% Object
45221828Sgrehan */
46221828Sgrehan@ScriptClass("Iterator")
47221828Sgrehanpublic abstract class AbstractIterator extends ScriptObject {
48221828Sgrehan
49221828Sgrehan    // initialized by nasgen
50221828Sgrehan    private static PropertyMap $nasgenmap$;
51221828Sgrehan
52221828Sgrehan    private final static Object ITERATOR_INVOKER_KEY = new Object();
53221828Sgrehan    private final static Object NEXT_INVOKER_KEY     = new Object();
54221828Sgrehan    private final static Object DONE_INVOKER_KEY     = new Object();
55221828Sgrehan    private final static Object VALUE_INVOKER_KEY    = new Object();
56221828Sgrehan
57221828Sgrehan    /** ECMA6 iteration kinds */
58221828Sgrehan    enum IterationKind {
59221828Sgrehan        /** key iteration */
60221828Sgrehan        KEY,
61221828Sgrehan        /** value iteration */
62221828Sgrehan        VALUE,
63221828Sgrehan        /** key+value iteration */
64221828Sgrehan        KEY_VALUE
65221828Sgrehan    }
66221828Sgrehan
67221828Sgrehan    /**
68221828Sgrehan     * Create an abstract iterator object with the given prototype and property map.
69221828Sgrehan     *
70221828Sgrehan     * @param prototype the prototype
71221828Sgrehan     * @param map the property map
72221828Sgrehan     */
73221828Sgrehan    protected AbstractIterator(final ScriptObject prototype, final PropertyMap map) {
74221828Sgrehan        super(prototype, map);
75221828Sgrehan    }
76221828Sgrehan
77221828Sgrehan    /**
78221828Sgrehan     * 25.1.2.1 %IteratorPrototype% [ @@iterator ] ( )
79221828Sgrehan     *
80221828Sgrehan     * @param self the self object
81221828Sgrehan     * @return this iterator
82221828Sgrehan     */
83221828Sgrehan    @Function(attributes = Attribute.NOT_ENUMERABLE, name = "@@iterator")
84221828Sgrehan    public static Object getIterator(final Object self) {
85221828Sgrehan        return self;
86221828Sgrehan    }
87221828Sgrehan
88221828Sgrehan    @Override
89221828Sgrehan    public String getClassName() {
90221828Sgrehan        return "Iterator";
91221828Sgrehan    }
92221828Sgrehan
93221828Sgrehan    /**
94221828Sgrehan     * ES6 25.1.1.2 The Iterator Interface
95221828Sgrehan     *
96221828Sgrehan     * @param arg argument
97221828Sgrehan     * @return next iterator result
98221828Sgrehan     */
99221828Sgrehan    protected abstract IteratorResult next(final Object arg);
100221828Sgrehan
101221828Sgrehan    /**
102221828Sgrehan     * ES6 25.1.1.3 The IteratorResult Interface
103221828Sgrehan     *
104221828Sgrehan     * @param value result value
105221828Sgrehan     * @param done result status
106221828Sgrehan     * @param global the global object
107221828Sgrehan     * @return result object
108221828Sgrehan     */
109221828Sgrehan    protected IteratorResult makeResult(final Object value, final Boolean done, final Global global) {
110221828Sgrehan        return new IteratorResult(value, done, global);
111221828Sgrehan    }
112221828Sgrehan
113221828Sgrehan    /**
114221828Sgrehan     * ES6 7.4.1 GetIterator abstract operation
115221828Sgrehan     *
116221828Sgrehan     * @param iterable an object
117221828Sgrehan     * @param global the global object
118221828Sgrehan     * @return the iterator
119221828Sgrehan     */
120221828Sgrehan    public static Object getIterator(final Object iterable, final Global global) {
121221828Sgrehan        final Object object = Global.toObject(iterable);
122221828Sgrehan
123221828Sgrehan        if (object instanceof ScriptObject) {
124221828Sgrehan            // TODO we need to implement fast property access for Symbol keys in order to use InvokeByName here.
125221828Sgrehan            final Object getter = ((ScriptObject) object).get(NativeSymbol.iterator);
126221828Sgrehan
127221828Sgrehan            if (Bootstrap.isCallable(getter)) {
128221828Sgrehan                try {
129221828Sgrehan                    final MethodHandle invoker = global.getDynamicInvoker(ITERATOR_INVOKER_KEY,
130221828Sgrehan                            () -> Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Object.class));
131221828Sgrehan
132221828Sgrehan                    final Object value = invoker.invokeExact(getter, iterable);
133221828Sgrehan                    if (JSType.isPrimitive(value)) {
134221828Sgrehan                        throw typeError("not.an.object", ScriptRuntime.safeToString(value));
135221828Sgrehan                    }
136221828Sgrehan                    return value;
137221828Sgrehan
138221828Sgrehan                } catch (final Throwable t) {
139221828Sgrehan                    throw new RuntimeException(t);
140221828Sgrehan                }
141221828Sgrehan            }
142221828Sgrehan            throw typeError("not.a.function", ScriptRuntime.safeToString(getter));
143221828Sgrehan        }
144221828Sgrehan
145221828Sgrehan        throw typeError("cannot.get.iterator", ScriptRuntime.safeToString(iterable));
146221828Sgrehan    }
147221828Sgrehan
148221828Sgrehan    /**
149221828Sgrehan     * Iterate over an iterable object, passing every value to {@code consumer}.
150221828Sgrehan     *
151221828Sgrehan     * @param iterable an iterable object
152221828Sgrehan     * @param global the current global
153221828Sgrehan     * @param consumer the value consumer
154221828Sgrehan     */
155221828Sgrehan    public static void iterate(final Object iterable, final Global global, final Consumer<Object> consumer) {
156221828Sgrehan
157221828Sgrehan        final Object iterator = AbstractIterator.getIterator(Global.toObject(iterable), global);
158221828Sgrehan
159221828Sgrehan        final InvokeByName nextInvoker = global.getInvokeByName(AbstractIterator.NEXT_INVOKER_KEY,
160221828Sgrehan                () -> new InvokeByName("next", Object.class, Object.class, Object.class));
161221828Sgrehan        final MethodHandle doneInvoker = global.getDynamicInvoker(AbstractIterator.DONE_INVOKER_KEY,
162221828Sgrehan                () -> Bootstrap.createDynamicInvoker("done", NashornCallSiteDescriptor.GET_PROPERTY, Object.class, Object.class));
163221828Sgrehan        final MethodHandle valueInvoker = global.getDynamicInvoker(AbstractIterator.VALUE_INVOKER_KEY,
164221828Sgrehan                () -> Bootstrap.createDynamicInvoker("value", NashornCallSiteDescriptor.GET_PROPERTY, Object.class, Object.class));
165221828Sgrehan
166221828Sgrehan        try {
167221828Sgrehan            do {
168221828Sgrehan                final Object next = nextInvoker.getGetter().invokeExact(iterator);
169221828Sgrehan                if (!Bootstrap.isCallable(next)) {
170221828Sgrehan                    break;
171221828Sgrehan                }
172221828Sgrehan
173221828Sgrehan                final Object result = nextInvoker.getInvoker().invokeExact(next, iterator, (Object) null);
174221828Sgrehan                if (!(result instanceof ScriptObject)) {
175221828Sgrehan                    break;
176221828Sgrehan                }
177221828Sgrehan
178221828Sgrehan                final Object done = doneInvoker.invokeExact(result);
179221828Sgrehan                if (JSType.toBoolean(done)) {
180221828Sgrehan                    break;
181221828Sgrehan                }
182221828Sgrehan
183221828Sgrehan                consumer.accept(valueInvoker.invokeExact(result));
184221828Sgrehan
185221828Sgrehan            } while (true);
186221828Sgrehan
187221828Sgrehan        } catch (final RuntimeException r) {
188221828Sgrehan            throw r;
189221828Sgrehan        } catch (final Throwable t) {
190221828Sgrehan            throw new RuntimeException(t);
191221828Sgrehan        }
192221828Sgrehan
193221828Sgrehan    }
194221828Sgrehan}
195221828Sgrehan
196221828Sgrehan
197221828Sgrehan