From c521f01a1fece322d65eff76e601bf8a42382094 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Thu, 30 Jun 2022 12:25:46 -0700 Subject: [PATCH] Review fixes --- .../svm/hosted/NativeImageGenerator.java | 22 ++++-- ...ostedUsagesClassInitializationSupport.java | 72 ++++++++----------- .../ClassInitializationSupport.java | 7 -- .../ProvenSafeClassInitializationSupport.java | 7 ++ 4 files changed, 53 insertions(+), 55 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index d43a32bcd44d5..72152de48cd03 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -31,6 +31,7 @@ import static org.graalvm.compiler.replacements.StandardGraphBuilderPlugins.registerInvocationPlugins; import java.io.IOException; +import java.lang.annotation.Annotation; import java.lang.ref.Reference; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -292,6 +293,7 @@ import com.oracle.svm.hosted.substitute.DeletedFieldsPlugin; import com.oracle.svm.hosted.substitute.UnsafeAutomaticSubstitutionProcessor; import com.oracle.svm.util.AnnotationExtracter; +import com.oracle.svm.util.ClassUtil; import com.oracle.svm.util.ImageBuildStatistics; import com.oracle.svm.util.ReflectionUtil; import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError; @@ -1602,7 +1604,7 @@ private void processNativeLibraryImports(NativeLibraries nativeLibs, MetaAccessP for (Method method : loader.findAnnotatedMethods(CConstant.class)) { if (LibCBase.isMethodProvidedInCurrentLibc(method)) { - classInitializationSupport.initializeAtBuildTime(method.getDeclaringClass(), "classes with " + CConstant.class.getSimpleName() + " annotations are always initialized"); + initializeAtBuildTime(method.getDeclaringClass(), classInitializationSupport, CConstant.class); nativeLibs.loadJavaMethod(metaAccess.lookupJavaMethod(method)); } } @@ -1613,38 +1615,38 @@ private void processNativeLibraryImports(NativeLibraries nativeLibs, MetaAccessP } for (Class clazz : loader.findAnnotatedClasses(CStruct.class, false)) { if (LibCBase.isTypeProvidedInCurrentLibc(clazz)) { - classInitializationSupport.initializeAtBuildTime(clazz, "classes annotated with " + CStruct.class.getSimpleName() + " are always initialized"); + initializeAtBuildTime(clazz, classInitializationSupport, CStruct.class); nativeLibs.loadJavaType(metaAccess.lookupJavaType(clazz)); } } for (Class clazz : loader.findAnnotatedClasses(RawStructure.class, false)) { if (LibCBase.isTypeProvidedInCurrentLibc(clazz)) { - classInitializationSupport.initializeAtBuildTime(clazz, "classes annotated with " + RawStructure.class.getSimpleName() + " are always initialized"); + initializeAtBuildTime(clazz, classInitializationSupport, RawStructure.class); nativeLibs.loadJavaType(metaAccess.lookupJavaType(clazz)); } } for (Class clazz : loader.findAnnotatedClasses(CPointerTo.class, false)) { if (LibCBase.isTypeProvidedInCurrentLibc(clazz)) { - classInitializationSupport.initializeAtBuildTime(clazz, "classes annotated with " + CPointerTo.class.getSimpleName() + " are always initialized"); + initializeAtBuildTime(clazz, classInitializationSupport, CPointerTo.class); nativeLibs.loadJavaType(metaAccess.lookupJavaType(clazz)); } } for (Class clazz : loader.findAnnotatedClasses(RawPointerTo.class, false)) { if (LibCBase.isTypeProvidedInCurrentLibc(clazz)) { - classInitializationSupport.initializeAtBuildTime(clazz, "classes annotated with " + RawPointerTo.class.getSimpleName() + " are always initialized"); + initializeAtBuildTime(clazz, classInitializationSupport, RawPointerTo.class); nativeLibs.loadJavaType(metaAccess.lookupJavaType(clazz)); } } for (Class clazz : loader.findAnnotatedClasses(CEnum.class, false)) { if (LibCBase.isTypeProvidedInCurrentLibc(clazz)) { ResolvedJavaType type = metaAccess.lookupJavaType(clazz); - classInitializationSupport.initializeAtBuildTime(clazz, "classes annotated with " + CEnum.class.getSimpleName() + " are always initialized"); + initializeAtBuildTime(clazz, classInitializationSupport, CEnum.class); nativeLibs.loadJavaType(type); } } for (Class clazz : loader.findAnnotatedClasses(CContext.class, false)) { if (LibCBase.isTypeProvidedInCurrentLibc(clazz)) { - classInitializationSupport.initializeAtBuildTime(clazz, "classes annotated with " + CContext.class.getSimpleName() + " are always initialized"); + initializeAtBuildTime(clazz, classInitializationSupport, CContext.class); } } nativeLibs.processCLibraryAnnotations(loader); @@ -1653,6 +1655,12 @@ private void processNativeLibraryImports(NativeLibraries nativeLibs, MetaAccessP nativeLibs.reportErrors(); } + private static void initializeAtBuildTime(Class clazz, ClassInitializationSupport classInitializationSupport, Class annotationForMessage) { + String message = "classes annotated with " + ClassUtil.getUnqualifiedName(annotationForMessage) + " are always initialized at image build time"; + classInitializationSupport.initializeAtBuildTime(clazz, message); + classInitializationSupport.forceInitializeHosted(clazz, message, false); + } + public AbstractImage getBuiltImage() { return image; } 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 bff048b511b4d..2ccb82b105450 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 @@ -34,7 +34,6 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ImageClassLoader; -import jdk.internal.misc.Unsafe; import jdk.vm.ci.meta.MetaAccessProvider; class AllowAllHostedUsagesClassInitializationSupport extends ClassInitializationSupport { @@ -43,23 +42,42 @@ class AllowAllHostedUsagesClassInitializationSupport extends ClassInitialization super(metaAccess, loader); } + @Override + public void initializeAtBuildTime(Class aClass, String reason) { + UserError.guarantee(!configurationSealed, "The class initialization configuration can be changed only before the phase analysis."); + Class cur = aClass; + do { + classInitializationConfiguration.insert(cur.getTypeName(), InitKind.BUILD_TIME, cur == aClass ? reason : "super type of " + aClass.getTypeName(), true); + initializeInterfacesAtBuildTime(cur.getInterfaces(), "interface of " + aClass.getTypeName()); + cur = cur.getSuperclass(); + } while (cur != null); + } + + private void initializeInterfacesAtBuildTime(Class[] interfaces, String reason) { + for (Class iface : interfaces) { + if (metaAccess.lookupJavaType(iface).declaresDefaultMethods()) { + classInitializationConfiguration.insert(iface.getTypeName(), InitKind.BUILD_TIME, reason, true); + } + initializeInterfacesAtBuildTime(iface.getInterfaces(), reason); + } + } + @Override public void initializeAtRunTime(Class clazz, String reason) { UserError.guarantee(!configurationSealed, "The class initialization configuration can be changed only before the phase analysis."); classInitializationConfiguration.insert(clazz.getTypeName(), InitKind.RUN_TIME, reason, true); - setSubclassesAsRunTime(clazz); } @Override public void rerunInitialization(Class clazz, String reason) { - UserError.guarantee(!configurationSealed, "The class initialization configuration can be changed only before the phase analysis."); - classInitializationConfiguration.insert(clazz.getTypeName(), InitKind.RERUN, reason, true); + /* There is no more difference between RUN_TIME and RERUN. */ + initializeAtRunTime(clazz, reason); + } - try { - Unsafe.getUnsafe().ensureClassInitialized(clazz); - } catch (Throwable ex) { - throw UserError.abort(ex, "Class initialization failed for %s. The class is requested for re-running (reason: %s)", clazz.getTypeName(), reason); - } + @Override + public void rerunInitialization(String name, String reason) { + /* There is no more difference between RUN_TIME and RERUN. */ + initializeAtRunTime(name, reason); } @Override @@ -75,21 +93,6 @@ String reasonForClass(Class clazz) { } } - private void setSubclassesAsRunTime(Class clazz) { - if (clazz.isInterface() && !metaAccess.lookupJavaType(clazz).declaresDefaultMethods()) { - /* - * An interface that does not declare a default method is independent from a class - * initialization point of view, i.e., it is not initialized when a class implementing - * that interface is initialized. - */ - return; - } - loader.findSubclasses(clazz, false).stream() - .filter(c -> !c.equals(clazz)) - .filter(c -> !(c.isInterface() && !metaAccess.lookupJavaType(c).declaresDefaultMethods())) - .forEach(c -> classInitializationConfiguration.insert(c.getTypeName(), InitKind.RUN_TIME, "subtype of " + clazz.getTypeName(), true)); - } - @Override public void forceInitializeHosted(Class clazz, String reason, boolean allowInitializationErrors) { if (clazz == null) { @@ -149,11 +152,6 @@ InitKind computeInitKindAndMaybeInitializeClass(Class clazz, boolean memoize) return InitKind.BUILD_TIME; } - if (clazz.getTypeName().contains("$$StringConcat")) { - forceInitializeHosted(clazz, "string concatenation classes are initialized at build time", false); - return InitKind.BUILD_TIME; - } - InitKind specifiedInitKind = specifiedInitKindFor(clazz); InitKind clazzResult = specifiedInitKind != null ? specifiedInitKind : InitKind.RUN_TIME; @@ -175,18 +173,10 @@ InitKind computeInitKindAndMaybeInitializeClass(Class clazz, boolean memoize) result = result.max(ensureClassInitialized(clazz, false)); } - /* - * Unfortunately, the computation of canInitializeWithoutSideEffects is not completely - * deterministic: Consider a class A whose class initializer depends on class B. Assume - * class B has no other dependencies and can therefore be initialized at build time. - * When class A is analyzed after class B has been initialized, it can also be - * initialized at build time. But when class A is analyzed before class B has been - * initialized, it cannot. Since two threads can analyze class A at the same time (there - * is no per-class locking) and another thread can initialize B at the same time, we can - * have a conflicting initialization status. In that case, BUILD_TIME must win over - * RUN_TIME because one thread has already initialized class A. - */ - result = classInitKinds.merge(clazz, result, InitKind::min); + InitKind previous = classInitKinds.putIfAbsent(clazz, result); + if (previous != null && previous != result) { + throw VMError.shouldNotReachHere("Conflicting class initialization kind: " + previous + " != " + result + " for " + clazz); + } } return result; } 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 853ad65a29dad..60f3ca7ee9ddc 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 @@ -318,13 +318,6 @@ static String getTraceString(StackTraceElement[] trace) { return b.toString(); } - @Override - public void initializeAtBuildTime(Class aClass, String reason) { - UserError.guarantee(!configurationSealed, "The class initialization configuration can be changed only before the phase analysis."); - classInitializationConfiguration.insert(aClass.getTypeName(), InitKind.BUILD_TIME, reason, true); - forceInitializeHosted(aClass, reason, false); - } - @Override public void reportClassInitialized(Class clazz, StackTraceElement[] stackTrace) { assert TraceClassInitialization.hasBeenSet(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ProvenSafeClassInitializationSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ProvenSafeClassInitializationSupport.java index 73f0151cae0d1..d2e5e6f86948a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ProvenSafeClassInitializationSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ProvenSafeClassInitializationSupport.java @@ -188,6 +188,13 @@ public void rerunInitialization(Class clazz, String reason) { } } + @Override + public void initializeAtBuildTime(Class aClass, String reason) { + UserError.guarantee(!configurationSealed, "The class initialization configuration can be changed only before the phase analysis."); + classInitializationConfiguration.insert(aClass.getTypeName(), InitKind.BUILD_TIME, reason, true); + forceInitializeHosted(aClass, reason, false); + } + private void setSubclassesAsRunTime(Class clazz) { if (clazz.isInterface() && !metaAccess.lookupJavaType(clazz).declaresDefaultMethods()) { /*