diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/PEGraphDecoder.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/PEGraphDecoder.java index 9547464002d0..d6b0891525c6 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/PEGraphDecoder.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/PEGraphDecoder.java @@ -1694,7 +1694,7 @@ protected void ensureOuterStateDecoded(PEMethodScope methodScope) { /** * Determines whether to omit an intermediate method (a method other than the root method or a - * leaf callee) from {@link FrameState} or {@link NodeSourcePosition} information. When used to + * leaf caller) from {@link FrameState} or {@link NodeSourcePosition} information. When used to * discard intermediate methods of generated code with non-deterministic names, for example, * this can improve matching of profile-guided optimization information between executions. */ diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index a7b0ecce0c71..a5b5f829b12c 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -124,7 +124,7 @@ def test(): # expect "#1 0x[0-9a-f]+ in com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_.* at [a-z/]+/JavaMainWrapper.java:[0-9]+" exec_string = execute("backtrace") stacktraceRegex = [r"#0%shello\.Hello::main%s %s at hello/Hello\.java:%d"%(spaces_pattern, param_types_pattern, arg_values_pattern, main_start), - r"#1%s(%s in )?java\.lang\.invoke\.LambdaForm\$DMH/0x%s::invokeStatic(Init)?%s %s( at java/lang/invoke/%s:[0-9]+)?"%(spaces_pattern, address_pattern, hex_digits_pattern, param_types_pattern, arg_values_pattern, package_file_pattern), + r"#1%s(%s in )?java\.lang\.invoke\.LambdaForm\$DMH/s%s::invokeStatic(Init)?%s %s( at java/lang/invoke/%s:[0-9]+)?"%(spaces_pattern, address_pattern, hex_digits_pattern, param_types_pattern, arg_values_pattern, package_file_pattern), r"#2%s(%s in )?com\.oracle\.svm\.core\.JavaMainWrapper::invokeMain%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, param_types_pattern, arg_values_pattern, package_pattern), r"#3%s(%s in )?com\.oracle\.svm\.core\.JavaMainWrapper::runCore0%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern), r"#4%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern), @@ -370,7 +370,7 @@ def test(): exec_string = execute("backtrace") stacktraceRegex = [r"#0%shello\.Hello\$Greeter::greeter%s %s at hello/Hello\.java:38"%(spaces_pattern, param_types_pattern, arg_values_pattern), r"#1%s%s in hello\.Hello::main%s %s at hello/Hello\.java:%d"%(spaces_pattern, address_pattern, param_types_pattern, arg_values_pattern, main_start), - r"#2%s(%s in )?java\.lang\.invoke\.LambdaForm\$DMH/0x%s::invokeStatic(Init)?%s %s( at java/lang/invoke/%s:[0-9]+)?"%(spaces_pattern, address_pattern, hex_digits_pattern, param_types_pattern, arg_values_pattern, package_file_pattern), + r"#2%s(%s in )?java\.lang\.invoke\.LambdaForm\$DMH/s%s::invokeStatic(Init)?%s %s( at java/lang/invoke/%s:[0-9]+)?"%(spaces_pattern, address_pattern, hex_digits_pattern, param_types_pattern, arg_values_pattern, package_file_pattern), r"#3%s(%s in )?com\.oracle\.svm\.core\.JavaMainWrapper::invokeMain%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, param_types_pattern, arg_values_pattern, package_pattern), r"#4%s(%s in )?com\.oracle\.svm\.core\.JavaMainWrapper::runCore0%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern), r"#5%s%s in com\.oracle\.svm\.core\.JavaMainWrapper::runCore%s %s at %sJavaMainWrapper\.java:[0-9]+"%(spaces_pattern, address_pattern, no_param_types_pattern, no_arg_values_pattern, package_pattern), diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionType.java new file mode 100644 index 000000000000..379fc0a06a41 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionType.java @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2023, 2023, 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.hosted.annotation; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; + +import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; +import com.oracle.svm.core.util.VMError; + +import jdk.vm.ci.meta.Assumptions.AssumptionResult; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.Signature; +import jdk.vm.ci.meta.UnresolvedJavaField; +import jdk.vm.ci.meta.UnresolvedJavaType; + +public abstract class CustomSubstitutionType implements ResolvedJavaType, OriginalClassProvider, AnnotationWrapper { + private final ResolvedJavaType original; + + public CustomSubstitutionType(ResolvedJavaType original) { + this.original = original; + } + + @Override + public String getName() { + return original.getName(); + } + + @Override + public AnnotatedElement getAnnotationRoot() { + return null; + } + + @Override + public boolean hasFinalizer() { + return original.hasFinalizer(); + } + + @Override + public AssumptionResult hasFinalizableSubclass() { + return original.hasFinalizableSubclass(); + } + + @Override + public boolean isInterface() { + return original.isInterface(); + } + + @Override + public boolean isInstanceClass() { + return original.isInstanceClass(); + } + + @Override + public boolean isPrimitive() { + return original.isPrimitive(); + } + + @Override + public boolean isLeaf() { + return original.isLeaf(); + } + + @Override + public boolean isEnum() { + return original.isEnum(); + } + + @Override + public boolean isInitialized() { + return original.isInitialized(); + } + + @Override + public void initialize() { + original.initialize(); + } + + @Override + public boolean isLinked() { + return original.isLinked(); + } + + @Override + public void link() { + original.link(); + } + + @Override + public boolean hasDefaultMethods() { + return original.hasDefaultMethods(); + } + + @Override + public boolean declaresDefaultMethods() { + return original.declaresDefaultMethods(); + } + + @Override + public boolean isAssignableFrom(ResolvedJavaType other) { + return original.isAssignableFrom(other); + } + + @SuppressWarnings("deprecation") + @Override + public ResolvedJavaType getHostClass() { + return original.getHostClass(); + } + + @Override + public boolean isJavaLangObject() { + return original.isJavaLangObject(); + } + + @Override + public boolean isInstance(JavaConstant obj) { + return original.isInstance(obj); + } + + @Override + public ResolvedJavaType getSuperclass() { + return original.getSuperclass(); + } + + @Override + public ResolvedJavaType[] getInterfaces() { + return original.getInterfaces(); + } + + @Override + public ResolvedJavaType getSingleImplementor() { + return original.getSingleImplementor(); + } + + @Override + public ResolvedJavaType findLeastCommonAncestor(ResolvedJavaType otherType) { + return original.findLeastCommonAncestor(otherType); + } + + @Override + public AssumptionResult findLeafConcreteSubtype() { + return original.findLeafConcreteSubtype(); + } + + @Override + public ResolvedJavaType getComponentType() { + return original.getComponentType(); + } + + @Override + public ResolvedJavaType getElementalType() { + return original.getElementalType(); + } + + @Override + public ResolvedJavaType getArrayClass() { + return original.getArrayClass(); + } + + @Override + public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { + return original.resolveMethod(method, callerType); + } + + @Override + public ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { + return original.resolveConcreteMethod(method, callerType); + } + + @Override + public AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) { + return original.findUniqueConcreteMethod(method); + } + + @Override + public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) { + return original.getInstanceFields(includeSuperclasses); + } + + @Override + public ResolvedJavaField[] getStaticFields() { + return original.getStaticFields(); + } + + @Override + public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) { + return original.findInstanceFieldWithOffset(offset, expectedKind); + } + + @Override + public String getSourceFileName() { + return original.getSourceFileName(); + } + + @Override + public boolean isLocal() { + return original.isLocal(); + } + + @Override + public boolean isMember() { + return original.isMember(); + } + + @Override + public ResolvedJavaType getEnclosingType() { + return original.getEnclosingType(); + } + + @Override + public ResolvedJavaMethod[] getDeclaredConstructors() { + return getDeclaredConstructors(true); + } + + @Override + public ResolvedJavaMethod[] getDeclaredConstructors(boolean forceLink) { + VMError.guarantee(forceLink == false, "only use getDeclaredConstructors without forcing to link, because linking can throw LinkageError"); + return original.getDeclaredConstructors(forceLink); + } + + @Override + public ResolvedJavaMethod[] getDeclaredMethods() { + return getDeclaredMethods(true); + } + + @Override + public ResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) { + return original.getDeclaredMethods(forceLink); + } + + @Override + public ResolvedJavaMethod getClassInitializer() { + return original.getClassInitializer(); + } + + @Override + public ResolvedJavaMethod findMethod(String name, Signature signature) { + return original.findMethod(name, signature); + } + + @Override + public boolean isCloneableWithAllocation() { + return original.isCloneableWithAllocation(); + } + + @Override + public ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, boolean resolve) { + return original.lookupType(unresolvedJavaType, resolve); + } + + @Override + public ResolvedJavaField resolveField(UnresolvedJavaField unresolvedJavaField, ResolvedJavaType accessingClass) { + return original.resolveField(unresolvedJavaField, accessingClass); + } + + @Override + public boolean isArray() { + return original.isArray(); + } + + @Override + public JavaKind getJavaKind() { + return original.getJavaKind(); + } + + @Override + public ResolvedJavaType resolve(ResolvedJavaType accessingClass) { + return original.resolve(accessingClass); + } + + @Override + public int getModifiers() { + return original.getModifiers(); + } + + @Override + public boolean isSynchronized() { + return original.isSynchronized(); + } + + @Override + public boolean isStatic() { + return original.isStatic(); + } + + @Override + public boolean isFinalFlagSet() { + return original.isFinalFlagSet(); + } + + @Override + public boolean isPublic() { + return original.isPublic(); + } + + @Override + public boolean isPackagePrivate() { + return original.isPackagePrivate(); + } + + @Override + public boolean isPrivate() { + return original.isPrivate(); + } + + @Override + public boolean isProtected() { + return original.isProtected(); + } + + @Override + public boolean isTransient() { + return original.isTransient(); + } + + @Override + public boolean isStrict() { + return original.isStrict(); + } + + @Override + public boolean isVolatile() { + return original.isVolatile(); + } + + @Override + public boolean isNative() { + return original.isNative(); + } + + @Override + public boolean isAbstract() { + return original.isAbstract(); + } + + @Override + public boolean isConcrete() { + return original.isConcrete(); + } + + @Override + public T[] getAnnotationsByType(Class annotationClass) { + return original.getAnnotationsByType(annotationClass); + } + + @Override + public T[] getDeclaredAnnotationsByType(Class annotationClass) { + return original.getDeclaredAnnotationsByType(annotationClass); + } + + public ResolvedJavaType getOriginal() { + return original; + } + + @Override + public Class getJavaClass() { + return OriginalClassProvider.getJavaClass(original); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java index 4b6b04fb71c1..c68d490f4442 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaSubstitutionType.java @@ -24,36 +24,22 @@ */ package com.oracle.svm.hosted.lambda; -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; - -import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.svm.core.jdk.LambdaFormHiddenMethod; -import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.annotation.AnnotationValue; -import com.oracle.svm.hosted.annotation.AnnotationWrapper; +import com.oracle.svm.hosted.annotation.CustomSubstitutionType; import com.oracle.svm.hosted.annotation.SubstrateAnnotationExtractor; -import jdk.vm.ci.meta.Assumptions.AssumptionResult; -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaField; -import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; -import jdk.vm.ci.meta.Signature; -import jdk.vm.ci.meta.UnresolvedJavaField; -import jdk.vm.ci.meta.UnresolvedJavaType; /** * Simply changes the name of Lambdas from a random ID into a stable name. */ -public class LambdaSubstitutionType implements ResolvedJavaType, OriginalClassProvider, AnnotationWrapper { - private final ResolvedJavaType original; +public class LambdaSubstitutionType extends CustomSubstitutionType { private final String stableName; @SuppressWarnings("try") LambdaSubstitutionType(ResolvedJavaType original, String stableName) { - this.original = original; + super(original); this.stableName = stableName; } @@ -62,341 +48,10 @@ public String getName() { return stableName; } - @Override - public AnnotatedElement getAnnotationRoot() { - return null; - } - private static final AnnotationValue[] INJECTED_ANNOTATIONS = SubstrateAnnotationExtractor.prepareInjectedAnnotations(LambdaFormHiddenMethod.Holder.INSTANCE); @Override public AnnotationValue[] getInjectedAnnotations() { return INJECTED_ANNOTATIONS; } - - @Override - public boolean hasFinalizer() { - return original.hasFinalizer(); - } - - @Override - public AssumptionResult hasFinalizableSubclass() { - return original.hasFinalizableSubclass(); - } - - @Override - public boolean isInterface() { - return original.isInterface(); - } - - @Override - public boolean isInstanceClass() { - return original.isInstanceClass(); - } - - @Override - public boolean isPrimitive() { - return original.isPrimitive(); - } - - @Override - public boolean isLeaf() { - return original.isLeaf(); - } - - @Override - public boolean isEnum() { - return original.isEnum(); - } - - @Override - public boolean isInitialized() { - return original.isInitialized(); - } - - @Override - public void initialize() { - original.initialize(); - } - - @Override - public boolean isLinked() { - return original.isLinked(); - } - - @Override - public void link() { - original.link(); - } - - @Override - public boolean hasDefaultMethods() { - return original.hasDefaultMethods(); - } - - @Override - public boolean declaresDefaultMethods() { - return original.declaresDefaultMethods(); - } - - @Override - public boolean isAssignableFrom(ResolvedJavaType other) { - return original.isAssignableFrom(other); - } - - @SuppressWarnings("deprecation") - @Override - public ResolvedJavaType getHostClass() { - return original.getHostClass(); - } - - @Override - public boolean isJavaLangObject() { - return original.isJavaLangObject(); - } - - @Override - public boolean isInstance(JavaConstant obj) { - return original.isInstance(obj); - } - - @Override - public ResolvedJavaType getSuperclass() { - return original.getSuperclass(); - } - - @Override - public ResolvedJavaType[] getInterfaces() { - return original.getInterfaces(); - } - - @Override - public ResolvedJavaType getSingleImplementor() { - return original.getSingleImplementor(); - } - - @Override - public ResolvedJavaType findLeastCommonAncestor(ResolvedJavaType otherType) { - return original.findLeastCommonAncestor(otherType); - } - - @Override - public AssumptionResult findLeafConcreteSubtype() { - return original.findLeafConcreteSubtype(); - } - - @Override - public ResolvedJavaType getComponentType() { - return original.getComponentType(); - } - - @Override - public ResolvedJavaType getElementalType() { - return original.getElementalType(); - } - - @Override - public ResolvedJavaType getArrayClass() { - return original.getArrayClass(); - } - - @Override - public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { - return original.resolveMethod(method, callerType); - } - - @Override - public ResolvedJavaMethod resolveConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { - return original.resolveConcreteMethod(method, callerType); - } - - @Override - public AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) { - return original.findUniqueConcreteMethod(method); - } - - @Override - public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) { - return original.getInstanceFields(includeSuperclasses); - } - - @Override - public ResolvedJavaField[] getStaticFields() { - return original.getStaticFields(); - } - - @Override - public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) { - return original.findInstanceFieldWithOffset(offset, expectedKind); - } - - @Override - public String getSourceFileName() { - return original.getSourceFileName(); - } - - @Override - public boolean isLocal() { - return original.isLocal(); - } - - @Override - public boolean isMember() { - return original.isMember(); - } - - @Override - public ResolvedJavaType getEnclosingType() { - return original.getEnclosingType(); - } - - @Override - public ResolvedJavaMethod[] getDeclaredConstructors() { - return getDeclaredConstructors(true); - } - - @Override - public ResolvedJavaMethod[] getDeclaredConstructors(boolean forceLink) { - VMError.guarantee(forceLink == false, "only use getDeclaredConstructors without forcing to link, because linking can throw LinkageError"); - return original.getDeclaredConstructors(forceLink); - } - - @Override - public ResolvedJavaMethod[] getDeclaredMethods() { - return getDeclaredMethods(true); - } - - @Override - public ResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) { - return original.getDeclaredMethods(forceLink); - } - - @Override - public ResolvedJavaMethod getClassInitializer() { - return original.getClassInitializer(); - } - - @Override - public ResolvedJavaMethod findMethod(String name, Signature signature) { - return original.findMethod(name, signature); - } - - @Override - public boolean isCloneableWithAllocation() { - return original.isCloneableWithAllocation(); - } - - @Override - public ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, boolean resolve) { - return original.lookupType(unresolvedJavaType, resolve); - } - - @Override - public ResolvedJavaField resolveField(UnresolvedJavaField unresolvedJavaField, ResolvedJavaType accessingClass) { - return original.resolveField(unresolvedJavaField, accessingClass); - } - - @Override - public boolean isArray() { - return original.isArray(); - } - - @Override - public JavaKind getJavaKind() { - return original.getJavaKind(); - } - - @Override - public ResolvedJavaType resolve(ResolvedJavaType accessingClass) { - return original.resolve(accessingClass); - } - - @Override - public int getModifiers() { - return original.getModifiers(); - } - - @Override - public boolean isSynchronized() { - return original.isSynchronized(); - } - - @Override - public boolean isStatic() { - return original.isStatic(); - } - - @Override - public boolean isFinalFlagSet() { - return original.isFinalFlagSet(); - } - - @Override - public boolean isPublic() { - return original.isPublic(); - } - - @Override - public boolean isPackagePrivate() { - return original.isPackagePrivate(); - } - - @Override - public boolean isPrivate() { - return original.isPrivate(); - } - - @Override - public boolean isProtected() { - return original.isProtected(); - } - - @Override - public boolean isTransient() { - return original.isTransient(); - } - - @Override - public boolean isStrict() { - return original.isStrict(); - } - - @Override - public boolean isVolatile() { - return original.isVolatile(); - } - - @Override - public boolean isNative() { - return original.isNative(); - } - - @Override - public boolean isAbstract() { - return original.isAbstract(); - } - - @Override - public boolean isConcrete() { - return original.isConcrete(); - } - - @Override - public T[] getAnnotationsByType(Class annotationClass) { - return original.getAnnotationsByType(annotationClass); - } - - @Override - public T[] getDeclaredAnnotationsByType(Class annotationClass) { - return original.getDeclaredAnnotationsByType(annotationClass); - } - - public ResolvedJavaType getOriginal() { - return original; - } - - @Override - public Class getJavaClass() { - return OriginalClassProvider.getJavaClass(original); - } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java index 6434dea55452..c366559db82f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleFeature.java @@ -47,6 +47,7 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; +import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.util.ReflectionUtil; import sun.invoke.util.ValueConversions; @@ -101,6 +102,8 @@ public class MethodHandleFeature implements InternalFeature { private Object runtimeMethodTypeInternTable; private Method concurrentWeakInternSetAdd; + private MethodHandleInvokerRenamingSubstitutionProcessor substitutionProcessor; + @Override public void duringSetup(DuringSetupAccess access) { Class memberNameClass = access.findClassByName("java.lang.invoke.MemberName"); @@ -121,6 +124,10 @@ public void duringSetup(DuringSetupAccess access) { Class concurrentWeakInternSetClass = access.findClassByName("java.lang.invoke.MethodType$ConcurrentWeakInternSet"); runtimeMethodTypeInternTable = ReflectionUtil.newInstance(concurrentWeakInternSetClass); concurrentWeakInternSetAdd = ReflectionUtil.lookupMethod(concurrentWeakInternSetClass, "add", Object.class); + + var accessImpl = (DuringSetupAccessImpl) access; + substitutionProcessor = new MethodHandleInvokerRenamingSubstitutionProcessor(accessImpl.getBigBang()); + accessImpl.registerSubstitutionProcessor(substitutionProcessor); } @Override @@ -381,4 +388,9 @@ private static void scanBoundMethodHandle(DuringAnalysisAccess a, Class bmhSu access.requireAnalysisIteration(); } } + + @Override + public void afterAnalysis(AfterAnalysisAccess access) { + assert substitutionProcessor.checkAllTypeNames(); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleInvokerRenamingSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleInvokerRenamingSubstitutionProcessor.java new file mode 100644 index 000000000000..ac025a32f451 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleInvokerRenamingSubstitutionProcessor.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2023, 2023, 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.hosted.methodhandles; + +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.oracle.graal.pointsto.BigBang; +import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; +import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.util.ReflectionUtil; + +import jdk.vm.ci.meta.ResolvedJavaType; + +/** + * A substitution processor that renames classes generated by {@code InvokerBytecodeGenerator}, + * which are assigned more or less arbitrary names by the host VM, to stable names that are based on + * the {@code LambdaForm} which they were compiled from. + */ +public class MethodHandleInvokerRenamingSubstitutionProcessor extends SubstitutionProcessor { + private static final Class LAMBDA_FORM_CLASS = ReflectionUtil.lookupClass(false, "java.lang.invoke.LambdaForm"); + private static final Method CLASS_GET_CLASS_DATA_METHOD = ReflectionUtil.lookupMethod(Class.class, "getClassData"); + + /* + * We currently only replace the invokers of direct method handles which have a simpler + * structure and appear to be reliably reused. + */ + private static final String CLASS_NAME_SUBSTRING = "LambdaForm$DMH"; + private static final String STABLE_NAME_TEMPLATE = "Ljava/lang/invoke/LambdaForm$DMH.s"; + + private final BigBang bb; + + private final ConcurrentMap typeSubstitutions = new ConcurrentHashMap<>(); + private final Set uniqueTypeNames = new HashSet<>(); + + MethodHandleInvokerRenamingSubstitutionProcessor(BigBang bb) { + this.bb = bb; + } + + @Override + public ResolvedJavaType lookup(ResolvedJavaType type) { + if (!shouldReplace(type)) { + return type; + } + return getSubstitution(type); + } + + private static boolean shouldReplace(ResolvedJavaType type) { + return !(type instanceof MethodHandleInvokerSubstitutionType) && type.isFinalFlagSet() && type.getName().contains(CLASS_NAME_SUBSTRING); + } + + @Override + public ResolvedJavaType resolve(ResolvedJavaType type) { + if (type instanceof MethodHandleInvokerSubstitutionType) { + return ((MethodHandleInvokerSubstitutionType) type).getOriginal(); + } + return type; + } + + private ResolvedJavaType getSubstitution(ResolvedJavaType type) { + return typeSubstitutions.computeIfAbsent(type, original -> { + try { + Class clazz = OriginalClassProvider.getJavaClass(original); + Object classData = CLASS_GET_CLASS_DATA_METHOD.invoke(clazz); + VMError.guarantee(LAMBDA_FORM_CLASS.isInstance(classData)); + int hash = classData.hashCode(); + return new MethodHandleInvokerSubstitutionType(original, findUniqueName(hash)); + } catch (ReflectiveOperationException e) { + throw VMError.shouldNotReachHere(e); + } + }); + } + + private String findUniqueName(int hashCode) { + CharSequence baseName = STABLE_NAME_TEMPLATE + Integer.toHexString(hashCode); + String name = baseName + ";"; + synchronized (uniqueTypeNames) { + int suffix = 1; + while (uniqueTypeNames.contains(name)) { + name = baseName + "_" + suffix + ";"; + suffix++; + } + uniqueTypeNames.add(name); + return name; + } + } + + boolean checkAllTypeNames() { + if (!SubstrateUtil.assertionsEnabled()) { + throw new AssertionError("Expensive check: should only run with assertions enabled."); + } + + List types = bb.getUniverse().getTypes(); + + if (types.stream().anyMatch(aType -> shouldReplace(aType.getWrapped()))) { + throw new AssertionError("All relevant types must have been substituted."); + } + + Set names = new HashSet<>(); + types.stream() + .map(AnalysisType::getName) + .filter(x -> x.contains(CLASS_NAME_SUBSTRING)) + .forEach(name -> { + if (names.contains(name)) { + throw new AssertionError("Duplicate name: " + name); + } + names.add(name); + }); + return true; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleInvokerSubstitutionType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleInvokerSubstitutionType.java new file mode 100644 index 000000000000..1b8158ee6f9a --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/methodhandles/MethodHandleInvokerSubstitutionType.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023, 2023, 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.hosted.methodhandles; + +import com.oracle.svm.hosted.annotation.CustomSubstitutionType; + +import jdk.vm.ci.meta.ResolvedJavaType; + +class MethodHandleInvokerSubstitutionType extends CustomSubstitutionType { + private final String stableName; + + MethodHandleInvokerSubstitutionType(ResolvedJavaType original, String stableName) { + super(original); + this.stableName = stableName; + } + + @Override + public String getName() { + return stableName; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java index 332718ea10c9..1ad826a3effe 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java @@ -70,6 +70,7 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ReachabilityRegistrationNode; import com.oracle.svm.hosted.SVMHost; +import com.oracle.svm.hosted.methodhandles.MethodHandleInvokerRenamingSubstitutionProcessor; import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -486,9 +487,11 @@ private static boolean inlineForMethodHandleIntrinsification(ResolvedJavaMethod } /** - * Discard information on inlined calls to generated classes of LambdaForms, which are not - * assigned names that are stable between executions and would cause mismatches in collected + * Discard information on inlined calls to generated classes of LambdaForms, not all of which + * are assigned names that are stable between executions and would cause mismatches in collected * profile-guided optimization data which prevent optimizations. + * + * @see MethodHandleInvokerRenamingSubstitutionProcessor */ protected boolean shouldOmitIntermediateMethodInState(ResolvedJavaMethod method) { return method.isAnnotationPresent(COMPILED_LAMBDA_FORM_ANNOTATION);