package com.oracle.svm.reflect.hosted;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicInteger;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.ImageClassLoader;
import com.oracle.svm.hosted.annotation.CustomSubstitution;
import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport;
import com.oracle.svm.reflect.helpers.InvokeSpecialReflectionProxy;
import com.oracle.svm.reflect.helpers.ReflectionProxy;
import com.oracle.svm.reflect.hosted.ReflectionSubstitutionType.ReflectionSubstitutionMethod;
import com.oracle.svm.util.ReflectionUtil;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
final class ReflectionSubstitution extends CustomSubstitution<ReflectionSubstitutionType> {
private static final String PROXY_NAME_SEPARATOR = "_";
private final ClassInitializationSupport classInitializationSupport;
private static final int ACC_PUBLIC = 0x00000001;
private static final int ACC_FINAL = 0x00000010;
private static final int ACC_SUPER = 0x00000020;
private final Method defineClass;
private final Method resolveClass;
private final ResolvedJavaType reflectionProxy;
private final ResolvedJavaType javaLangReflectProxy;
private final HashMap<Member, Class<?>> proxyMap = new HashMap<>();
private final HashMap<ResolvedJavaType, Member> typeToMember = new HashMap<>();
private static final AtomicInteger proxyNr = new AtomicInteger(0);
private final ImageClassLoader imageClassLoader;
ReflectionSubstitution(MetaAccessProvider metaAccess, ClassInitializationSupport initializationSupport, ImageClassLoader classLoader) {
super(metaAccess);
defineClass = ReflectionUtil.lookupMethod(ClassLoader.class, "defineClass", String.class, byte[].class, int.class, int.class);
resolveClass = ReflectionUtil.lookupMethod(ClassLoader.class, "resolveClass", Class.class);
reflectionProxy = metaAccess.lookupJavaType(ReflectionProxy.class);
javaLangReflectProxy = metaAccess.lookupJavaType(java.lang.reflect.Proxy.class);
classInitializationSupport = initializationSupport;
imageClassLoader = classLoader;
}
static String getStableProxyName(Member member) {
return "com.oracle.svm.reflect." + SubstrateUtil.uniqueShortName(member);
}
private static Class<?> getAccessorInterface(Member member) {
if (member instanceof Field) {
return packageJdkInternalReflectClassForName("FieldAccessor");
} else if (member instanceof Method) {
return packageJdkInternalReflectClassForName("MethodAccessor");
} else if (member instanceof Constructor) {
return packageJdkInternalReflectClassForName("ConstructorAccessor");
}
throw VMError.shouldNotReachHere();
}
private static Class<?> packageJdkInternalReflectClassForName(String className) {
final String packageName = (JavaVersionUtil.JAVA_SPEC <= 8 ? "sun.reflect." : "jdk.internal.reflect.");
try {
return Class.forName(packageName + className);
} catch (ClassNotFoundException cnfe) {
throw VMError.shouldNotReachHere(cnfe);
}
}
private static Method generateProxyMethod;
private static byte[] generateProxyClass(final String name, Class<?>[] interfaces) {
try {
if (generateProxyMethod == null) {
final String packageName = (JavaVersionUtil.JAVA_SPEC <= 8 ? "sun.misc." : "java.lang.reflect.");
generateProxyMethod = ReflectionUtil.lookupMethod(Class.forName(packageName + "ProxyGenerator"), "generateProxyClass", String.class, Class[].class);
}
return (byte[]) generateProxyMethod.invoke(null, name, interfaces);
} catch (ReflectiveOperationException ex) {
throw VMError.shouldNotReachHere(ex);
}
}
private static byte[] generateProxyClass14(final String name, Class<?>[] interfaces, ClassLoader loader) {
try {
if (generateProxyMethod == null) {
final String packageName = (JavaVersionUtil.JAVA_SPEC <= 8 ? "sun.misc." : "java.lang.reflect.");
generateProxyMethod = ReflectionUtil.lookupMethod(Class.forName(packageName + "ProxyGenerator"), "generateProxyClass", ClassLoader.class, String.class, List.class, int.class);
}
List<Class<?>> ilist = new ArrayList<>(Arrays.asList(interfaces));
return (byte[]) generateProxyMethod.invoke(null, loader, name, ilist, (ACC_PUBLIC | ACC_FINAL | ACC_SUPER));
} catch (ReflectiveOperationException ex) {
throw VMError.shouldNotReachHere(ex);
}
}
synchronized Class<?> getProxyClass(Member member) {
Class<?> ret = proxyMap.get(member);
if (ret == null) {
ClassLoader loader = imageClassLoader.getClassLoader();
String name = getStableProxyName(member) + PROXY_NAME_SEPARATOR + proxyNr.incrementAndGet();
Class<?>[] ifaces;
if (member instanceof Method && !Modifier.isStatic(member.getModifiers()) && !Modifier.isAbstract(member.getModifiers())) {
ifaces = new Class<?>[]{getAccessorInterface(member), ReflectionProxy.class, InvokeSpecialReflectionProxy.class};
} else {
ifaces = new Class<?>[]{getAccessorInterface(member), ReflectionProxy.class};
}
byte[] proxyBC;
if (JavaVersionUtil.JAVA_SPEC < 14) {
proxyBC = generateProxyClass(name, ifaces);
} else {
proxyBC = generateProxyClass14(name, ifaces, loader);
}
try {
ret = (Class<?>) defineClass.invoke(loader, name, proxyBC, 0, proxyBC.length);
resolveClass.invoke(loader, ret);
proxyMap.put(member, ret);
ResolvedJavaType type = metaAccess.lookupJavaType(ret);
typeToMember.put(type, member);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
throw VMError.shouldNotReachHere(ex);
}
}
classInitializationSupport.forceInitializeHosted(ret, "all proxy classes are initialized", false);
return ret;
}
private boolean isReflectionProxy(ResolvedJavaType type) {
return reflectionProxy.isAssignableFrom(type) && javaLangReflectProxy.isAssignableFrom(type);
}
@Override
public ResolvedJavaType lookup(ResolvedJavaType type) {
if (isReflectionProxy(type)) {
return getSubstitution(type);
} else {
return type;
}
}
@Override
public ResolvedJavaMethod lookup(ResolvedJavaMethod method) {
if (isReflectionProxy(method.getDeclaringClass())) {
ReflectionSubstitutionType declaringClass = getSubstitution(method.getDeclaringClass());
ReflectionSubstitutionMethod result = declaringClass.getSubstitutionMethod(method);
if (result != null) {
return result;
}
}
return method;
}
@Override
public ResolvedJavaType resolve(ResolvedJavaType type) {
if (type instanceof ReflectionSubstitutionType) {
return ((ReflectionSubstitutionType) type).getOriginal();
} else {
return type;
}
}
@Override
public ResolvedJavaMethod resolve(ResolvedJavaMethod method) {
if (method instanceof ReflectionSubstitutionMethod) {
return ((ReflectionSubstitutionMethod) method).getOriginal();
} else {
return method;
}
}
private synchronized ReflectionSubstitutionType getSubstitution(ResolvedJavaType original) {
ReflectionSubstitutionType subst = getSubstitutionType(original);
if (subst == null) {
Member member = typeToMember.get(original);
subst = new ReflectionSubstitutionType(original, member);
addSubstitutionType(original, subst);
}
return subst;
}
}