diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2584b75b7736..25035d790e7b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -79,12 +79,10 @@ jobs: JDK: "labsjdk-ce-17" GATE: "build,debuginfotest" PRIMARY: "substratevm" - GHA_EXPECTED_FAILURE: true # temporarily marked as expected failure due to #4018 / GR-35118 - env: JDK: "labsjdk-ce-11" GATE: "build,debuginfotest" PRIMARY: "substratevm" - GHA_EXPECTED_FAILURE: true # temporarily marked as expected failure due to #4018 / GR-35118 - env: JDK: "labsjdk-ce-11" GATE: "hellomodule" diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index f27c39e0027d..90ec19a4db14 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -574,78 +574,78 @@ def test(): checker.check(exec_string, skip_fails=False) exec_string = execute("info break 6") - rexp = [r"6.1%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:140"%(spaces_pattern, spaces_pattern, address_pattern), - r"6.2%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:177"%(spaces_pattern, spaces_pattern, address_pattern), - r"6.3%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:160"%(spaces_pattern, spaces_pattern, address_pattern), - r"6.4%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:177"%(spaces_pattern, spaces_pattern, address_pattern)] + rexp = [r"6.1%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:141"%(spaces_pattern, spaces_pattern, address_pattern), + r"6.2%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:179"%(spaces_pattern, spaces_pattern, address_pattern), + r"6.3%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:162"%(spaces_pattern, spaces_pattern, address_pattern), + r"6.4%sy%s%s in hello\.Hello::inlineFrom at hello/Hello\.java:179"%(spaces_pattern, spaces_pattern, address_pattern)] checker = Checker('info break inlineFrom', rexp) checker.check(exec_string) execute("delete breakpoints") - exec_string = execute("break Hello.java:155") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 155\."%(digits_pattern, address_pattern) - checker = Checker('break Hello.java:155', rexp) + exec_string = execute("break Hello.java:157") + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 157\."%(digits_pattern, address_pattern) + checker = Checker('break Hello.java:157', rexp) checker.check(exec_string) execute("continue 5") exec_string = execute("backtrace 14") - rexp = [r"#0%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:155"%(spaces_pattern), - r"#1%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(spaces_pattern), - r"#2%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:153"%(spaces_pattern, address_pattern), - r"#3%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(spaces_pattern), - r"#4%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:153"%(spaces_pattern, address_pattern), - r"#5%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(spaces_pattern), - r"#6%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:153"%(spaces_pattern, address_pattern), - r"#7%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(spaces_pattern), - r"#8%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:153"%(spaces_pattern, address_pattern), - r"#9%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(spaces_pattern), - r"#10%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:153"%(spaces_pattern, address_pattern), - r"#11%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:147"%(spaces_pattern), - r"#12%s%s in hello\.Hello::inlineFrom \(\) at hello/Hello\.java:140"%(spaces_pattern, address_pattern), + rexp = [r"#0%shello\.Hello::inlineMixTo \(\) at hello/Hello\.java:157"%(spaces_pattern), + r"#1%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), + r"#2%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:155"%(spaces_pattern, address_pattern), + r"#3%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), + r"#4%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:155"%(spaces_pattern, address_pattern), + r"#5%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), + r"#6%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:155"%(spaces_pattern, address_pattern), + r"#7%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), + r"#8%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:155"%(spaces_pattern, address_pattern), + r"#9%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), + r"#10%s%s in hello\.Hello::inlineMixTo \(\) at hello/Hello\.java:155"%(spaces_pattern, address_pattern), + r"#11%shello\.Hello::noInlineHere\(int\) \(\) at hello/Hello\.java:149"%(spaces_pattern), + r"#12%s%s in hello\.Hello::inlineFrom \(\) at hello/Hello\.java:141"%(spaces_pattern, address_pattern), r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:94"%(spaces_pattern)] checker = Checker('backtrace in recursive inlineMixTo', rexp) checker.check(exec_string, skip_fails=False) execute("delete breakpoints") - exec_string = execute("break Hello.java:168") - rexp = r"Breakpoint %s at %s: Hello\.java:168\. \(2 locations\)"%(digits_pattern, address_pattern) - checker = Checker('break Hello.java:168', rexp) + exec_string = execute("break Hello.java:170") + rexp = r"Breakpoint %s at %s: Hello\.java:170\. \(2 locations\)"%(digits_pattern, address_pattern) + checker = Checker('break Hello.java:170', rexp) checker.check(exec_string) - execute("continue 5") + execute("continue") exec_string = execute("backtrace 14") - rexp = [r"#0%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:168"%(spaces_pattern), - r"#1%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:160"%(spaces_pattern, address_pattern), - r"#2%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:166"%(spaces_pattern), - r"#3%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:160"%(spaces_pattern, address_pattern), - r"#4%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:166"%(spaces_pattern), - r"#5%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:160"%(spaces_pattern, address_pattern), - r"#6%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:166"%(spaces_pattern), - r"#7%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:160"%(spaces_pattern, address_pattern), - r"#8%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:166"%(spaces_pattern), - r"#9%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:160"%(spaces_pattern, address_pattern), - r"#10%shello\.Hello::inlineTo \(\) at hello/Hello\.java:166"%(spaces_pattern), - r"#11%shello\.Hello::inlineHere \(\) at hello/Hello\.java:160"%(spaces_pattern), - r"#12%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:141"%(spaces_pattern), + rexp = [r"#0%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:170"%(spaces_pattern), + r"#1%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern, address_pattern), + r"#2%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:168"%(spaces_pattern), + r"#3%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern, address_pattern), + r"#4%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:168"%(spaces_pattern), + r"#5%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern, address_pattern), + r"#6%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:168"%(spaces_pattern), + r"#7%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern, address_pattern), + r"#8%shello\.Hello::inlineTo\(int\) \(\) at hello/Hello\.java:168"%(spaces_pattern), + r"#9%s%s in hello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern, address_pattern), + r"#10%shello\.Hello::inlineTo \(\) at hello/Hello\.java:168"%(spaces_pattern), + r"#11%shello\.Hello::inlineHere \(\) at hello/Hello\.java:162"%(spaces_pattern), + r"#12%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:143"%(spaces_pattern), r"#13%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:94"%(spaces_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) execute("delete breakpoints") - exec_string = execute("break Hello.java:174") - rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 174\."%(digits_pattern, address_pattern) - checker = Checker('break Hello.java:174', rexp) + exec_string = execute("break Hello.java:176") + rexp = r"Breakpoint %s at %s: file hello/Hello\.java, line 176\."%(digits_pattern, address_pattern) + checker = Checker('break Hello.java:176', rexp) checker.check(exec_string) execute("continue 5") exec_string = execute("backtrace 8") - rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:174"%(spaces_pattern), - r"#1%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:177"%(spaces_pattern, address_pattern), - r"#2%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:177"%(spaces_pattern, address_pattern), - r"#3%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:177"%(spaces_pattern, address_pattern), - r"#4%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:177"%(spaces_pattern, address_pattern), - r"#5%s%s in hello\.Hello::inlineTailRecursion \(\) at hello/Hello\.java:177"%(spaces_pattern, address_pattern), - r"#6%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:142"%(spaces_pattern), + rexp = [r"#0%shello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:176"%(spaces_pattern), + r"#1%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:179"%(spaces_pattern, address_pattern), + r"#2%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:179"%(spaces_pattern, address_pattern), + r"#3%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:179"%(spaces_pattern, address_pattern), + r"#4%s%s in hello\.Hello::inlineTailRecursion\(int\) \(\) at hello/Hello\.java:179"%(spaces_pattern, address_pattern), + r"#5%s%s in hello\.Hello::inlineTailRecursion \(\) at hello/Hello\.java:179"%(spaces_pattern, address_pattern), + r"#6%shello\.Hello::inlineFrom \(\) at hello/Hello\.java:144"%(spaces_pattern), r"#7%shello\.Hello::main\(java\.lang\.String\[\] \*\) \(\) at hello/Hello\.java:94"%(spaces_pattern)] checker = Checker('backtrace in recursive inlineTo', rexp) checker.check(exec_string, skip_fails=False) diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java index 643ad61ec936..fc21a324bf03 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/ClassEntry.java @@ -292,14 +292,18 @@ protected MethodEntry processMethod(DebugMethodInfo debugMethodInfo, DebugInfoBa debugContext.log("typename %s adding %s method %s %s(%s)\n", typeName, memberModifiers(modifiers), resultTypeName, methodName, formatParams(paramTypes, paramNames)); TypeEntry resultType = debugInfoBase.lookupTypeEntry(resultTypeName); - TypeEntry[] paramTypeArray = new TypeEntry[paramCount]; - String[] paramNameArray = new String[paramCount]; - int idx = 0; - for (String paramTypeName : paramTypes) { - TypeEntry paramType = debugInfoBase.lookupTypeEntry(TypeEntry.canonicalize(paramTypeName)); - paramTypeArray[idx++] = paramType; + TypeEntry[] paramTypeArray = null; + String[] paramNameArray = null; + if (paramCount != 0) { + paramTypeArray = new TypeEntry[paramCount]; + paramNameArray = new String[paramCount]; + int idx = 0; + for (String paramTypeName : paramTypes) { + TypeEntry paramType = debugInfoBase.lookupTypeEntry(TypeEntry.canonicalize(paramTypeName)); + paramTypeArray[idx++] = paramType; + } + paramNameArray = paramNames.toArray(paramNameArray); } - paramNameArray = paramNames.toArray(paramNameArray); /* * n.b. the method file may differ from the owning class file when the method is a * substitution diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java index a5ab87a66717..3f1172b612d0 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/MethodEntry.java @@ -31,8 +31,8 @@ import com.oracle.objectfile.debuginfo.DebugInfoProvider.DebugMethodInfo; public class MethodEntry extends MemberEntry { - final TypeEntry[] paramTypes; - final String[] paramNames; + private final TypeEntry[] paramTypes; + private final String[] paramNames; static final int DEOPT = 1 << 0; static final int IN_RANGE = 1 << 1; static final int INLINED = 1 << 2; diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java index c3f7e5ca594e..71d3674ae61c 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/debugentry/Range.java @@ -182,11 +182,14 @@ private String getExtendedMethodName(boolean includeClass, boolean includeParams builder.append(getMethodName()); if (includeParams) { builder.append("("); - String prefix = ""; - for (TypeEntry t : methodEntry.paramTypes) { - builder.append(prefix); - builder.append(t.getTypeName()); - prefix = ", "; + TypeEntry[] paramTypes = methodEntry.getParamTypes(); + if (paramTypes != null) { + String prefix = ""; + for (TypeEntry t : paramTypes) { + builder.append(prefix); + builder.append(t.getTypeName()); + prefix = ", "; + } } builder.append(')'); } diff --git a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java index 9a93f68fd374..b8341cf197de 100644 --- a/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java +++ b/substratevm/src/com.oracle.objectfile/src/com/oracle/objectfile/elf/dwarf/DwarfInfoSectionImpl.java @@ -737,6 +737,9 @@ private int writeMethodParameterDeclarations(DebugContext context, ClassEntry cl if (!Modifier.isStatic(method.getModifiers())) { pos = writeMethodParameterDeclaration(context, "this", classEntry.getTypeName(), true, isSpecification, buffer, pos); } + if (method.getParamTypes() == null) { + return pos; + } for (TypeEntry paramType : method.getParamTypes()) { String paramTypeName = paramType.getTypeName(); String paramName = uniqueDebugString(""); 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 new file mode 100644 index 000000000000..da0e60266f81 --- /dev/null +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIJavaCallTrampolines.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017, 2021, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 com.oracle.svm.jni; + +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.jni.hosted.JNIJavaCallWrapperMethod; +import com.oracle.svm.jni.hosted.JNIJavaCallWrapperMethod.CallVariant; + +/** + * Holder class for generated {@link JNIJavaCallWrapperMethod} code. + */ +public final class JNIJavaCallTrampolines { + + private JNIJavaCallTrampolines() { + } + + public static String getTrampolineName(CallVariant variant, boolean nonVirtual) { + StringBuilder name = new StringBuilder(48); + if (variant == CallVariant.VARARGS) { + name.append("varargs"); + } else if (variant == CallVariant.ARRAY) { + name.append("array"); + } else if (variant == CallVariant.VA_LIST) { + name.append("valist"); + } else { + throw VMError.shouldNotReachHere(); + } + if (nonVirtual) { + name.append("Nonvirtual"); + } + name.append("JavaCallTrampoline"); + return name.toString(); + } + + 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(); + + private native void arrayJavaCallTrampoline(); + + private native void valistJavaCallTrampoline(); + + private native void varargsNonvirtualJavaCallTrampoline(); + + private native void arrayNonvirtualJavaCallTrampoline(); + + private native void valistNonvirtualJavaCallTrampoline(); +} diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIJavaCallWrappers.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIJavaCallWrappers.java index e481d3d11eb9..cfa79ed072c6 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIJavaCallWrappers.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIJavaCallWrappers.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, 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 @@ -24,16 +24,13 @@ */ package com.oracle.svm.jni; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.jni.hosted.JNIJavaCallWrapperMethod; -import com.oracle.svm.jni.hosted.JNIJavaCallWrapperMethod.CallVariant; +import com.oracle.svm.jni.hosted.JNICallTrampolineMethod; import jdk.vm.ci.meta.ConstantPool; import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.ResolvedJavaMethod; /** - * Holder class for generated {@link JNIJavaCallWrapperMethod} code. + * Holder class for generated {@link JNICallTrampolineMethod} code. */ public final class JNIJavaCallWrappers { public static ConstantPool getConstantPool(MetaAccessProvider metaAccess) { @@ -42,40 +39,6 @@ public static ConstantPool getConstantPool(MetaAccessProvider metaAccess) { return metaAccess.lookupJavaType(JNIJavaCallWrappers.class).getDeclaredConstructors()[0].getConstantPool(); } - public static ResolvedJavaMethod lookupJavaCallTrampoline(MetaAccessProvider metaAccess, CallVariant variant, boolean nonVirtual) { - StringBuilder name = new StringBuilder(48); - if (variant == CallVariant.VARARGS) { - name.append("varargs"); - } else if (variant == CallVariant.ARRAY) { - name.append("array"); - } else if (variant == CallVariant.VA_LIST) { - name.append("valist"); - } else { - throw VMError.shouldNotReachHere(); - } - if (nonVirtual) { - name.append("Nonvirtual"); - } - name.append("JavaCallTrampoline"); - try { - return metaAccess.lookupJavaMethod(JNIJavaCallWrappers.class.getDeclaredMethod(name.toString())); - } catch (NoSuchMethodException e) { - throw VMError.shouldNotReachHere(e); - } - } - private JNIJavaCallWrappers() { } - - private native void varargsJavaCallTrampoline(); - - private native void arrayJavaCallTrampoline(); - - private native void valistJavaCallTrampoline(); - - private native void varargsNonvirtualJavaCallTrampoline(); - - private native void arrayNonvirtualJavaCallTrampoline(); - - private native void valistNonvirtualJavaCallTrampoline(); } 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 433d14283a67..d4cf2b3c0b15 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 @@ -28,14 +28,20 @@ import java.lang.reflect.Executable; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Set; 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; import org.graalvm.compiler.options.Option; import org.graalvm.nativeimage.ImageSingletons; @@ -66,7 +72,6 @@ import com.oracle.svm.hosted.config.ConfigurationParserUtils; import com.oracle.svm.hosted.meta.MaterializedConstantFields; import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; -import com.oracle.svm.jni.JNIJavaCallWrappers; import com.oracle.svm.jni.JNISupport; import com.oracle.svm.jni.hosted.JNICallTrampolineMethod; import com.oracle.svm.jni.hosted.JNIFieldAccessorMethod; @@ -90,12 +95,7 @@ public static JNIAccessFeature singleton() { private boolean sealed = false; private NativeLibraries nativeLibraries; - private JNICallTrampolineMethod varargsCallTrampolineMethod; - private JNICallTrampolineMethod arrayCallTrampolineMethod; - private JNICallTrampolineMethod valistCallTrampolineMethod; - private JNICallTrampolineMethod varargsNonvirtualCallTrampolineMethod; - private JNICallTrampolineMethod arrayNonvirtualCallTrampolineMethod; - private JNICallTrampolineMethod valistNonvirtualCallTrampolineMethod; + private final Map trampolineMethods = new HashMap<>(); private int loadedConfigurations; @@ -169,12 +169,12 @@ public void beforeAnalysis(BeforeAnalysisAccess arg) { BeforeAnalysisAccessImpl access = (BeforeAnalysisAccessImpl) arg; this.nativeLibraries = access.getNativeLibraries(); - varargsCallTrampolineMethod = createJavaCallTrampoline(access, CallVariant.VARARGS, false); - arrayCallTrampolineMethod = createJavaCallTrampoline(access, CallVariant.ARRAY, false); - valistCallTrampolineMethod = createJavaCallTrampoline(access, CallVariant.VA_LIST, false); - varargsNonvirtualCallTrampolineMethod = createJavaCallTrampoline(access, CallVariant.VARARGS, true); - arrayNonvirtualCallTrampolineMethod = createJavaCallTrampoline(access, CallVariant.ARRAY, true); - valistNonvirtualCallTrampolineMethod = createJavaCallTrampoline(access, CallVariant.VA_LIST, true); + registerJavaCallTrampoline(access, CallVariant.VARARGS, false); + registerJavaCallTrampoline(access, CallVariant.ARRAY, false); + registerJavaCallTrampoline(access, CallVariant.VA_LIST, false); + registerJavaCallTrampoline(access, CallVariant.VARARGS, true); + registerJavaCallTrampoline(access, CallVariant.ARRAY, true); + registerJavaCallTrampoline(access, CallVariant.VA_LIST, true); /* duplicated to reduce the number of analysis iterations */ getConditionalConfigurationRegistry().flushConditionalConfiguration(access); @@ -184,28 +184,47 @@ private static ConditionalConfigurationRegistry getConditionalConfigurationRegis return (ConditionalConfigurationRegistry) ImageSingletons.lookup(JNIRuntimeAccess.JNIRuntimeAccessibilitySupport.class); } - private static JNICallTrampolineMethod createJavaCallTrampoline(BeforeAnalysisAccessImpl access, CallVariant variant, boolean nonVirtual) { - MetaAccessProvider wrappedMetaAccess = access.getMetaAccess().getWrapped(); - ResolvedJavaField field = JNIAccessibleMethod.getCallWrapperField(wrappedMetaAccess, variant, nonVirtual); + private static void registerJavaCallTrampoline(BeforeAnalysisAccessImpl access, CallVariant variant, boolean 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)); - ResolvedJavaMethod method = JNIJavaCallWrappers.lookupJavaCallTrampoline(wrappedMetaAccess, variant, nonVirtual); - JNICallTrampolineMethod trampoline = new JNICallTrampolineMethod(method, field, nonVirtual); - access.registerAsCompiled(access.getUniverse().lookup(trampoline)); - return trampoline; + String name = JNIJavaCallTrampolines.getTrampolineName(variant, nonVirtual); + Method method = ReflectionUtil.lookupMethod(JNIJavaCallTrampolines.class, name); + // Look up the java method to ensure a JNICallTrampolineMethod gets created for it through + // com.oracle.svm.jni.hosted.JNINativeCallWrapperSubstitutionProcessor.lookup + AnalysisMethod trampoline = metaAccess.lookupJavaMethod(method); + access.registerAsCompiled(trampoline); } public JNICallTrampolineMethod getCallTrampolineMethod(CallVariant variant, boolean nonVirtual) { - JNICallTrampolineMethod method = null; - if (variant == CallVariant.VARARGS) { - method = nonVirtual ? varargsNonvirtualCallTrampolineMethod : varargsCallTrampolineMethod; - } else if (variant == CallVariant.ARRAY) { - method = nonVirtual ? arrayNonvirtualCallTrampolineMethod : arrayCallTrampolineMethod; - } else if (variant == CallVariant.VA_LIST) { - method = nonVirtual ? valistNonvirtualCallTrampolineMethod : valistCallTrampolineMethod; + String name = JNIJavaCallTrampolines.getTrampolineName(variant, nonVirtual); + return getCallTrampolineMethod(name); + } + + public JNICallTrampolineMethod getCallTrampolineMethod(String javaTrampolineName) { + JNICallTrampolineMethod trampoline = trampolineMethods.get(javaTrampolineName); + assert trampoline != null; + return trampoline; + } + + public JNICallTrampolineMethod getOrCreateCallTrampolineMethod(DuringSetupAccessImpl access, String trampolineName) { + JNICallTrampolineMethod trampoline = trampolineMethods.get(trampolineName); + + if (trampoline != null) { + return trampoline; } - assert method != null; - return method; + + 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); + trampoline = new JNICallTrampolineMethod(method, field, nonVirtual); + trampolineMethods.put(trampolineName, trampoline); + return trampoline; } public JNINativeLinkage makeLinkage(String declaringClass, String name, String descriptor) { 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 4f2eec53caef..d64f1ed0c0a2 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 ff6f342c4314..17267b59fda6 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, 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 @@ -29,7 +29,11 @@ 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.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; /** * Substitutes methods declared as {@code native} with {@link JNINativeCallWrapperMethod} instances @@ -37,10 +41,21 @@ */ class JNINativeCallWrapperSubstitutionProcessor extends SubstitutionProcessor { private final Map callWrappers = new ConcurrentHashMap<>(); + private final DuringSetupAccessImpl access; + private final ResolvedJavaType jniJavaCallTrampolinesType; + + JNINativeCallWrapperSubstitutionProcessor(DuringSetupAccessImpl access) { + this.access = access; + this.jniJavaCallTrampolinesType = access.getMetaAccess().lookupJavaType(JNIJavaCallTrampolines.class).getWrapped(); + } @Override public ResolvedJavaMethod lookup(ResolvedJavaMethod method) { assert method.isNative() : "Must have been registered as a native substitution processor"; + if (method.getDeclaringClass() == jniJavaCallTrampolinesType) { + // Avoid generating JNINativeCallWrapperMethods for trampolines + return JNIAccessFeature.singleton().getOrCreateCallTrampolineMethod(access, method.getName()); + } return callWrappers.computeIfAbsent(method, JNINativeCallWrapperMethod::new); } @@ -48,6 +63,8 @@ public ResolvedJavaMethod lookup(ResolvedJavaMethod method) { public ResolvedJavaMethod resolve(ResolvedJavaMethod method) { if (method instanceof JNINativeCallWrapperMethod) { return ((JNINativeCallWrapperMethod) method).getOriginal(); + } else if (method instanceof JNICallTrampolineMethod) { + return ((JNICallTrampolineMethod) method).getOriginal(); } return method; }