1 /*
   2  * Copyright (c) 2018, 2019, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.vm.ci.hotspot;
  24 
  25 import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM;
  26 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
  27 import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
  28 
  29 import java.lang.annotation.Annotation;
  30 import java.lang.reflect.Array;
  31 import java.lang.reflect.Executable;
  32 import java.lang.reflect.Field;
  33 import java.lang.reflect.Method;
  34 import java.lang.reflect.Type;
  35 import java.util.HashMap;
  36 
  37 import jdk.vm.ci.common.JVMCIError;
  38 import jdk.vm.ci.meta.JavaConstant;
  39 import jdk.vm.ci.meta.JavaKind;
  40 import jdk.vm.ci.meta.MetaAccessProvider;
  41 import jdk.vm.ci.meta.ResolvedJavaField;
  42 import jdk.vm.ci.meta.ResolvedJavaMethod;
  43 import jdk.vm.ci.meta.ResolvedJavaType;
  44 
  45 /**
  46  * Implementation of {@link HotSpotJVMCIReflection} in terms of standard JDK reflection API. This is
  47  * only available when running in the HotSpot heap.
  48  */
  49 final class HotSpotJDKReflection extends HotSpotJVMCIReflection {
  50 
  51     @Override
  52     Object resolveObject(HotSpotObjectConstantImpl object) {
  53         if (object == null) {
  54             return null;
  55         }
  56         return ((DirectHotSpotObjectConstantImpl) object).object;
  57     }
  58 
  59     @Override
  60     boolean isInstance(HotSpotResolvedObjectTypeImpl holder, HotSpotObjectConstantImpl obj) {
  61         Class<?> javaMirror = getMirror(holder);
  62         Object value = resolveObject(obj);
  63         return javaMirror.isInstance(value);
  64     }
  65 
  66     @Override
  67     boolean isAssignableFrom(HotSpotResolvedObjectTypeImpl holder, HotSpotResolvedObjectTypeImpl otherType) {
  68         Class<?> javaMirror = getMirror(holder);
  69         return javaMirror.isAssignableFrom(getMirror(otherType));
  70 
  71     }
  72 
  73     @Override
  74     Annotation[] getAnnotations(HotSpotResolvedObjectTypeImpl holder) {
  75         Class<?> javaMirror = getMirror(holder);
  76         return javaMirror.getAnnotations();
  77     }
  78 
  79     @Override
  80     Annotation[] getDeclaredAnnotations(HotSpotResolvedObjectTypeImpl holder) {
  81         Class<?> javaMirror = getMirror(holder);
  82         return javaMirror.getDeclaredAnnotations();
  83     }
  84 
  85     @Override
  86     <T extends Annotation> T getAnnotation(HotSpotResolvedObjectTypeImpl holder, Class<T> annotationClass) {
  87         Class<?> javaMirror = getMirror(holder);
  88         return javaMirror.getAnnotation(annotationClass);
  89     }
  90 
  91     @Override
  92     boolean isLocalClass(HotSpotResolvedObjectTypeImpl holder) {
  93         Class<?> javaMirror = getMirror(holder);
  94         return javaMirror.isLocalClass();
  95     }
  96 
  97     @Override
  98     boolean isMemberClass(HotSpotResolvedObjectTypeImpl holder) {
  99         Class<?> javaMirror = getMirror(holder);
 100         return javaMirror.isMemberClass();
 101     }
 102 
 103     @Override
 104     HotSpotResolvedObjectType getEnclosingClass(HotSpotResolvedObjectTypeImpl holder) {
 105         Class<?> javaMirror = getMirror(holder);
 106         return (HotSpotResolvedObjectType) runtime().fromClass(javaMirror.getEnclosingClass());
 107     }
 108 
 109     @Override
 110     JavaConstant readFieldValue(HotSpotResolvedObjectTypeImpl holder, HotSpotResolvedJavaField field, boolean isVolatile) {
 111         Class<?> javaMirror = getMirror(holder);
 112         return readFieldValue(field, javaMirror, isVolatile);
 113     }
 114 
 115     @Override
 116     JavaConstant readFieldValue(HotSpotObjectConstantImpl object, HotSpotResolvedJavaField field, boolean isVolatile) {
 117         Object value = resolveObject(object);
 118         return readFieldValue(field, value, isVolatile);
 119     }
 120 
 121     @Override
 122     boolean equals(HotSpotObjectConstantImpl a, HotSpotObjectConstantImpl b) {
 123         return resolveObject(a) == resolveObject(b) && a.isCompressed() == b.isCompressed();
 124     }
 125 
 126     @Override
 127     JavaConstant getJavaMirror(HotSpotResolvedPrimitiveType holder) {
 128         return holder.mirror;
 129     }
 130 
 131     @Override
 132     ResolvedJavaMethod.Parameter[] getParameters(HotSpotResolvedJavaMethodImpl javaMethod) {
 133         java.lang.reflect.Parameter[] javaParameters = getMethod(javaMethod).getParameters();
 134         ResolvedJavaMethod.Parameter[] res = new ResolvedJavaMethod.Parameter[javaParameters.length];
 135         for (int i = 0; i < res.length; i++) {
 136             java.lang.reflect.Parameter src = javaParameters[i];
 137             String paramName = src.isNamePresent() ? src.getName() : null;
 138             res[i] = new ResolvedJavaMethod.Parameter(paramName, src.getModifiers(), javaMethod, i);
 139         }
 140         return res;
 141     }
 142 
 143     @Override
 144     Annotation[][] getParameterAnnotations(HotSpotResolvedJavaMethodImpl javaMethod) {
 145         return getMethod(javaMethod).getParameterAnnotations();
 146     }
 147 
 148     @Override
 149     Type[] getGenericParameterTypes(HotSpotResolvedJavaMethodImpl javaMethod) {
 150         return getMethod(javaMethod).getGenericParameterTypes();
 151     }
 152 
 153     @Override
 154     Annotation[] getFieldAnnotations(HotSpotResolvedJavaFieldImpl javaField) {
 155         return getField(javaField).getAnnotations();
 156     }
 157 
 158     @Override
 159     Annotation[] getMethodAnnotations(HotSpotResolvedJavaMethodImpl javaMethod) {
 160         return getMethod(javaMethod).getAnnotations();
 161     }
 162 
 163     @Override
 164     Annotation[] getMethodDeclaredAnnotations(HotSpotResolvedJavaMethodImpl javaMethod) {
 165         return getMethod(javaMethod).getDeclaredAnnotations();
 166     }
 167 
 168     @Override
 169     Annotation[] getFieldDeclaredAnnotations(HotSpotResolvedJavaFieldImpl javaField) {
 170         return getField(javaField).getDeclaredAnnotations();
 171     }
 172 
 173     @Override
 174     <T extends Annotation> T getMethodAnnotation(HotSpotResolvedJavaMethodImpl javaMethod, Class<T> annotationClass) {
 175         return getMethod(javaMethod).getAnnotation(annotationClass);
 176     }
 177 
 178     @Override
 179     <T extends Annotation> T getFieldAnnotation(HotSpotResolvedJavaFieldImpl javaField, Class<T> annotationClass) {
 180         return getField(javaField).getAnnotation(annotationClass);
 181     }
 182 
 183     @Override
 184     HotSpotResolvedObjectTypeImpl getType(HotSpotObjectConstantImpl object) {
 185         Object value = resolveObject(object);
 186         Class<?> theClass = value.getClass();
 187         return (HotSpotResolvedObjectTypeImpl) runtime().fromClass(theClass);
 188     }
 189 
 190     @Override
 191     String asString(HotSpotObjectConstantImpl object) {
 192         Object value = resolveObject(object);
 193         if (value instanceof String) {
 194             return (String) value;
 195         }
 196         return null;
 197     }
 198 
 199     @Override
 200     ResolvedJavaType asJavaType(HotSpotObjectConstantImpl object) {
 201         Object value = resolveObject(object);
 202         if (value instanceof Class) {
 203             Class<?> javaClass = (Class<?>) value;
 204             return runtime().fromClass(javaClass);
 205         }
 206         if (value instanceof ResolvedJavaType) {
 207             return (ResolvedJavaType) value;
 208         }
 209         return null;
 210     }
 211 
 212     @SuppressWarnings("unchecked")
 213     @Override
 214     <T> T asObject(HotSpotObjectConstantImpl object, Class<T> type) {
 215         Object value = resolveObject(object);
 216         if (type.isInstance(value)) {
 217             return (T) value;
 218         }
 219         return null;
 220     }
 221 
 222     @Override
 223     Object asObject(HotSpotObjectConstantImpl object, HotSpotResolvedJavaType type) {
 224         Object value = resolveObject(object);
 225         if (getMirror(type).isInstance(value)) {
 226             return value;
 227         }
 228         return null;
 229     }
 230 
 231     @Override
 232     String formatString(HotSpotObjectConstantImpl object) {
 233         return JavaKind.Object.format(resolveObject(object));
 234     }
 235 
 236     @Override
 237     Integer getLength(HotSpotObjectConstantImpl arrayObject) {
 238         Object object = resolveObject(arrayObject);
 239         if (object.getClass().isArray()) {
 240             return Array.getLength(object);
 241         }
 242         return null;
 243     }
 244 
 245     @Override
 246     JavaConstant readArrayElement(HotSpotObjectConstantImpl arrayObject, int index) {
 247         Object a = resolveObject(arrayObject);
 248         if (!a.getClass().isArray() || index < 0 || index >= Array.getLength(a)) {
 249             return null;
 250         }
 251         if (a instanceof Object[]) {
 252             Object element = ((Object[]) a)[index];
 253             return forObject(element);
 254         } else {
 255             if (a instanceof int[]) {
 256                 return JavaConstant.forInt(((int[]) a)[index]);
 257             } else if (a instanceof char[]) {
 258                 return JavaConstant.forChar(((char[]) a)[index]);
 259             } else if (a instanceof byte[]) {
 260                 return JavaConstant.forByte(((byte[]) a)[index]);
 261             } else if (a instanceof long[]) {
 262                 return JavaConstant.forLong(((long[]) a)[index]);
 263             } else if (a instanceof short[]) {
 264                 return JavaConstant.forShort(((short[]) a)[index]);
 265             } else if (a instanceof float[]) {
 266                 return JavaConstant.forFloat(((float[]) a)[index]);
 267             } else if (a instanceof double[]) {
 268                 return JavaConstant.forDouble(((double[]) a)[index]);
 269             } else if (a instanceof boolean[]) {
 270                 return JavaConstant.forBoolean(((boolean[]) a)[index]);
 271             } else {
 272                 throw new JVMCIError("Should not reach here");
 273             }
 274         }
 275     }
 276 
 277     @Override
 278     JavaConstant unboxPrimitive(HotSpotObjectConstantImpl source) {
 279         return JavaConstant.forBoxedPrimitive(resolveObject(source));
 280     }
 281 
 282     @Override
 283     JavaConstant forObject(Object value) {
 284         if (value == null) {
 285             return JavaConstant.NULL_POINTER;
 286         }
 287         return forNonNullObject(value);
 288     }
 289 
 290     private static HotSpotObjectConstantImpl forNonNullObject(Object value) {
 291         return DirectHotSpotObjectConstantImpl.forNonNullObject(value, false);
 292     }
 293 
 294     @Override
 295     JavaConstant boxPrimitive(JavaConstant source) {
 296         return forNonNullObject(source.asBoxedPrimitive());
 297     }
 298 
 299     @Override
 300     int getInt(HotSpotObjectConstantImpl object, long displacement) {
 301         return UNSAFE.getInt((resolveObject(object)), displacement);
 302     }
 303 
 304     @Override
 305     byte getByte(HotSpotObjectConstantImpl object, long displacement) {
 306         return UNSAFE.getByte(resolveObject(object), displacement);
 307     }
 308 
 309     @Override
 310     short getShort(HotSpotObjectConstantImpl object, long displacement) {
 311         return UNSAFE.getShort(resolveObject(object), displacement);
 312     }
 313 
 314     @Override
 315     long getLong(HotSpotObjectConstantImpl object, long displacement) {
 316         return UNSAFE.getLong(resolveObject(object), displacement);
 317     }
 318 
 319     @Override
 320     void checkRead(HotSpotObjectConstantImpl constant, JavaKind kind, long displacement, HotSpotResolvedObjectType type) {
 321         checkRead(kind, displacement, type, resolveObject(constant));
 322     }
 323 
 324     /**
 325      * Offset of injected {@code java.lang.Class::oop_size} field. No need to make {@code volatile}
 326      * as initialization is idempotent.
 327      */
 328     private long oopSizeOffset;
 329 
 330     private static int computeOopSizeOffset(HotSpotJVMCIRuntime runtime) {
 331         MetaAccessProvider metaAccess = runtime.getHostJVMCIBackend().getMetaAccess();
 332         ResolvedJavaType staticType = metaAccess.lookupJavaType(Class.class);
 333         for (ResolvedJavaField f : staticType.getInstanceFields(false)) {
 334             if (f.getName().equals("oop_size")) {
 335                 int offset = f.getOffset();
 336                 assert offset != 0 : "not expecting offset of java.lang.Class::oop_size to be 0";
 337                 return offset;
 338             }
 339         }
 340         throw new JVMCIError("Could not find injected java.lang.Class::oop_size field");
 341     }
 342 
 343     long oopSizeOffset() {
 344         if (oopSizeOffset == 0) {
 345             oopSizeOffset = computeOopSizeOffset(runtime());
 346         }
 347         return oopSizeOffset;
 348     }
 349 
 350     private boolean checkRead(JavaKind kind, long displacement, HotSpotResolvedObjectType type, Object object) {
 351         if (type.isArray()) {
 352             ResolvedJavaType componentType = type.getComponentType();
 353             JavaKind componentKind = componentType.getJavaKind();
 354             final int headerSize = runtime().getArrayBaseOffset(componentKind);
 355             int sizeOfElement = runtime().getArrayIndexScale(componentKind);
 356             int length = Array.getLength(object);
 357             long arrayEnd = headerSize + (sizeOfElement * length);
 358             boolean aligned = ((displacement - headerSize) % sizeOfElement) == 0;
 359             if (displacement < 0 || displacement > (arrayEnd - sizeOfElement) || (kind == JavaKind.Object && !aligned)) {
 360                 int index = (int) ((displacement - headerSize) / sizeOfElement);
 361                 throw new IllegalArgumentException("Unsafe array access: reading element of kind " + kind +
 362                                 " at offset " + displacement + " (index ~ " + index + ") in " +
 363                                 type.toJavaName() + " object of length " + length);
 364             }
 365         } else if (kind != JavaKind.Object) {
 366             long size;
 367             if (object instanceof Class) {
 368                 int wordSize = runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordSize;
 369                 size = UNSAFE.getInt(object, oopSizeOffset()) * wordSize;
 370             } else {
 371                 size = Math.abs(type.instanceSize());
 372             }
 373             int bytesToRead = kind.getByteCount();
 374             if (displacement + bytesToRead > size || displacement < 0) {
 375                 throw new IllegalArgumentException("Unsafe access: reading " + bytesToRead + " bytes at offset " + displacement + " in " +
 376                                 type.toJavaName() + " object of size " + size);
 377             }
 378         } else {
 379             ResolvedJavaField field = null;
 380             if (object instanceof Class) {
 381                 // Read of a static field
 382                 HotSpotResolvedJavaType hotSpotResolvedJavaType = runtime().fromClass((Class<?>) object);
 383                 if (hotSpotResolvedJavaType instanceof HotSpotResolvedObjectTypeImpl) {
 384                     HotSpotResolvedObjectTypeImpl staticFieldsHolder = (HotSpotResolvedObjectTypeImpl) hotSpotResolvedJavaType;
 385                     field = staticFieldsHolder.findStaticFieldWithOffset(displacement, JavaKind.Object);
 386                 }
 387             }
 388             if (field == null) {
 389                 field = type.findInstanceFieldWithOffset(displacement, JavaKind.Object);
 390             }
 391             if (field == null) {
 392                 throw new IllegalArgumentException("Unsafe object access: field not found for read of kind Object" +
 393                                 " at offset " + displacement + " in " + type.toJavaName() + " object");
 394             }
 395             if (field.getJavaKind() != JavaKind.Object) {
 396                 throw new IllegalArgumentException("Unsafe object access: field " + field.format("%H.%n:%T") + " not of expected kind Object" +
 397                                 " at offset " + displacement + " in " + type.toJavaName() + " object");
 398             }
 399         }
 400         return true;
 401     }
 402 
 403     JavaConstant readFieldValue(HotSpotResolvedJavaField field, Object obj, boolean isVolatile) {
 404         assert obj != null;
 405         assert !field.isStatic() || obj instanceof Class;
 406         long displacement = field.getOffset();
 407 
 408         assert checkRead(field.getJavaKind(), displacement,
 409                         (HotSpotResolvedObjectType) runtime().getHostJVMCIBackend().getMetaAccess().lookupJavaType(field.isStatic() ? (Class<?>) obj : obj.getClass()),
 410                         obj);
 411         JavaKind kind = field.getJavaKind();
 412         switch (kind) {
 413             case Boolean:
 414                 return JavaConstant.forBoolean(isVolatile ? UNSAFE.getBooleanVolatile(obj, displacement) : UNSAFE.getBoolean(obj, displacement));
 415             case Byte:
 416                 return JavaConstant.forByte(isVolatile ? UNSAFE.getByteVolatile(obj, displacement) : UNSAFE.getByte(obj, displacement));
 417             case Char:
 418                 return JavaConstant.forChar(isVolatile ? UNSAFE.getCharVolatile(obj, displacement) : UNSAFE.getChar(obj, displacement));
 419             case Short:
 420                 return JavaConstant.forShort(isVolatile ? UNSAFE.getShortVolatile(obj, displacement) : UNSAFE.getShort(obj, displacement));
 421             case Int:
 422                 return JavaConstant.forInt(isVolatile ? UNSAFE.getIntVolatile(obj, displacement) : UNSAFE.getInt(obj, displacement));
 423             case Long:
 424                 return JavaConstant.forLong(isVolatile ? UNSAFE.getLongVolatile(obj, displacement) : UNSAFE.getLong(obj, displacement));
 425             case Float:
 426                 return JavaConstant.forFloat(isVolatile ? UNSAFE.getFloatVolatile(obj, displacement) : UNSAFE.getFloat(obj, displacement));
 427             case Double:
 428                 return JavaConstant.forDouble(isVolatile ? UNSAFE.getDoubleVolatile(obj, displacement) : UNSAFE.getDouble(obj, displacement));
 429             case Object:
 430                 return forObject(isVolatile ? UNSAFE.getReferenceVolatile(obj, displacement) : UNSAFE.getReference(obj, displacement));
 431             default:
 432                 throw new IllegalArgumentException("Unsupported kind: " + kind);
 433 
 434         }
 435     }
 436 
 437     /**
 438      * Gets a {@link Method} object corresponding to {@code method}. This method guarantees the same
 439      * {@link Method} object is returned if called twice on the same {@code method} value.
 440      */
 441     private static Executable getMethod(HotSpotResolvedJavaMethodImpl method) {
 442         assert !method.isClassInitializer() : method;
 443         if (method.toJavaCache == null) {
 444             synchronized (method) {
 445                 if (method.toJavaCache == null) {
 446                     method.toJavaCache = compilerToVM().asReflectionExecutable(method);
 447                 }
 448             }
 449         }
 450         return method.toJavaCache;
 451     }
 452 
 453     /**
 454      * Gets a {@link Field} object corresponding to {@code field}. This method guarantees the same
 455      * {@link Field} object is returned if called twice on the same {@code field} value. This is
 456      * required to ensure the results of {@link HotSpotResolvedJavaFieldImpl#getAnnotations()} and
 457      * {@link HotSpotResolvedJavaFieldImpl#getAnnotation(Class)} are stable (i.e., for a given field
 458      * {@code f} and annotation class {@code a}, the same object is returned for each call to
 459      * {@code f.getAnnotation(a)}).
 460      */
 461     private static Field getField(HotSpotResolvedJavaFieldImpl field) {
 462         HotSpotResolvedObjectTypeImpl declaringClass = field.getDeclaringClass();
 463         synchronized (declaringClass) {
 464             HashMap<HotSpotResolvedJavaFieldImpl, Field> cache = declaringClass.reflectionFieldCache;
 465             if (cache == null) {
 466                 cache = new HashMap<>();
 467                 declaringClass.reflectionFieldCache = cache;
 468             }
 469             Field reflect = cache.get(field);
 470             if (reflect == null) {
 471                 reflect = compilerToVM().asReflectionField(field.getDeclaringClass(), field.getIndex());
 472                 cache.put(field, reflect);
 473             }
 474             return reflect;
 475         }
 476     }
 477 
 478     Class<?> getMirror(HotSpotResolvedObjectTypeImpl holder) {
 479         return (Class<?>) resolveObject((HotSpotObjectConstantImpl) holder.getJavaMirror());
 480     }
 481 
 482     Class<?> getMirror(HotSpotResolvedJavaType type) {
 483         assert type != null;
 484         if (type instanceof HotSpotResolvedPrimitiveType) {
 485             return (Class<?>) resolveObject(((HotSpotResolvedPrimitiveType) type).mirror);
 486         } else {
 487             return getMirror((HotSpotResolvedObjectTypeImpl) type);
 488         }
 489     }
 490 }
 491