diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIJavaCallTrampolines.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIJavaCallTrampolines.java index 41e2875e29be0..58a571ac8a580 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIJavaCallTrampolines.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIJavaCallTrampolines.java @@ -41,6 +41,9 @@ public static ConstantPool getConstantPool(MetaAccessProvider metaAccess) { return metaAccess.lookupJavaType(JNIJavaCallWrappers.class).getDeclaredConstructors()[0].getConstantPool(); } + private JNIJavaCallTrampolines() { + } + public static String getTrampolineName(CallVariant variant, boolean nonVirtual) { StringBuilder name = new StringBuilder(48); if (variant == CallVariant.VARARGS) { @@ -59,7 +62,21 @@ public static String getTrampolineName(CallVariant variant, boolean nonVirtual) return name.toString(); } - private JNIJavaCallTrampolines() { + public static boolean isNonVirtual(String trampolineName) { + return trampolineName.endsWith("NonvirtualJavaCallTrampoline"); + } + + public static CallVariant getVariant(String trampolineName) { + if (trampolineName.startsWith("varargs")) { + return CallVariant.VARARGS; + } + if (trampolineName.startsWith("array")) { + return CallVariant.ARRAY; + } + if (trampolineName.startsWith("valist")) { + return CallVariant.VA_LIST; + } + throw VMError.shouldNotReachHere(); } private native void varargsJavaCallTrampoline(); diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessFeature.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessFeature.java index 4ec513213fefc..7061d0bad3719 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessFeature.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/access/JNIAccessFeature.java @@ -38,6 +38,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; +import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; +import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.jni.JNIJavaCallTrampolines; import com.oracle.svm.util.ReflectionUtil; import org.graalvm.compiler.api.replacements.Fold; @@ -183,16 +185,17 @@ private static ConditionalConfigurationRegistry getConditionalConfigurationRegis } private void createJavaCallTrampoline(BeforeAnalysisAccessImpl access, CallVariant variant, boolean nonVirtual) { - MetaAccessProvider wrappedMetaAccess = access.getMetaAccess().getWrapped(); - ResolvedJavaField field = JNIAccessibleMethod.getCallWrapperField(wrappedMetaAccess, variant, nonVirtual); + AnalysisMetaAccess metaAccess = access.getMetaAccess(); + ResolvedJavaField field = JNIAccessibleMethod.getCallWrapperField(metaAccess.getWrapped(), variant, nonVirtual); access.getUniverse().lookup(field.getDeclaringClass()).registerAsReachable(); access.registerAsAccessed(access.getUniverse().lookup(field)); String trampolineName = JNIJavaCallTrampolines.getTrampolineName(variant, nonVirtual); Method reflectionMethod = ReflectionUtil.lookupMethod(JNIJavaCallTrampolines.class, trampolineName); - ResolvedJavaMethod method = wrappedMetaAccess.lookupJavaMethod(reflectionMethod); - JNICallTrampolineMethod trampoline = new JNICallTrampolineMethod(method, field, nonVirtual); + // Look up the java method to ensure a JNICallTrampolineMethod gets created for it through + // com.oracle.svm.jni.hosted.JNINativeCallWrapperSubstitutionProcessor.lookup + metaAccess.lookupJavaMethod(reflectionMethod); + JNICallTrampolineMethod trampoline = getCallTrampolineMethod(trampolineName); access.registerAsCompiled(access.getUniverse().lookup(trampoline)); - trampolineMethods.put(trampolineName, trampoline); } public JNICallTrampolineMethod getCallTrampolineMethod(CallVariant variant, boolean nonVirtual) { @@ -206,6 +209,25 @@ public JNICallTrampolineMethod getCallTrampolineMethod(String javaTrampolineName return jniCallTrampolineMethod; } + public JNICallTrampolineMethod getOrCreateCallTrampolineMethod(DuringSetupAccessImpl access, String trampolineName) { + JNICallTrampolineMethod jniCallTrampolineMethod = trampolineMethods.get(trampolineName); + + if (jniCallTrampolineMethod != null) { + return jniCallTrampolineMethod; + } + + MetaAccessProvider wrappedMetaAccess = access.getMetaAccess().getWrapped(); + Method reflectionMethod = ReflectionUtil.lookupMethod(JNIJavaCallTrampolines.class, trampolineName); + boolean nonVirtual = JNIJavaCallTrampolines.isNonVirtual(trampolineName); + ResolvedJavaField field = JNIAccessibleMethod.getCallWrapperField(wrappedMetaAccess, JNIJavaCallTrampolines.getVariant(trampolineName), nonVirtual); + // Use wrapped MetaAccess to avoid infinite recursion through + // com.oracle.svm.jni.hosted.JNINativeCallWrapperSubstitutionProcessor.lookup + ResolvedJavaMethod method = wrappedMetaAccess.lookupJavaMethod(reflectionMethod); + JNICallTrampolineMethod trampoline = new JNICallTrampolineMethod(method, field, nonVirtual); + trampolineMethods.put(trampolineName, trampoline); + return trampoline; + } + public JNINativeLinkage makeLinkage(String declaringClass, String name, String descriptor) { UserError.guarantee(!sealed, "All linkages for JNI calls must be created before the analysis has completed.%nOffending class: %s name: %s descriptor: %s", diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNICallWrapperFeature.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNICallWrapperFeature.java index 4f2eec53caef7..d64f1ed0c0a23 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNICallWrapperFeature.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNICallWrapperFeature.java @@ -74,6 +74,6 @@ public List> getRequiredFeatures() { @Override public void duringSetup(DuringSetupAccess access) { DuringSetupAccessImpl config = (DuringSetupAccessImpl) access; - config.registerNativeSubstitutionProcessor(new JNINativeCallWrapperSubstitutionProcessor()); + config.registerNativeSubstitutionProcessor(new JNINativeCallWrapperSubstitutionProcessor(config)); } } diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperSubstitutionProcessor.java index e968aafb7863d..85bc82938794a 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperSubstitutionProcessor.java @@ -29,6 +29,7 @@ import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; +import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.jni.JNIJavaCallTrampolines; import com.oracle.svm.jni.access.JNIAccessFeature; import jdk.vm.ci.meta.MetaUtil; @@ -40,6 +41,12 @@ */ class JNINativeCallWrapperSubstitutionProcessor extends SubstitutionProcessor { private final Map callWrappers = new ConcurrentHashMap<>(); + private final DuringSetupAccessImpl access; + + public JNINativeCallWrapperSubstitutionProcessor(DuringSetupAccessImpl access) { + super(); + this.access = access; + } @Override public ResolvedJavaMethod lookup(ResolvedJavaMethod method) { @@ -47,10 +54,7 @@ public ResolvedJavaMethod lookup(ResolvedJavaMethod method) { String jniJavaCallWrappersInternalName = MetaUtil.toInternalName(JNIJavaCallTrampolines.class.getTypeName()); if (method.getDeclaringClass().getName().equals(jniJavaCallWrappersInternalName)) { // Avoid generating JNINativeCallWrapperMethods for trampolines - JNICallTrampolineMethod callTrampolineMethod = JNIAccessFeature.singleton().getCallTrampolineMethod(method.getName()); - if (callTrampolineMethod != null) { - return callTrampolineMethod; - } + return JNIAccessFeature.singleton().getOrCreateCallTrampolineMethod(access, method.getName()); } return callWrappers.computeIfAbsent(method, JNINativeCallWrapperMethod::new); }