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      */
  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      */
  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      */
  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      */
  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      */
  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      */
 103     protected final int throwHas(final int index) {
 104         if (!has(index)) {
 105             throw new ClassCastException();
 106         }
 107         return index;
 108     }
 109 
 110     @Override
 111     public abstract ContinuousArrayData copy();
 112 
 113     /**
 114      * Returns the type used to store an element in this array
 115      * @return element type
 116      */
 117     public abstract Class<?> getElementType();
 118 
 119     @Override
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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")
 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
 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
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 350     public ContinuousArrayData fastConcat(final ContinuousArrayData otherData) {
 351         throw new ClassCastException(String.valueOf(getClass()) + " != " + String.valueOf(otherData.getClass()));
 352     }
 353 }