--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJDKReflection.java Wed May 01 12:31:29 2019 -0700
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.vm.ci.hotspot;
+
+import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM;
+import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
+import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.MetaAccessProvider;
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+
+/**
+ * Implementation of {@link HotSpotJVMCIReflection} in terms of standard JDK reflection API. This is
+ * only available when running in the HotSpot heap.
+ */
+final class HotSpotJDKReflection extends HotSpotJVMCIReflection {
+
+ @Override
+ Object resolveObject(HotSpotObjectConstantImpl object) {
+ if (object == null) {
+ return null;
+ }
+ return ((DirectHotSpotObjectConstantImpl) object).object;
+ }
+
+ @Override
+ boolean isInstance(HotSpotResolvedObjectTypeImpl holder, HotSpotObjectConstantImpl obj) {
+ Class<?> javaMirror = getMirror(holder);
+ Object value = resolveObject(obj);
+ return javaMirror.isInstance(value);
+ }
+
+ @Override
+ boolean isAssignableFrom(HotSpotResolvedObjectTypeImpl holder, HotSpotResolvedObjectTypeImpl otherType) {
+ Class<?> javaMirror = getMirror(holder);
+ return javaMirror.isAssignableFrom(getMirror(otherType));
+
+ }
+
+ @Override
+ Annotation[] getAnnotations(HotSpotResolvedObjectTypeImpl holder) {
+ Class<?> javaMirror = getMirror(holder);
+ return javaMirror.getAnnotations();
+ }
+
+ @Override
+ Annotation[] getDeclaredAnnotations(HotSpotResolvedObjectTypeImpl holder) {
+ Class<?> javaMirror = getMirror(holder);
+ return javaMirror.getDeclaredAnnotations();
+ }
+
+ @Override
+ <T extends Annotation> T getAnnotation(HotSpotResolvedObjectTypeImpl holder, Class<T> annotationClass) {
+ Class<?> javaMirror = getMirror(holder);
+ return javaMirror.getAnnotation(annotationClass);
+ }
+
+ @Override
+ boolean isLocalClass(HotSpotResolvedObjectTypeImpl holder) {
+ Class<?> javaMirror = getMirror(holder);
+ return javaMirror.isLocalClass();
+ }
+
+ @Override
+ boolean isMemberClass(HotSpotResolvedObjectTypeImpl holder) {
+ Class<?> javaMirror = getMirror(holder);
+ return javaMirror.isMemberClass();
+ }
+
+ @Override
+ HotSpotResolvedObjectType getEnclosingClass(HotSpotResolvedObjectTypeImpl holder) {
+ Class<?> javaMirror = getMirror(holder);
+ return (HotSpotResolvedObjectType) runtime().fromClass(javaMirror.getEnclosingClass());
+ }
+
+ @Override
+ JavaConstant readFieldValue(HotSpotResolvedObjectTypeImpl holder, HotSpotResolvedJavaField field, boolean isVolatile) {
+ Class<?> javaMirror = getMirror(holder);
+ return readFieldValue(field, javaMirror, isVolatile);
+ }
+
+ @Override
+ JavaConstant readFieldValue(HotSpotObjectConstantImpl object, HotSpotResolvedJavaField field, boolean isVolatile) {
+ Object value = resolveObject(object);
+ return readFieldValue(field, value, isVolatile);
+ }
+
+ @Override
+ boolean equals(HotSpotObjectConstantImpl a, HotSpotObjectConstantImpl b) {
+ return resolveObject(a) == resolveObject(b) && a.isCompressed() == b.isCompressed();
+ }
+
+ @Override
+ JavaConstant getJavaMirror(HotSpotResolvedPrimitiveType holder) {
+ return holder.mirror;
+ }
+
+ @Override
+ ResolvedJavaMethod.Parameter[] getParameters(HotSpotResolvedJavaMethodImpl javaMethod) {
+ java.lang.reflect.Parameter[] javaParameters = getMethod(javaMethod).getParameters();
+ ResolvedJavaMethod.Parameter[] res = new ResolvedJavaMethod.Parameter[javaParameters.length];
+ for (int i = 0; i < res.length; i++) {
+ java.lang.reflect.Parameter src = javaParameters[i];
+ String paramName = src.isNamePresent() ? src.getName() : null;
+ res[i] = new ResolvedJavaMethod.Parameter(paramName, src.getModifiers(), javaMethod, i);
+ }
+ return res;
+ }
+
+ @Override
+ Annotation[][] getParameterAnnotations(HotSpotResolvedJavaMethodImpl javaMethod) {
+ return getMethod(javaMethod).getParameterAnnotations();
+ }
+
+ @Override
+ Type[] getGenericParameterTypes(HotSpotResolvedJavaMethodImpl javaMethod) {
+ return getMethod(javaMethod).getGenericParameterTypes();
+ }
+
+ @Override
+ Annotation[] getFieldAnnotations(HotSpotResolvedJavaFieldImpl javaField) {
+ return getField(javaField).getAnnotations();
+ }
+
+ @Override
+ Annotation[] getMethodAnnotations(HotSpotResolvedJavaMethodImpl javaMethod) {
+ return getMethod(javaMethod).getAnnotations();
+ }
+
+ @Override
+ Annotation[] getMethodDeclaredAnnotations(HotSpotResolvedJavaMethodImpl javaMethod) {
+ return getMethod(javaMethod).getDeclaredAnnotations();
+ }
+
+ @Override
+ Annotation[] getFieldDeclaredAnnotations(HotSpotResolvedJavaFieldImpl javaField) {
+ return getField(javaField).getDeclaredAnnotations();
+ }
+
+ @Override
+ <T extends Annotation> T getMethodAnnotation(HotSpotResolvedJavaMethodImpl javaMethod, Class<T> annotationClass) {
+ return getMethod(javaMethod).getAnnotation(annotationClass);
+ }
+
+ @Override
+ <T extends Annotation> T getFieldAnnotation(HotSpotResolvedJavaFieldImpl javaField, Class<T> annotationClass) {
+ return getField(javaField).getAnnotation(annotationClass);
+ }
+
+ @Override
+ HotSpotResolvedObjectTypeImpl getType(HotSpotObjectConstantImpl object) {
+ Object value = resolveObject(object);
+ Class<?> theClass = value.getClass();
+ return (HotSpotResolvedObjectTypeImpl) runtime().fromClass(theClass);
+ }
+
+ @Override
+ String asString(HotSpotObjectConstantImpl object) {
+ Object value = resolveObject(object);
+ if (value instanceof String) {
+ return (String) value;
+ }
+ return null;
+ }
+
+ @Override
+ ResolvedJavaType asJavaType(HotSpotObjectConstantImpl object) {
+ Object value = resolveObject(object);
+ if (value instanceof Class) {
+ Class<?> javaClass = (Class<?>) value;
+ return runtime().fromClass(javaClass);
+ }
+ if (value instanceof ResolvedJavaType) {
+ return (ResolvedJavaType) value;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ <T> T asObject(HotSpotObjectConstantImpl object, Class<T> type) {
+ Object value = resolveObject(object);
+ if (type.isInstance(value)) {
+ return (T) value;
+ }
+ return null;
+ }
+
+ @Override
+ Object asObject(HotSpotObjectConstantImpl object, HotSpotResolvedJavaType type) {
+ Object value = resolveObject(object);
+ if (getMirror(type).isInstance(value)) {
+ return value;
+ }
+ return null;
+ }
+
+ @Override
+ String formatString(HotSpotObjectConstantImpl object) {
+ return JavaKind.Object.format(resolveObject(object));
+ }
+
+ @Override
+ Integer getLength(HotSpotObjectConstantImpl arrayObject) {
+ Object object = resolveObject(arrayObject);
+ if (object.getClass().isArray()) {
+ return Array.getLength(object);
+ }
+ return null;
+ }
+
+ @Override
+ JavaConstant readArrayElement(HotSpotObjectConstantImpl arrayObject, int index) {
+ Object a = resolveObject(arrayObject);
+ if (!a.getClass().isArray() || index < 0 || index >= Array.getLength(a)) {
+ return null;
+ }
+ if (a instanceof Object[]) {
+ Object element = ((Object[]) a)[index];
+ return forObject(element);
+ } else {
+ if (a instanceof int[]) {
+ return JavaConstant.forInt(((int[]) a)[index]);
+ } else if (a instanceof char[]) {
+ return JavaConstant.forChar(((char[]) a)[index]);
+ } else if (a instanceof byte[]) {
+ return JavaConstant.forByte(((byte[]) a)[index]);
+ } else if (a instanceof long[]) {
+ return JavaConstant.forLong(((long[]) a)[index]);
+ } else if (a instanceof short[]) {
+ return JavaConstant.forShort(((short[]) a)[index]);
+ } else if (a instanceof float[]) {
+ return JavaConstant.forFloat(((float[]) a)[index]);
+ } else if (a instanceof double[]) {
+ return JavaConstant.forDouble(((double[]) a)[index]);
+ } else if (a instanceof boolean[]) {
+ return JavaConstant.forBoolean(((boolean[]) a)[index]);
+ } else {
+ throw new JVMCIError("Should not reach here");
+ }
+ }
+ }
+
+ @Override
+ JavaConstant unboxPrimitive(HotSpotObjectConstantImpl source) {
+ return JavaConstant.forBoxedPrimitive(resolveObject(source));
+ }
+
+ @Override
+ JavaConstant forObject(Object value) {
+ if (value == null) {
+ return JavaConstant.NULL_POINTER;
+ }
+ return forNonNullObject(value);
+ }
+
+ private static HotSpotObjectConstantImpl forNonNullObject(Object value) {
+ return DirectHotSpotObjectConstantImpl.forNonNullObject(value, false);
+ }
+
+ @Override
+ JavaConstant boxPrimitive(JavaConstant source) {
+ return forNonNullObject(source.asBoxedPrimitive());
+ }
+
+ @Override
+ int getInt(HotSpotObjectConstantImpl object, long displacement) {
+ return UNSAFE.getInt((resolveObject(object)), displacement);
+ }
+
+ @Override
+ byte getByte(HotSpotObjectConstantImpl object, long displacement) {
+ return UNSAFE.getByte(resolveObject(object), displacement);
+ }
+
+ @Override
+ short getShort(HotSpotObjectConstantImpl object, long displacement) {
+ return UNSAFE.getShort(resolveObject(object), displacement);
+ }
+
+ @Override
+ long getLong(HotSpotObjectConstantImpl object, long displacement) {
+ return UNSAFE.getLong(resolveObject(object), displacement);
+ }
+
+ @Override
+ void checkRead(HotSpotObjectConstantImpl constant, JavaKind kind, long displacement, HotSpotResolvedObjectType type) {
+ checkRead(kind, displacement, type, resolveObject(constant));
+ }
+
+ /**
+ * Offset of injected {@code java.lang.Class::oop_size} field. No need to make {@code volatile}
+ * as initialization is idempotent.
+ */
+ private long oopSizeOffset;
+
+ private static int computeOopSizeOffset(HotSpotJVMCIRuntime runtime) {
+ MetaAccessProvider metaAccess = runtime.getHostJVMCIBackend().getMetaAccess();
+ ResolvedJavaType staticType = metaAccess.lookupJavaType(Class.class);
+ for (ResolvedJavaField f : staticType.getInstanceFields(false)) {
+ if (f.getName().equals("oop_size")) {
+ int offset = f.getOffset();
+ assert offset != 0 : "not expecting offset of java.lang.Class::oop_size to be 0";
+ return offset;
+ }
+ }
+ throw new JVMCIError("Could not find injected java.lang.Class::oop_size field");
+ }
+
+ long oopSizeOffset() {
+ if (oopSizeOffset == 0) {
+ oopSizeOffset = computeOopSizeOffset(runtime());
+ }
+ return oopSizeOffset;
+ }
+
+ private boolean checkRead(JavaKind kind, long displacement, HotSpotResolvedObjectType type, Object object) {
+ if (type.isArray()) {
+ ResolvedJavaType componentType = type.getComponentType();
+ JavaKind componentKind = componentType.getJavaKind();
+ final int headerSize = runtime().getArrayBaseOffset(componentKind);
+ int sizeOfElement = runtime().getArrayIndexScale(componentKind);
+ int length = Array.getLength(object);
+ long arrayEnd = headerSize + (sizeOfElement * length);
+ boolean aligned = ((displacement - headerSize) % sizeOfElement) == 0;
+ if (displacement < 0 || displacement > (arrayEnd - sizeOfElement) || (kind == JavaKind.Object && !aligned)) {
+ int index = (int) ((displacement - headerSize) / sizeOfElement);
+ throw new IllegalArgumentException("Unsafe array access: reading element of kind " + kind +
+ " at offset " + displacement + " (index ~ " + index + ") in " +
+ type.toJavaName() + " object of length " + length);
+ }
+ } else if (kind != JavaKind.Object) {
+ long size;
+ if (object instanceof Class) {
+ int wordSize = runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordSize;
+ size = UNSAFE.getInt(object, oopSizeOffset()) * wordSize;
+ } else {
+ size = Math.abs(type.instanceSize());
+ }
+ int bytesToRead = kind.getByteCount();
+ if (displacement + bytesToRead > size || displacement < 0) {
+ throw new IllegalArgumentException("Unsafe access: reading " + bytesToRead + " bytes at offset " + displacement + " in " +
+ type.toJavaName() + " object of size " + size);
+ }
+ } else {
+ ResolvedJavaField field = null;
+ if (object instanceof Class) {
+ // Read of a static field
+ HotSpotResolvedJavaType hotSpotResolvedJavaType = runtime().fromClass((Class<?>) object);
+ if (hotSpotResolvedJavaType instanceof HotSpotResolvedObjectTypeImpl) {
+ HotSpotResolvedObjectTypeImpl staticFieldsHolder = (HotSpotResolvedObjectTypeImpl) hotSpotResolvedJavaType;
+ field = staticFieldsHolder.findStaticFieldWithOffset(displacement, JavaKind.Object);
+ }
+ }
+ if (field == null) {
+ field = type.findInstanceFieldWithOffset(displacement, JavaKind.Object);
+ }
+ if (field == null) {
+ throw new IllegalArgumentException("Unsafe object access: field not found for read of kind Object" +
+ " at offset " + displacement + " in " + type.toJavaName() + " object");
+ }
+ if (field.getJavaKind() != JavaKind.Object) {
+ throw new IllegalArgumentException("Unsafe object access: field " + field.format("%H.%n:%T") + " not of expected kind Object" +
+ " at offset " + displacement + " in " + type.toJavaName() + " object");
+ }
+ }
+ return true;
+ }
+
+ JavaConstant readFieldValue(HotSpotResolvedJavaField field, Object obj, boolean isVolatile) {
+ assert obj != null;
+ assert !field.isStatic() || obj instanceof Class;
+ long displacement = field.getOffset();
+
+ assert checkRead(field.getJavaKind(), displacement,
+ (HotSpotResolvedObjectType) runtime().getHostJVMCIBackend().getMetaAccess().lookupJavaType(field.isStatic() ? (Class<?>) obj : obj.getClass()),
+ obj);
+ JavaKind kind = field.getJavaKind();
+ switch (kind) {
+ case Boolean:
+ return JavaConstant.forBoolean(isVolatile ? UNSAFE.getBooleanVolatile(obj, displacement) : UNSAFE.getBoolean(obj, displacement));
+ case Byte:
+ return JavaConstant.forByte(isVolatile ? UNSAFE.getByteVolatile(obj, displacement) : UNSAFE.getByte(obj, displacement));
+ case Char:
+ return JavaConstant.forChar(isVolatile ? UNSAFE.getCharVolatile(obj, displacement) : UNSAFE.getChar(obj, displacement));
+ case Short:
+ return JavaConstant.forShort(isVolatile ? UNSAFE.getShortVolatile(obj, displacement) : UNSAFE.getShort(obj, displacement));
+ case Int:
+ return JavaConstant.forInt(isVolatile ? UNSAFE.getIntVolatile(obj, displacement) : UNSAFE.getInt(obj, displacement));
+ case Long:
+ return JavaConstant.forLong(isVolatile ? UNSAFE.getLongVolatile(obj, displacement) : UNSAFE.getLong(obj, displacement));
+ case Float:
+ return JavaConstant.forFloat(isVolatile ? UNSAFE.getFloatVolatile(obj, displacement) : UNSAFE.getFloat(obj, displacement));
+ case Double:
+ return JavaConstant.forDouble(isVolatile ? UNSAFE.getDoubleVolatile(obj, displacement) : UNSAFE.getDouble(obj, displacement));
+ case Object:
+ return forObject(isVolatile ? UNSAFE.getReferenceVolatile(obj, displacement) : UNSAFE.getReference(obj, displacement));
+ default:
+ throw new IllegalArgumentException("Unsupported kind: " + kind);
+
+ }
+ }
+
+ /**
+ * Gets a {@link Method} object corresponding to {@code method}. This method guarantees the same
+ * {@link Method} object is returned if called twice on the same {@code method} value.
+ */
+ private static Executable getMethod(HotSpotResolvedJavaMethodImpl method) {
+ assert !method.isClassInitializer() : method;
+ if (method.toJavaCache == null) {
+ synchronized (method) {
+ if (method.toJavaCache == null) {
+ method.toJavaCache = compilerToVM().asReflectionExecutable(method);
+ }
+ }
+ }
+ return method.toJavaCache;
+ }
+
+ /**
+ * Gets a {@link Field} object corresponding to {@code field}. This method guarantees the same
+ * {@link Field} object is returned if called twice on the same {@code field} value. This is
+ * required to ensure the results of {@link HotSpotResolvedJavaFieldImpl#getAnnotations()} and
+ * {@link HotSpotResolvedJavaFieldImpl#getAnnotation(Class)} are stable (i.e., for a given field
+ * {@code f} and annotation class {@code a}, the same object is returned for each call to
+ * {@code f.getAnnotation(a)}).
+ */
+ private static Field getField(HotSpotResolvedJavaFieldImpl field) {
+ HotSpotResolvedObjectTypeImpl declaringClass = field.getDeclaringClass();
+ synchronized (declaringClass) {
+ HashMap<HotSpotResolvedJavaFieldImpl, Field> cache = declaringClass.reflectionFieldCache;
+ if (cache == null) {
+ cache = new HashMap<>();
+ declaringClass.reflectionFieldCache = cache;
+ }
+ Field reflect = cache.get(field);
+ if (reflect == null) {
+ reflect = compilerToVM().asReflectionField(field.getDeclaringClass(), field.getIndex());
+ cache.put(field, reflect);
+ }
+ return reflect;
+ }
+ }
+
+ Class<?> getMirror(HotSpotResolvedObjectTypeImpl holder) {
+ return (Class<?>) resolveObject((HotSpotObjectConstantImpl) holder.getJavaMirror());
+ }
+
+ Class<?> getMirror(HotSpotResolvedJavaType type) {
+ assert type != null;
+ if (type instanceof HotSpotResolvedPrimitiveType) {
+ return (Class<?>) resolveObject(((HotSpotResolvedPrimitiveType) type).mirror);
+ } else {
+ return getMirror((HotSpotResolvedObjectTypeImpl) type);
+ }
+ }
+}
+