From 526a6d7573d3e8118b5138f812deb0270ad5036e Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Thu, 20 Jul 2023 13:58:17 -0700 Subject: [PATCH 1/2] Simplify class initialization configuration for Lambda classes --- ...ostedUsagesClassInitializationSupport.java | 26 ++++++++-- .../ClassInitializationFeature.java | 47 +++++++++++++++---- .../ClassInitializationSupport.java | 15 ++++++ .../DisallowedImageHeapObjectFeature.java | 11 +---- .../test/clinit/TestClassInitialization.java | 13 +++++ 5 files changed, 91 insertions(+), 21 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/AllowAllHostedUsagesClassInitializationSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/AllowAllHostedUsagesClassInitializationSupport.java index 76058e96bccd..0794aed3ce75 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/AllowAllHostedUsagesClassInitializationSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/AllowAllHostedUsagesClassInitializationSupport.java @@ -26,6 +26,8 @@ import java.lang.reflect.Proxy; +import org.graalvm.compiler.java.LambdaUtils; + import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.util.UserError; @@ -159,9 +161,27 @@ InitKind computeInitKindAndMaybeInitializeClass(Class clazz, boolean memoize) } superResult = superResult.max(processInterfaces(clazz, memoize)); - if (superResult == InitKind.BUILD_TIME && Proxy.isProxyClass(clazz)) { - forceInitializeHosted(clazz, "proxy classes with interfaces initialized at build time are also initialized at build time", false); - return InitKind.BUILD_TIME; + if (superResult == InitKind.BUILD_TIME && (Proxy.isProxyClass(clazz) || LambdaUtils.isLambdaType(metaAccess.lookupJavaType(clazz)))) { + /* + * To simplify class initialization configuration for proxy and lambda types, + * registering all of their implemented interfaces as "initialize at build time" is + * equivalent to registering the proxy/lambda type itself. This is safe because we know + * that proxy/lambda types themselves have no problematic code in the class initializer + * (they are generated classes). + * + * Note that we must look at all interfaces, including transitive dependencies. + */ + boolean allInterfacesSpecifiedAsBuildTime = true; + for (Class iface : allInterfaces(clazz)) { + if (specifiedInitKindFor(iface) != InitKind.BUILD_TIME) { + allInterfacesSpecifiedAsBuildTime = false; + break; + } + } + if (allInterfacesSpecifiedAsBuildTime) { + forceInitializeHosted(clazz, "proxy/lambda classes with all interfaces explicitly marked as --initialize-at-build-time are also initialized at build time", false); + return InitKind.BUILD_TIME; + } } InitKind result = superResult.max(clazzResult); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java index 415dc609f7f9..973647ee6751 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java @@ -29,6 +29,7 @@ import static com.oracle.svm.hosted.classinitialization.InitKind.RUN_TIME; import java.io.PrintWriter; +import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -125,14 +126,42 @@ private Object checkImageHeapInstance(Object obj) { * means that the user cannot later manually register it as RERUN or RUN_TIME. */ if (obj != null && !classInitializationSupport.maybeInitializeAtBuildTime(obj.getClass())) { - String msg = "No instances of " + obj.getClass().getTypeName() + " are allowed in the image heap as this class should be initialized at image runtime."; - msg += classInitializationSupport.objectInstantiationTraceMessage(obj, - " To fix the issue mark " + obj.getClass().getTypeName() + " for build-time initialization with " + - SubstrateOptionsParser.commandArgument(ClassInitializationOptions.ClassInitialization, obj.getClass().getTypeName(), "initialize-at-build-time") + - " or use the the information from the trace to find the culprit and " + - SubstrateOptionsParser.commandArgument(ClassInitializationOptions.ClassInitialization, "", "initialize-at-run-time") + - " to prevent its instantiation.\n"); - throw new UnsupportedFeatureException(msg); + StringBuilder msg = new StringBuilder() + .append("No instances of ").append(obj.getClass().getTypeName()).append(" are allowed in the image heap as this class should be initialized at image runtime."); + + String action = " To fix the issue mark " + obj.getClass().getTypeName() + " for build-time initialization with " + + SubstrateOptionsParser.commandArgument(ClassInitializationOptions.ClassInitialization, obj.getClass().getTypeName(), "initialize-at-build-time") + + " or use the the information from the trace to find the culprit and " + + SubstrateOptionsParser.commandArgument(ClassInitializationOptions.ClassInitialization, "", "initialize-at-run-time") + + " to prevent its instantiation." + System.lineSeparator(); + msg.append(classInitializationSupport.objectInstantiationTraceMessage(obj, action)); + + if (!ClassInitializationOptions.UseDeprecatedOldClassInitialization.getValue()) { + msg.append(System.lineSeparator()) + .append("If you see this error while migrating to a newer GraalVM release, please note that the class initialization strategy has changed in GraalVM for JDK 21.") + .append(" All classes can now be used at image build time. However, only classes explicitly marked as --initialize-at-build-time are allowed to be in the image heap.") + .append(" This rule is now strictly enforced, i.e., the problem might be solvable by registering the reported type as --initialize-at-build-time."); + } + + String proxyOrLambda = null; + if (Proxy.isProxyClass(obj.getClass())) { + proxyOrLambda = "Proxy"; + } else if (obj.getClass().getName().contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) { + proxyOrLambda = "Lambda"; + } + if (proxyOrLambda != null) { + msg.append(System.lineSeparator()) + .append("For ").append(proxyOrLambda).append(" classes, it is also sufficient to register all interfaces that the class implements as --initialize-at-build-time. ") + .append(" The interfaces of this ").append(proxyOrLambda).append(" class are: ["); + String sep = ""; + for (var iface : ClassInitializationSupport.allInterfaces(obj.getClass())) { + msg.append(sep).append(iface.getTypeName()); + sep = ", "; + } + msg.append("]."); + } + + throw new UnsupportedFeatureException(msg.toString()); } return obj; } @@ -205,7 +234,7 @@ public void afterAnalysis(AfterAnalysisAccess a) { .filter(c -> c.getClassLoader() != null && c.getClassLoader() != ClassLoader.getPlatformClassLoader()) .filter(c -> classInitializationSupport.specifiedInitKindFor(c) == null) .map(Class::getTypeName) - .filter(name -> !LambdaUtils.isLambdaName(name)) + .filter(name -> !name.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) .collect(Collectors.toList()); if (!unspecifiedClasses.isEmpty()) { System.err.println("The following classes have unspecified initialization policy:" + System.lineSeparator() + String.join(System.lineSeparator(), unspecifiedClasses)); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java index a6e09bb8248b..88a107f35d04 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java @@ -34,6 +34,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; +import org.graalvm.collections.EconomicSet; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport; import org.graalvm.nativeimage.impl.clinit.ClassInitializationTracking; @@ -305,4 +306,18 @@ static String getTraceString(StackTraceElement[] trace) { abstract boolean checkDelayedInitialization(); abstract void doLateInitialization(AnalysisUniverse universe, AnalysisMetaAccess aMetaAccess); + + public static EconomicSet> allInterfaces(Class clazz) { + EconomicSet> result = EconomicSet.create(); + addAllInterfaces(clazz, result); + return result; + } + + private static void addAllInterfaces(Class clazz, EconomicSet> result) { + for (var interf : clazz.getInterfaces()) { + if (result.add(interf)) { + addAllInterfaces(interf, result); + } + } + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java index eb050f5d8d2c..83cb4b961fb2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java @@ -41,12 +41,12 @@ import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.image.DisallowedImageHeapObjects; import com.oracle.svm.core.jdk.management.ManagementFeature; import com.oracle.svm.core.jdk.management.ManagementSupport; import com.oracle.svm.core.option.SubstrateOptionsParser; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.classinitialization.ClassInitializationOptions; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; @@ -168,18 +168,11 @@ private void checkDisallowedMBeanObjects(Object original) { } private RuntimeException error(String msg, Object obj, String initializerAction) { - String suffix = ""; - if (!ClassInitializationOptions.UseDeprecatedOldClassInitialization.getValue()) { - suffix = System.lineSeparator() + - "If you see this error while migrating to a newer GraalVM release, please note that the class initialization strategy has changed in GraalVM for JDK 21." + - " All classes can now be used at image build time. However, only classes explicitly marked as --initialize-at-built-time are allowed to be in the image heap." + - " This rule is now strictly enforced, i.e., the problem might be solvable by registering the reported type as --initialize-at-built-time."; - } throw new UnsupportedFeatureException(msg + " " + classInitialization.objectInstantiationTraceMessage(obj, initializerAction) + " " + "The object was probably created by a class initializer and is reachable from a static field. " + "You can request class initialization at image runtime by using the option " + SubstrateOptionsParser.commandArgument(ClassInitializationOptions.ClassInitialization, "", "initialize-at-run-time") + ". " + - "Or you can write your own initialization methods and call them explicitly from your main entry point." + suffix); + "Or you can write your own initialization methods and call them explicitly from your main entry point."); } private static boolean search(byte[] haystack, byte[] needle) { diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java index 14f481166aa7..aa5248001e2f 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Vector; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -823,6 +824,16 @@ private static int transitivelyPure() { return 42; } + /* + * Since {@link Function} is a core JDK type that is always marked as + * "initialize at build time", it is allowed to have a lambda for it in the image heap. + */ + static Function buildTimeLambda = TestClassInitialization::duplicate; + + static String duplicate(String s) { + return s + s; + } + public static void main(String[] args) { for (var checkedClass : checkedClasses) { boolean nameHasSimulated = checkedClass.getName().contains("MustBeSimulated"); @@ -833,6 +844,8 @@ public static void main(String[] args) { } } + assertTrue("123123".equals(buildTimeLambda.apply("123"))); + assertSame(42, PureMustBeSafeEarly.v); assertSame(84, PureCallMustBeSafeEarly.v); assertSame(42, InitializesPureMustBeDelayed.v); From c60b247b052a17121d2cd4f254c98a656734c226 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Thu, 20 Jul 2023 15:39:20 -0700 Subject: [PATCH 2/2] Canonicalize class initialization checks coming from invocation plugins during InlineBeforeAnalysis / SimulateClassInitializer --- .../graalvm/compiler/java/BytecodeParser.java | 4 +-- .../graphbuilderconf/GraphBuilderContext.java | 17 +++++++++--- .../graphbuilderconf/GraphBuilderTool.java | 4 +-- .../compiler/replacements/GraphKit.java | 9 ++++--- .../replacements/IntrinsicGraphBuilder.java | 5 ++-- .../compiler/replacements/PEGraphDecoder.java | 26 +++++++++++++++---- .../SubstrateClassInitializationPlugin.java | 2 +- .../test/clinit/TestClassInitialization.java | 12 +++++++++ 8 files changed, 60 insertions(+), 19 deletions(-) diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/java/BytecodeParser.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/java/BytecodeParser.java index 5fc9c682e69b..6297d3b45d67 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/java/BytecodeParser.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/java/BytecodeParser.java @@ -2861,7 +2861,7 @@ protected ConstantNode appendConstant(JavaConstant constant) { } @Override - public T append(T v) { + public T append(T v) { assert !graph.trackNodeSourcePosition() || graph.currentNodeSourcePosition() != null || currentBlock == blockMap.getUnwindBlock() || currentBlock instanceof ExceptionDispatchBlock; if (v.graph() != null) { return v; @@ -2873,7 +2873,7 @@ public T append(T v) { return added; } - private void updateLastInstruction(T v) { + private void updateLastInstruction(T v) { if (v instanceof FixedNode) { FixedNode fixedNode = (FixedNode) v; if (lastInstr != null) { diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java index 8e96d37ed3a0..f3cd04a45b87 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java @@ -35,6 +35,7 @@ import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.core.common.type.StampPair; import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.AbstractMergeNode; import org.graalvm.compiler.nodes.BeginNode; @@ -90,7 +91,7 @@ public interface GraphBuilderContext extends GraphBuilderTool { * * @param kind the kind to use when type checking this operation * @param value the value to push to the stack. The value must already have been - * {@linkplain #append(ValueNode) appended}. + * {@linkplain #append(Node) appended}. */ void push(JavaKind kind, ValueNode value); @@ -121,7 +122,7 @@ default ValueNode[] popArguments(int argSize) { * type checking this operation. * @return a node equivalent to {@code value} in the graph */ - default T add(T value) { + default T add(T value) { if (value.graph() != null) { assert !(value instanceof StateSplit) || ((StateSplit) value).stateAfter() != null; return value; @@ -129,6 +130,16 @@ default T add(T value) { return setStateAfterIfNecessary(this, append(value)); } + /** + * Maybe performs canonicalization on the provided node. Either the result of the + * canonicalization, or the original node if canonicalization is not possible, is added to the + * graph and returned. Note that the return value can be null when canonicalization determines + * that the node can be deleted. + */ + default Node canonicalizeAndAdd(Node value) { + return add(value); + } + default ValueNode addNonNullCast(ValueNode value) { AbstractPointerStamp valueStamp = (AbstractPointerStamp) value.stamp(NodeView.DEFAULT); if (valueStamp.nonNull()) { @@ -536,7 +547,7 @@ default void replacePluginWithException(GeneratedInvocationPlugin plugin, Resolv throw GraalError.unimplementedParent(); // ExcludeFromJacocoGeneratedReport } - static T setStateAfterIfNecessary(GraphBuilderContext b, T value) { + static T setStateAfterIfNecessary(GraphBuilderContext b, T value) { if (value instanceof StateSplit) { StateSplit stateSplit = (StateSplit) value; FrameState oldState = stateSplit.stateAfter(); diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderTool.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderTool.java index aafb7262c423..d44c77fe1c0a 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderTool.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderTool.java @@ -25,8 +25,8 @@ package org.graalvm.compiler.nodes.graphbuilderconf; import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodes.StructuredGraph; -import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.spi.CoreProviders; import org.graalvm.compiler.options.OptionValues; @@ -43,7 +43,7 @@ public interface GraphBuilderTool extends CoreProviders { * @param value the node to be added to the graph * @return either the node added or an equivalent node */ - T append(T value); + T append(T value); default Assumptions getAssumptions() { return getGraph().getAssumptions(); diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/GraphKit.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/GraphKit.java index 2e63bcb02928..eb056257bb68 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/GraphKit.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/GraphKit.java @@ -41,6 +41,7 @@ import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.graph.Graph; +import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.Node.ValueNumberable; import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.compiler.java.FrameStateBuilder; @@ -153,9 +154,9 @@ public T add(T node) { return graph.add(changeToWord(node)); } - public T changeToWord(T node) { - if (wordTypes != null && wordTypes.isWord(node)) { - node.setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(node))); + public T changeToWord(T node) { + if (node instanceof ValueNode valueNode && wordTypes != null && wordTypes.isWord(valueNode)) { + valueNode.setStamp(wordTypes.getWordStamp(StampTool.typeOrNull(valueNode))); } return node; } @@ -170,7 +171,7 @@ public final JavaKind asKind(JavaType type) { } @Override - public T append(T node) { + public T append(T node) { if (node.graph() != null) { return node; } diff --git a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/IntrinsicGraphBuilder.java b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/IntrinsicGraphBuilder.java index 57ac9c7003b7..c88c5c85b453 100644 --- a/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/IntrinsicGraphBuilder.java +++ b/compiler/src/jdk.internal.vm.compiler/src/org/graalvm/compiler/replacements/IntrinsicGraphBuilder.java @@ -34,6 +34,7 @@ import org.graalvm.compiler.debug.DebugCloseable; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.GraalError; +import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeSourcePosition; import org.graalvm.compiler.java.FrameStateBuilder; import org.graalvm.compiler.nodes.AbstractBeginNode; @@ -163,7 +164,7 @@ protected IntrinsicGraphBuilder(OptionValues options, } } - private void updateLastInstruction(T v) { + private void updateLastInstruction(T v) { if (v instanceof FixedNode) { FixedNode fixedNode = (FixedNode) v; if (lastInstr != null) { @@ -222,7 +223,7 @@ protected void mergeUnwinds() { } @Override - public T append(T v) { + public T append(T v) { if (v.graph() != null) { return v; } 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 a67824f9a862..ffa6bfa013cd 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 @@ -94,6 +94,7 @@ import org.graalvm.compiler.nodes.UnwindNode; import org.graalvm.compiler.nodes.ValueNode; import org.graalvm.compiler.nodes.WithExceptionNode; +import org.graalvm.compiler.nodes.calc.FloatingNode; import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; import org.graalvm.compiler.nodes.extended.AnchoringNode; import org.graalvm.compiler.nodes.extended.BytecodeExceptionNode; @@ -421,7 +422,7 @@ public IntrinsicContext getIntrinsic() { } @Override - public T append(T value) { + public T append(T value) { throw unimplementedOverride(); // ExcludeFromJacocoGeneratedReport } @@ -550,7 +551,7 @@ public void setStateAfter(StateSplit stateSplit) { @SuppressWarnings("try") @Override - public T append(T v) { + public T append(T v) { if (v.graph() != null) { return v; } @@ -563,6 +564,21 @@ public T append(T v) { } } + @Override + public Node canonicalizeAndAdd(Node node) { + Node canonicalized = node; + if (canonicalized instanceof FixedNode fixedNode) { + canonicalized = canonicalizeFixedNode(methodScope, null, fixedNode); + } else if (canonicalized instanceof FloatingNode floatingNode) { + canonicalized = handleFloatingNodeBeforeAdd(methodScope, null, floatingNode); + } + + if (canonicalized == null) { + return null; + } + return super.canonicalizeAndAdd(canonicalized); + } + private DebugCloseable withNodeSourcePosition() { if (getGraph().trackNodeSourcePosition()) { NodeSourcePosition callerBytecodePosition = methodScope.getCallerNodeSourcePosition(); @@ -573,7 +589,7 @@ private DebugCloseable withNodeSourcePosition() { return null; } - private void updateLastInstruction(T v) { + private void updateLastInstruction(T v) { if (v instanceof FixedNode) { FixedNode fixedNode = (FixedNode) v; if (lastInstr != null) { @@ -721,7 +737,7 @@ public void setStateAfter(StateSplit sideEffect) { @SuppressWarnings("try") @Override - public T append(T v) { + public T append(T v) { if (v.graph() != null) { return v; } @@ -744,7 +760,7 @@ private DebugCloseable withNodeSourcePosition() { return null; } - private void updateLastInstruction(T value) { + private void updateLastInstruction(T value) { if (value instanceof FixedWithNextNode) { FixedWithNextNode fixed = (FixedWithNextNode) value; graph.addBeforeFixed(insertBefore, fixed); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateClassInitializationPlugin.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateClassInitializationPlugin.java index f32431a9b61e..205622fb01ed 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateClassInitializationPlugin.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SubstrateClassInitializationPlugin.java @@ -78,6 +78,6 @@ public boolean apply(GraphBuilderContext builder, ResolvedJavaType type, Supplie private static void emitEnsureClassInitialized(GraphBuilderContext builder, JavaConstant hubConstant, FrameState frameState) { ValueNode hub = ConstantNode.forConstant(hubConstant, builder.getMetaAccess(), builder.getGraph()); EnsureClassInitializedNode node = new EnsureClassInitializedNode(hub, frameState); - builder.add(node); + builder.canonicalizeAndAdd(node); } } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java index aa5248001e2f..ef00d6926740 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java @@ -425,10 +425,22 @@ class ReflectionMustBeSafeEarly { m1 = c1Local.getDeclaredMethod("foo", int.class); f2 = c2Local.getDeclaredField("field"); + /* + * Check that reflective class lookup and the elimination of the class initialization + * check also works when the class name is not constant yet during bytecode parsing. + */ + if (c1Local != Class.forName(forNameMustBeSafeEarly(), true, ReflectionMustBeSafeEarly.class.getClassLoader())) { + throw new Error("wrong class"); + } + } catch (ReflectiveOperationException ex) { throw new Error(ex); } } + + private static String forNameMustBeSafeEarly() { + return "com.oracle.svm.test.clinit.ForNameMustBeSafeEarly"; + } } @SuppressWarnings("unused")