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 }