package com.oracle.svm.methodhandles;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.function.BooleanSupplier;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.hosted.NativeImageOptions;
import com.oracle.svm.util.ReflectionUtil;
import sun.invoke.util.ValueConversions;
import sun.invoke.util.Wrapper;
@AutomaticFeature
@SuppressWarnings("unused")
public class MethodHandleFeature implements Feature {
@Override
public boolean isInConfiguration(IsInConfigurationAccess access) {
return NativeImageOptions.areMethodHandlesSupported();
}
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
Class<?> mhImplClazz = access.findClassByName("java.lang.invoke.MethodHandleImpl");
access.registerReachabilityHandler(MethodHandleFeature::registerMHImplFunctionsForReflection,
ReflectionUtil.lookupMethod(mhImplClazz, "createFunction", byte.class));
access.registerReachabilityHandler(MethodHandleFeature::registerMHImplConstantHandlesForReflection,
ReflectionUtil.lookupMethod(mhImplClazz, "makeConstantHandle", int.class));
access.registerReachabilityHandler(MethodHandleFeature::registerMHImplCountingWrapperFunctionsForReflection,
access.findClassByName("java.lang.invoke.MethodHandleImpl$CountingWrapper"));
access.registerReachabilityHandler(MethodHandleFeature::registerInvokersFunctionsForReflection,
ReflectionUtil.lookupMethod(access.findClassByName("java.lang.invoke.Invokers"), "createFunction", byte.class));
access.registerReachabilityHandler(MethodHandleFeature::registerValueConversionBoxFunctionsForReflection,
ReflectionUtil.lookupMethod(ValueConversions.class, "boxExact", Wrapper.class));
access.registerReachabilityHandler(MethodHandleFeature::registerValueConversionUnboxFunctionsForReflection,
ReflectionUtil.lookupMethod(ValueConversions.class, "unbox", Wrapper.class, int.class));
access.registerReachabilityHandler(MethodHandleFeature::registerValueConversionConvertFunctionsForReflection,
ReflectionUtil.lookupMethod(ValueConversions.class, "convertPrimitive", Wrapper.class, Wrapper.class));
access.registerReachabilityHandler(MethodHandleFeature::registerValueConversionIgnoreForReflection,
ReflectionUtil.lookupMethod(ValueConversions.class, "ignore"));
access.registerClassInitializerReachabilityHandler(MethodHandleFeature::registerDelegatingMHFunctionsForReflection,
access.findClassByName("java.lang.invoke.DelegatingMethodHandle"));
access.registerReachabilityHandler(MethodHandleFeature::registerCallSiteGetTargetForReflection,
ReflectionUtil.lookupMethod(CallSite.class, "getTargetHandle"));
access.registerReachabilityHandler(MethodHandleFeature::registerUninitializedCallSiteForReflection,
ReflectionUtil.lookupMethod(CallSite.class, "uninitializedCallSiteHandle"));
}
private static void registerMHImplFunctionsForReflection(DuringAnalysisAccess access) {
Class<?> mhImplClazz = access.findClassByName("java.lang.invoke.MethodHandleImpl");
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "checkSpreadArgument", Object.class, int.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "guardWithCatch", MethodHandle.class, Class.class, MethodHandle.class, Object[].class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "tryFinally", MethodHandle.class, MethodHandle.class, Object[].class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "loop", access.findClassByName("[Ljava.lang.invoke.LambdaForm$BasicType;"),
access.findClassByName("java.lang.invoke.MethodHandleImpl$LoopClauses"), Object[].class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "throwException", Throwable.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "profileBoolean", boolean.class, int[].class));
}
private static void registerMHImplConstantHandlesForReflection(DuringAnalysisAccess access) {
Class<?> mhImplClazz = access.findClassByName("java.lang.invoke.MethodHandleImpl");
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "copyAsPrimitiveArray", access.findClassByName("sun.invoke.util.Wrapper"), Object[].class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "identity", Object[].class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "fillNewArray", Integer.class, Object[].class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "fillNewTypedArray", Object[].class, Integer.class, Object[].class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "selectAlternative", boolean.class, MethodHandle.class, MethodHandle.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "countedLoopPredicate", int.class, int.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "countedLoopStep", int.class, int.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "initIterator", Iterable.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "iteratePredicate", Iterator.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(mhImplClazz, "iterateNext", Iterator.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(Array.class, "newInstance", Class.class, int.class));
}
private static void registerMHImplCountingWrapperFunctionsForReflection(DuringAnalysisAccess access) {
RuntimeReflection.register(ReflectionUtil.lookupMethod(access.findClassByName("java.lang.invoke.MethodHandleImpl$CountingWrapper"), "maybeStopCounting", Object.class));
}
private static void registerInvokersFunctionsForReflection(DuringAnalysisAccess access) {
Class<?> invokersClazz = access.findClassByName("java.lang.invoke.Invokers");
RuntimeReflection.register(ReflectionUtil.lookupMethod(invokersClazz, "checkExactType", MethodHandle.class, MethodType.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(invokersClazz, "checkGenericType", MethodHandle.class, MethodType.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(invokersClazz, "getCallSiteTarget", CallSite.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(invokersClazz, "checkCustomized", MethodHandle.class));
RuntimeReflection.register(ReflectionUtil.lookupMethod(invokersClazz, "checkVarHandleGenericType", access.findClassByName("java.lang.invoke.VarHandle"),
access.findClassByName("java.lang.invoke.VarHandle$AccessDescriptor")));
RuntimeReflection.register(ReflectionUtil.lookupMethod(invokersClazz, "checkVarHandleExactType", access.findClassByName("java.lang.invoke.VarHandle"),
access.findClassByName("java.lang.invoke.VarHandle$AccessDescriptor")));
}
private static void registerValueConversionBoxFunctionsForReflection(DuringAnalysisAccess access) {
for (Wrapper type : Wrapper.values()) {
if (type.primitiveType().isPrimitive() && type != Wrapper.VOID) {
RuntimeReflection.register(ReflectionUtil.lookupMethod(ValueConversions.class, "box" + type.wrapperSimpleName(), type.primitiveType()));
}
}
}
private static void registerValueConversionUnboxFunctionsForReflection(DuringAnalysisAccess access) {
for (Wrapper type : Wrapper.values()) {
if (type.primitiveType().isPrimitive() && type != Wrapper.VOID) {
RuntimeReflection.register(ReflectionUtil.lookupMethod(ValueConversions.class, "unbox" + type.wrapperSimpleName(), type.wrapperType()));
RuntimeReflection.register(ReflectionUtil.lookupMethod(ValueConversions.class, "unbox" + type.wrapperSimpleName(), Object.class, boolean.class));
}
}
}
private static void registerValueConversionConvertFunctionsForReflection(DuringAnalysisAccess access) {
for (Wrapper src : Wrapper.values()) {
for (Wrapper dest : Wrapper.values()) {
if (src != dest && src.primitiveType().isPrimitive() && src != Wrapper.VOID && dest.primitiveType().isPrimitive() && dest != Wrapper.VOID) {
RuntimeReflection.register(ReflectionUtil.lookupMethod(ValueConversions.class, valueConverterName(src, dest), src.primitiveType()));
}
}
}
}
private static String valueConverterName(Wrapper src, Wrapper dest) {
String srcType = src.primitiveSimpleName();
String destType = dest.primitiveSimpleName();
return srcType + "To" + destType.substring(0, 1).toUpperCase() + destType.substring(1);
}
private static void registerValueConversionIgnoreForReflection(DuringAnalysisAccess access) {
RuntimeReflection.register(ReflectionUtil.lookupMethod(ValueConversions.class, "ignore", Object.class));
}
private static void registerDelegatingMHFunctionsForReflection(DuringAnalysisAccess access) {
Class<?> delegatingMHClazz = access.findClassByName("java.lang.invoke.DelegatingMethodHandle");
RuntimeReflection.register(ReflectionUtil.lookupMethod(delegatingMHClazz, "getTarget"));
}
private static void registerCallSiteGetTargetForReflection(DuringAnalysisAccess access) {
RuntimeReflection.register(ReflectionUtil.lookupMethod(CallSite.class, "getTarget"));
}
private static void registerUninitializedCallSiteForReflection(DuringAnalysisAccess access) {
RuntimeReflection.register(ReflectionUtil.lookupMethod(CallSite.class, "uninitializedCallSite", Object[].class));
}
}
class MethodHandlesSupported implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
return NativeImageOptions.areMethodHandlesSupported();
}
}
class MethodHandlesNotSupported implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
return !NativeImageOptions.areMethodHandlesSupported();
}
}