1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3  *
4  * This code is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 only, as
6  * published by the Free Software Foundation.  Oracle designates this
7  * particular file as subject to the "Classpath" exception as provided
8  * by Oracle in the LICENSE file that accompanied this code.
9  *
10  * This code is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * version 2 for more details (a copy is included in the LICENSE file that
14  * accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License version
17  * 2 along with this work; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21  * or visit www.oracle.com if you need additional information or have any
22  * questions.
23  */
24 
25 package jdk.nashorn.internal.runtime.arrays;
26 
27 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
28 import static jdk.nashorn.internal.lookup.Lookup.MH;
29 import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
30 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
31 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.lang.invoke.MethodType;
35 import java.lang.invoke.SwitchPoint;
36 import jdk.dynalink.CallSiteDescriptor;
37 import jdk.dynalink.linker.GuardedInvocation;
38 import jdk.dynalink.linker.LinkRequest;
39 import jdk.nashorn.internal.codegen.types.Type;
40 import jdk.nashorn.internal.lookup.Lookup;
41 import jdk.nashorn.internal.runtime.ScriptObject;
42 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
43 import 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")
50 public abstract class ContinuousArrayData extends ArrayData {
51     /**
52      * Constructor
53      * @param length length (elementLength)
54      */
ContinuousArrayData(final long length)55     protected ContinuousArrayData(final long length) {
56         super(length);
57     }
58 
59     /**
60      * Check if we can put one more element at the end of this continuous
61      * array without reallocating, or if we are overwriting an already
62      * allocated element
63      *
64      * @param index index to check
65      * @return true if we don't need to do any array reallocation to fit an element at index
66      */
hasRoomFor(final int index)67     public final boolean hasRoomFor(final int index) {
68         return has(index) || (index == length() && ensure(index) == this);
69     }
70 
71     /**
72      * Check if an arraydata is empty
73      * @return true if empty
74      */
isEmpty()75     public boolean isEmpty() {
76         return length() == 0L;
77     }
78 
79     /**
80      * Return element getter for a certain type at a certain program point
81      * @param returnType   return type
82      * @param programPoint program point
83      * @return element getter or null if not supported (used to implement slow linkage instead
84      *   as fast isn't possible)
85      */
getElementGetter(final Class<?> returnType, final int programPoint)86     public abstract MethodHandle getElementGetter(final Class<?> returnType, final int programPoint);
87 
88     /**
89      * Return element getter for a certain type at a certain program point
90      * @param elementType element type
91      * @return element setter or null if not supported (used to implement slow linkage instead
92      *   as fast isn't possible)
93      */
getElementSetter(final Class<?> elementType)94     public abstract MethodHandle getElementSetter(final Class<?> elementType);
95 
96     /**
97      * Version of has that throws a class cast exception if element does not exist
98      * used for relinking
99      *
100      * @param index index to check - currently only int indexes
101      * @return index
102      */
throwHas(final int index)103     protected final int throwHas(final int index) {
104         if (!has(index)) {
105             throw new ClassCastException();
106         }
107         return index;
108     }
109 
110     @Override
copy()111     public abstract ContinuousArrayData copy();
112 
113     /**
114      * Returns the type used to store an element in this array
115      * @return element type
116      */
getElementType()117     public abstract Class<?> getElementType();
118 
119     @Override
getOptimisticType()120     public Type getOptimisticType() {
121         return Type.typeFor(getElementType());
122     }
123 
124     /**
125      * Returns the boxed type of the type used to store an element in this array
126      * @return element type
127      */
getBoxedElementType()128     public abstract Class<?> getBoxedElementType();
129 
130     /**
131      * Get the widest element type of two arrays. This can be done faster in subclasses, but
132      * this works for all ContinuousArrayDatas and for where more optimal checks haven't been
133      * implemented.
134      *
135      * @param otherData another ContinuousArrayData
136      * @return the widest boxed element type
137      */
widest(final ContinuousArrayData otherData)138     public ContinuousArrayData widest(final ContinuousArrayData otherData) {
139         final Class<?> elementType = getElementType();
140         return Type.widest(elementType, otherData.getElementType()) == elementType ? this : otherData;
141     }
142 
143     /**
144      * Look up a continuous array element getter
145      * @param get          getter, sometimes combined with a has check that throws CCE on failure for relink
146      * @param returnType   return type
147      * @param programPoint program point
148      * @return array getter
149      */
getContinuousElementGetter(final MethodHandle get, final Class<?> returnType, final int programPoint)150     protected final MethodHandle getContinuousElementGetter(final MethodHandle get, final Class<?> returnType, final int programPoint) {
151         return getContinuousElementGetter(getClass(), get, returnType, programPoint);
152     }
153 
154     /**
155      * Look up a continuous array element setter
156      * @param set          setter, sometimes combined with a has check that throws CCE on failure for relink
157      * @param returnType   return type
158      * @return array setter
159      */
getContinuousElementSetter(final MethodHandle set, final Class<?> returnType)160     protected final MethodHandle getContinuousElementSetter(final MethodHandle set, final Class<?> returnType) {
161         return getContinuousElementSetter(getClass(), set, returnType);
162     }
163 
164     /**
165      * Return element getter for a {@link ContinuousArrayData}
166      * @param clazz        clazz for exact type guard
167      * @param getHas       has getter
168      * @param returnType   return type
169      * @param programPoint program point
170      * @return method handle for element setter
171      */
getContinuousElementGetter(final Class<? extends ContinuousArrayData> clazz, final MethodHandle getHas, final Class<?> returnType, final int programPoint)172     protected MethodHandle getContinuousElementGetter(final Class<? extends ContinuousArrayData> clazz, final MethodHandle getHas, final Class<?> returnType, final int programPoint) {
173         final boolean isOptimistic = isValid(programPoint);
174         final int     fti          = getAccessorTypeIndex(getHas.type().returnType());
175         final int     ti           = getAccessorTypeIndex(returnType);
176         MethodHandle  mh           = getHas;
177 
178         if (isOptimistic) {
179             if (ti < fti) {
180                 mh = MH.insertArguments(ArrayData.THROW_UNWARRANTED.methodHandle(), 1, programPoint);
181             }
182         }
183         mh = MH.asType(mh, mh.type().changeReturnType(returnType).changeParameterType(0, clazz));
184 
185         if (!isOptimistic) {
186             //for example a & array[17];
187             return Lookup.filterReturnType(mh, returnType);
188         }
189         return mh;
190     }
191 
192     /**
193      * Return element setter for a {@link ContinuousArrayData}
194      * @param clazz        class for exact type guard
195      * @param setHas       set has guard
196      * @param elementType  element type
197      * @return method handle for element setter
198      */
getContinuousElementSetter(final Class<? extends ContinuousArrayData> clazz, final MethodHandle setHas, final Class<?> elementType)199     protected MethodHandle getContinuousElementSetter(final Class<? extends ContinuousArrayData> clazz, final MethodHandle setHas, final Class<?> elementType) {
200         return MH.asType(setHas, setHas.type().changeParameterType(2, elementType).changeParameterType(0, clazz));
201     }
202 
203     /** Fast access guard - it is impractical for JIT performance reasons to use only CCE asType as guard :-(, also we need
204       the null case explicitly, which is the one that CCE doesn't handle */
205     protected static final MethodHandle FAST_ACCESS_GUARD =
206             MH.dropArguments(
207                     staticCall(
208                             MethodHandles.lookup(),
209                             ContinuousArrayData.class,
210                             "guard",
211                             boolean.class,
212                             Class.class,
213                             ScriptObject.class).methodHandle(),
214                     2,
215                     int.class);
216 
217     @SuppressWarnings("unused")
guard(final Class<? extends ContinuousArrayData> clazz, final ScriptObject sobj)218     private static boolean guard(final Class<? extends ContinuousArrayData> clazz, final ScriptObject sobj) {
219         return sobj != null && sobj.getArray().getClass() == clazz;
220     }
221 
222     /**
223      * Return a fast linked array getter, or null if we have to dispatch to super class
224      * @param desc     descriptor
225      * @param request  link request
226      * @return invocation or null if needs to be sent to slow relink
227      */
228     @Override
findFastGetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request)229     public GuardedInvocation findFastGetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) {
230         final MethodType callType   = desc.getMethodType();
231         final Class<?>   indexType  = callType.parameterType(1);
232         final Class<?>   returnType = callType.returnType();
233 
234         if (ContinuousArrayData.class.isAssignableFrom(clazz) && indexType == int.class) {
235             final Object[] args  = request.getArguments();
236             final int      index = (int)args[args.length - 1];
237 
238             if (has(index)) {
239                 final MethodHandle getArray     = ScriptObject.GET_ARRAY.methodHandle();
240                 final int          programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT;
241                 MethodHandle       getElement   = getElementGetter(returnType, programPoint);
242                 if (getElement != null) {
243                     getElement = MH.filterArguments(getElement, 0, MH.asType(getArray, getArray.type().changeReturnType(clazz)));
244                     final MethodHandle guard = MH.insertArguments(FAST_ACCESS_GUARD, 0, clazz);
245                     return new GuardedInvocation(getElement, guard, (SwitchPoint)null, ClassCastException.class);
246                 }
247             }
248         }
249 
250         return null;
251     }
252 
253     /**
254      * Return a fast linked array setter, or null if we have to dispatch to super class
255      * @param desc     descriptor
256      * @param request  link request
257      * @return invocation or null if needs to be sent to slow relink
258      */
259     @Override
findFastSetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request)260     public GuardedInvocation findFastSetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
261         final MethodType callType    = desc.getMethodType();
262         final Class<?>   indexType   = callType.parameterType(1);
263         final Class<?>   elementType = callType.parameterType(2);
264 
265         if (ContinuousArrayData.class.isAssignableFrom(clazz) && indexType == int.class) {
266             final Object[]        args  = request.getArguments();
267             final int             index = (int)args[args.length - 2];
268 
269             if (hasRoomFor(index)) {
270                 MethodHandle setElement = getElementSetter(elementType); //Z(continuousarraydata, int, int), return true if successful
271                 if (setElement != null) {
272                     //else we are dealing with a wider type than supported by this callsite
273                     MethodHandle getArray = ScriptObject.GET_ARRAY.methodHandle();
274                     getArray   = MH.asType(getArray, getArray.type().changeReturnType(getClass()));
275                     setElement = MH.filterArguments(setElement, 0, getArray);
276                     final MethodHandle guard = MH.insertArguments(FAST_ACCESS_GUARD, 0, clazz);
277                     return new GuardedInvocation(setElement, guard, (SwitchPoint)null, ClassCastException.class); //CCE if not a scriptObject anymore
278                 }
279             }
280         }
281 
282         return null;
283     }
284 
285     /**
286      * Specialization - fast push implementation
287      * @param arg argument
288      * @return new array length
289      */
fastPush(final int arg)290     public double fastPush(final int arg) {
291         throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
292     }
293 
294     /**
295      * Specialization - fast push implementation
296      * @param arg argument
297      * @return new array length
298      */
fastPush(final long arg)299     public double fastPush(final long arg) {
300         throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
301     }
302 
303     /**
304      * Specialization - fast push implementation
305      * @param arg argument
306      * @return new array length
307      */
fastPush(final double arg)308     public double fastPush(final double arg) {
309         throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
310     }
311 
312     /**
313      * Specialization - fast push implementation
314      * @param arg argument
315      * @return new array length
316      */
fastPush(final Object arg)317     public double fastPush(final Object arg) {
318         throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
319     }
320 
321     /**
322      * Specialization - fast pop implementation
323      * @return element value
324      */
fastPopInt()325     public int fastPopInt() {
326         throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
327     }
328 
329     /**
330      * Specialization - fast pop implementation
331      * @return element value
332      */
fastPopDouble()333     public double fastPopDouble() {
334        throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
335     }
336 
337     /**
338      * Specialization - fast pop implementation
339      * @return element value
340      */
fastPopObject()341     public Object fastPopObject() {
342         throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
343     }
344 
345     /**
346      * Specialization - fast concat implementation
347      * @param otherData data to concat
348      * @return new arraydata
349      */
fastConcat(final ContinuousArrayData otherData)350     public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
351         throw new ClassCastException(String.valueOf(getClass()) + " != " + String.valueOf(otherData.getClass()));
352     }
353 }
354