ContinuousArrayData.java revision 1062:f9ed1ca59030
1/*
2 * Copyright (c) 2010, 2013, 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
26package jdk.nashorn.internal.runtime.arrays;
27
28import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
29import static jdk.nashorn.internal.lookup.Lookup.MH;
30import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
31import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
32import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
33
34import java.lang.invoke.MethodHandle;
35import java.lang.invoke.MethodHandles;
36import java.lang.invoke.MethodType;
37import java.lang.invoke.SwitchPoint;
38import jdk.internal.dynalink.CallSiteDescriptor;
39import jdk.internal.dynalink.linker.GuardedInvocation;
40import jdk.internal.dynalink.linker.LinkRequest;
41import jdk.nashorn.internal.codegen.types.Type;
42import jdk.nashorn.internal.lookup.Lookup;
43import jdk.nashorn.internal.runtime.ScriptObject;
44import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
45import jdk.nashorn.internal.runtime.logging.Logger;
46
47/**
48 * Interface implemented by all arrays that are directly accessible as underlying
49 * native arrays
50 */
51@Logger(name="arrays")
52public abstract class ContinuousArrayData extends ArrayData {
53
54    private SwitchPoint sp;
55
56    /**
57     * Constructor
58     * @param length length (elementLength)
59     */
60    protected ContinuousArrayData(final long length) {
61        super(length);
62    }
63
64    private SwitchPoint ensureSwitchPointExists() {
65        if (sp == null){
66            sp = new SwitchPoint();
67        }
68        return sp;
69    }
70
71    @Override
72    public void invalidateSetters() {
73        SwitchPoint.invalidateAll(new SwitchPoint[] { ensureSwitchPointExists() });
74    }
75
76    /**
77     * Check if we can put one more element at the end of this continous
78     * array without reallocating, or if we are overwriting an already
79     * allocated element
80     *
81     * @param index index to check
82     * @return true if we don't need to do any array reallocation to fit an element at index
83     */
84    public final boolean hasRoomFor(final int index) {
85        return has(index) || (index == length && ensure(index) == this);
86    }
87
88    /**
89     * Return element getter for a certain type at a certain program point
90     * @param returnType   return type
91     * @param programPoint program point
92     * @return element getter or null if not supported (used to implement slow linkage instead
93     *   as fast isn't possible)
94     */
95    public abstract MethodHandle getElementGetter(final Class<?> returnType, final int programPoint);
96
97    /**
98     * Return element getter for a certain type at a certain program point
99     * @param elementType element type
100     * @return element setter or null if not supported (used to implement slow linkage instead
101     *   as fast isn't possible)
102     */
103    public abstract MethodHandle getElementSetter(final Class<?> elementType);
104
105    /**
106     * Version of has that throws a class cast exception if element does not exist
107     * used for relinking
108     *
109     * @param index index to check - currently only int indexes
110     * @return index
111     */
112    protected int throwHas(final int index) {
113        if (!has(index)) {
114            throw new ClassCastException();
115        }
116        return index;
117    }
118
119    /**
120     * Returns the type used to store an element in this array
121     * @return element type
122     */
123    public abstract Class<?> getElementType();
124
125    @Override
126    public Type getOptimisticType() {
127        return Type.typeFor(getElementType());
128    }
129
130    /**
131     * Look up a continuous array element getter
132     * @param get          getter, sometimes combined with a has check that throws CCE on failure for relink
133     * @param returnType   return type
134     * @param programPoint program point
135     * @return array getter
136     */
137    protected final MethodHandle getContinuousElementGetter(final MethodHandle get, final Class<?> returnType, final int programPoint) {
138        return getContinuousElementGetter(getClass(), get, returnType, programPoint);
139    }
140
141    /**
142     * Look up a continuous array element setter
143     * @param set          setter, sometimes combined with a has check that throws CCE on failure for relink
144     * @param returnType   return type
145     * @return array setter
146     */
147    protected final MethodHandle getContinuousElementSetter(final MethodHandle set, final Class<?> returnType) {
148        return getContinuousElementSetter(getClass(), set, returnType);
149    }
150
151    /**
152     * Return element getter for a {@link ContinuousArrayData}
153     * @param clazz        clazz for exact type guard
154     * @param getHas       has getter
155     * @param returnType   return type
156     * @param programPoint program point
157     * @return method handle for element setter
158     */
159    protected MethodHandle getContinuousElementGetter(final Class<? extends ContinuousArrayData> clazz, final MethodHandle getHas, final Class<?> returnType, final int programPoint) {
160        final boolean isOptimistic = isValid(programPoint);
161        final int     fti          = getAccessorTypeIndex(getHas.type().returnType());
162        final int     ti           = getAccessorTypeIndex(returnType);
163        MethodHandle  mh           = getHas;
164
165        if (isOptimistic) {
166            if (ti < fti) {
167                mh = MH.insertArguments(ArrayData.THROW_UNWARRANTED.methodHandle(), 1, programPoint);
168            }
169        }
170        mh = MH.asType(mh, mh.type().changeReturnType(returnType).changeParameterType(0, clazz));
171
172        if (!isOptimistic) {
173            //for example a & array[17];
174            return Lookup.filterReturnType(mh, returnType);
175        }
176        return mh;
177    }
178
179    /**
180     * Return element setter for a {@link ContinuousArrayData}
181     * @param clazz        clazz for exact type guard
182     * @param setHas       set has guard
183     * @param elementType  element type
184     * @return method handle for element setter
185     */
186    protected MethodHandle getContinuousElementSetter(final Class<? extends ContinuousArrayData> clazz, final MethodHandle setHas, final Class<?> elementType) {
187        return MH.asType(setHas, setHas.type().changeParameterType(2, elementType).changeParameterType(0, clazz));
188    }
189
190    /** Fast access guard - it is impractical for JIT performance reasons to use only CCE asType as guard :-(, also we need
191      the null case explicitly, which is the one that CCE doesn't handle */
192    protected static final MethodHandle FAST_ACCESS_GUARD =
193            MH.dropArguments(
194                    staticCall(
195                            MethodHandles.lookup(),
196                            ContinuousArrayData.class,
197                            "guard",
198                            boolean.class,
199                            Class.class,
200                            ScriptObject.class).methodHandle(),
201                    2,
202                    int.class);
203
204    @SuppressWarnings("unused")
205    private static final boolean guard(final Class<? extends ContinuousArrayData> clazz, final ScriptObject sobj) {
206        if (sobj != null && sobj.getArray().getClass() == clazz) {
207            return true;
208        }
209        return false;
210    }
211
212    /**
213     * Return a fast linked array getter, or null if we have to dispatch to super class
214     * @param desc     descriptor
215     * @param request  link request
216     * @return invocation or null if needs to be sent to slow relink
217     */
218    @Override
219    public GuardedInvocation findFastGetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) {
220        final MethodType callType   = desc.getMethodType();
221        final Class<?>   indexType  = callType.parameterType(1);
222        final Class<?>   returnType = callType.returnType();
223
224        if (ContinuousArrayData.class.isAssignableFrom(clazz) && indexType == int.class) {
225            final Object[] args  = request.getArguments();
226            final int      index = (int)args[args.length - 1];
227
228            if (has(index)) {
229                final MethodHandle getArray     = ScriptObject.GET_ARRAY.methodHandle();
230                final int          programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT;
231                MethodHandle       getElement   = getElementGetter(returnType, programPoint);
232                if (getElement != null) {
233                    getElement = MH.filterArguments(getElement, 0, MH.asType(getArray, getArray.type().changeReturnType(clazz)));
234                    final MethodHandle guard = MH.insertArguments(FAST_ACCESS_GUARD, 0, clazz);
235                    return new GuardedInvocation(getElement, guard, (SwitchPoint)null, ClassCastException.class);
236                }
237            }
238        }
239
240        return null;
241    }
242
243    /**
244     * Return a fast linked array setter, or null if we have to dispatch to super class
245     * @param desc     descriptor
246     * @param request  link request
247     * @return invocation or null if needs to be sent to slow relink
248     */
249    @Override
250    public GuardedInvocation findFastSetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
251        final MethodType callType    = desc.getMethodType();
252        final Class<?>   indexType   = callType.parameterType(1);
253        final Class<?>   elementType = callType.parameterType(2);
254
255        if (ContinuousArrayData.class.isAssignableFrom(clazz) && indexType == int.class) {
256            final Object[]        args  = request.getArguments();
257            final int             index = (int)args[args.length - 2];
258
259            //sp may be invalidated by e.g. preventExtensions before the first setter is linked
260            //then it is already created. otherwise, create it here to guard against future
261            //invalidations
262            ensureSwitchPointExists();
263
264            if (!sp.hasBeenInvalidated() && hasRoomFor(index)) {
265                MethodHandle setElement = getElementSetter(elementType); //Z(continuousarraydata, int, int), return true if successful
266                if (setElement != null) {
267                    //else we are dealing with a wider type than supported by this callsite
268                    MethodHandle getArray = ScriptObject.GET_ARRAY.methodHandle();
269                    getArray   = MH.asType(getArray, getArray.type().changeReturnType(getClass()));
270                    setElement = MH.filterArguments(setElement, 0, getArray);
271                    final MethodHandle guard = MH.insertArguments(FAST_ACCESS_GUARD, 0, clazz);
272                    return new GuardedInvocation(setElement, guard, sp, ClassCastException.class); //CCE if not a scriptObject anymore
273                }
274            }
275        }
276
277        return null;
278    }
279
280    /**
281     * Specialization - fast push implementation
282     * @param arg argument
283     * @return new array length
284     */
285    public long fastPush(final int arg) {
286        throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
287    }
288
289    /**
290     * Specialization - fast push implementation
291     * @param arg argument
292     * @return new array length
293     */
294    public long fastPush(final long arg) {
295        throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
296    }
297
298    /**
299     * Specialization - fast push implementation
300     * @param arg argument
301     * @return new array length
302     */
303    public long fastPush(final double arg) {
304        throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
305    }
306
307    /**
308     * Specialization - fast push implementation
309     * @param arg argument
310     * @return new array length
311     */
312    public long fastPush(final Object arg) {
313        throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
314    }
315
316    /**
317     * Specialization - fast pop implementation
318     * @return element value
319     */
320    public int fastPopInt() {
321        throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
322    }
323
324    /**
325     * Specialization - fast pop implementation
326     * @return element value
327     */
328    public long fastPopLong() {
329        throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
330    }
331
332    /**
333     * Specialization - fast pop implementation
334     * @return element value
335     */
336    public double fastPopDouble() {
337       throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
338    }
339
340    /**
341     * Specialization - fast pop implementation
342     * @return element value
343     */
344    public Object fastPopObject() {
345        throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
346    }
347}
348