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