From 9b546961950fba0e9908a2144f1c610f054cff56 Mon Sep 17 00:00:00 2001 From: Vojin Jovanovic Date: Thu, 23 May 2024 21:09:22 +0200 Subject: [PATCH 1/4] Type reached for serialization --- .../ClassInitializationInfo.java | 4 +- .../com/oracle/svm/core/hub/DynamicHub.java | 16 +-- .../core/hub/PredefinedClassesSupport.java | 2 +- .../serialize/SerializationRegistry.java | 3 +- .../serialize/SerializationSupport.java | 39 +++--- .../ConditionalConfigurationRegistry.java | 10 +- .../heap/SVMImageLayerSnapshotUtil.java | 5 +- .../svm/hosted/meta/UniverseBuilder.java | 6 +- .../serialize/SerializationFeature.java | 111 ++++++++++-------- 9 files changed, 106 insertions(+), 90 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/classinitialization/ClassInitializationInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/classinitialization/ClassInitializationInfo.java index 48282e9b6ffd..ecc1daad4c43 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/classinitialization/ClassInitializationInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/classinitialization/ClassInitializationInfo.java @@ -188,8 +188,8 @@ interface ClassInitializerFunctionPointer extends CFunctionPointer { private boolean hasInitializer; private boolean buildTimeInitialized; - public boolean isTypeReached() { - assert typeReached != TypeReached.UNTRACKED : "We should never emit a check for untracked types as this was known at build time"; + public boolean isTypeReached(DynamicHub caller) { + assert typeReached != TypeReached.UNTRACKED : "We should never emit a check for untracked types as this was known at build time: " + caller.getName(); return typeReached == TypeReached.REACHED; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index bd02076f9466..cb125bbdfe9a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -82,12 +82,12 @@ import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.nativeimage.impl.InternalPlatform; -import com.oracle.svm.core.RuntimeAssertionsSupport; -import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.BuildPhaseProvider.AfterCompilation; import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse; import com.oracle.svm.core.BuildPhaseProvider.CompileQueueFinished; +import com.oracle.svm.core.RuntimeAssertionsSupport; +import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.InjectAccessors; @@ -114,6 +114,7 @@ import com.oracle.svm.core.reflect.RuntimeMetadataDecoder.FieldDescriptor; import com.oracle.svm.core.reflect.RuntimeMetadataDecoder.MethodDescriptor; import com.oracle.svm.core.reflect.fieldaccessor.UnsafeFieldAccessorFactory; +import com.oracle.svm.core.reflect.serialize.SerializationRegistry; import com.oracle.svm.core.reflect.target.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.LazyFinalReference; import com.oracle.svm.core.util.VMError; @@ -492,7 +493,7 @@ public void setClassInitializationInfo(ClassInitializationInfo classInitializati @Platforms(Platform.HOSTED_ONLY.class) public void setSharedData(int layoutEncoding, int monitorOffset, int identityHashOffset, long referenceMapIndex, - boolean isInstantiated, boolean canUnsafeInstantiateAsInstance, boolean isRegisteredForSerialization) { + boolean isInstantiated, boolean canUnsafeInstantiateAsInstance) { assert !(!isInstantiated && canUnsafeInstantiateAsInstance); VMError.guarantee(monitorOffset == (char) monitorOffset, "Class %s has an invalid monitor field offset. Most likely, its objects are larger than supported.", name); VMError.guarantee(identityHashOffset == (char) identityHashOffset, "Class %s has an invalid identity hash code field offset. Most likely, its objects are larger than supported.", name); @@ -506,8 +507,7 @@ public void setSharedData(int layoutEncoding, int monitorOffset, int identityHas } this.referenceMapIndex = (int) referenceMapIndex; this.additionalFlags = NumUtil.safeToUByte(makeFlag(IS_INSTANTIATED_BIT, isInstantiated) | - makeFlag(CAN_UNSAFE_INSTANTIATE_AS_INSTANCE_BIT, canUnsafeInstantiateAsInstance) | - makeFlag(IS_REGISTERED_FOR_SERIALIZATION, isRegisteredForSerialization)); + makeFlag(CAN_UNSAFE_INSTANTIATE_AS_INSTANCE_BIT, canUnsafeInstantiateAsInstance)); } @Platforms(Platform.HOSTED_ONLY.class) @@ -983,7 +983,7 @@ public boolean isLinked() { } public boolean isRegisteredForSerialization() { - return isFlagSet(additionalFlags, IS_REGISTERED_FOR_SERIALIZATION); + return ImageSingletons.lookup(SerializationRegistry.class).isRegisteredForSerialization(DynamicHub.toClass(this)); } @KeepOriginal @@ -1951,7 +1951,7 @@ public Object getJfrEventConfiguration() { } public boolean isReached() { - return classInitializationInfo.isTypeReached(); + return classInitializationInfo.isTypeReached(this); } private static class ReflectionDataAccessors { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java index 116a1ee6c960..7914ae10520f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java @@ -151,7 +151,7 @@ private static void registerLambdaForReflection(Class lambdaClass) { * lambda-class information from the capturing class. */ if (Serializable.class.isAssignableFrom(lambdaClass) && - SerializationSupport.isLambdaCapturingClassRegistered(LambdaUtils.capturingClass(lambdaClass.getName()))) { + SerializationSupport.singleton().isLambdaCapturingClassRegistered(LambdaUtils.capturingClass(lambdaClass.getName()))) { try { Method serializeLambdaMethod = lambdaClass.getDeclaredMethod("writeReplace"); RuntimeReflection.register(serializeLambdaMethod); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationRegistry.java index 6547a178e55b..b991d397b1e4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationRegistry.java @@ -27,8 +27,7 @@ public interface SerializationRegistry { - boolean isRegisteredForSerialization(Class cl); - Object getSerializationConstructorAccessor(Class serializationTargetClass, Class targetConstructorClass); + boolean isRegisteredForSerialization(Class aClass); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java index cb9b1ef822de..3f673afaf549 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java @@ -31,15 +31,18 @@ import java.lang.invoke.SerializedLambda; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; +import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.impl.ConfigurationCondition; +import com.oracle.svm.core.configure.RuntimeConditionSet; import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.core.util.VMError; @@ -47,6 +50,10 @@ public class SerializationSupport implements SerializationRegistry { + public static SerializationSupport singleton() { + return (SerializationSupport) ImageSingletons.lookup(SerializationRegistry.class); + } + /** * Method MethodAccessorGenerator.generateSerializationConstructor dynamically defines a * SerializationConstructorAccessorImpl type class. The class has a newInstance method which @@ -150,28 +157,24 @@ public SerializationLookupKey getKeyFromConstructorAccessorClass(Class constr return null; } - @Platforms(Platform.HOSTED_ONLY.class) private final Set> classes = ConcurrentHashMap.newKeySet(); - @Platforms(Platform.HOSTED_ONLY.class) private static final Set lambdaCapturingClasses = ConcurrentHashMap.newKeySet(); + private final Map, RuntimeConditionSet> classes = new ConcurrentHashMap<>(); + private final Map lambdaCapturingClasses = new ConcurrentHashMap<>(); @Platforms(Platform.HOSTED_ONLY.class) - public void registerSerializationTargetClass(Class serializationTargetClass) { - classes.add(serializationTargetClass); + public void registerSerializationTargetClass(ConfigurationCondition cnd, Class serializationTargetClass) { + classes.computeIfAbsent(serializationTargetClass, k -> RuntimeConditionSet.createHosted()) + .addCondition(cnd); } @Platforms(Platform.HOSTED_ONLY.class) - public static void registerLambdaCapturingClass(String lambdaCapturingClass) { - lambdaCapturingClasses.add(lambdaCapturingClass); + public void registerLambdaCapturingClass(ConfigurationCondition cnd, String lambdaCapturingClass) { + lambdaCapturingClasses.computeIfAbsent(lambdaCapturingClass, k -> RuntimeConditionSet.createHosted()) + .addCondition(cnd); } @Platforms(Platform.HOSTED_ONLY.class) - public static boolean isLambdaCapturingClassRegistered(String lambdaCapturingClass) { - return lambdaCapturingClasses.contains(lambdaCapturingClass); - } - - @Override - @Platforms(Platform.HOSTED_ONLY.class) - public boolean isRegisteredForSerialization(Class cl) { - return classes.contains(cl); + public boolean isLambdaCapturingClassRegistered(String lambdaCapturingClass) { + return lambdaCapturingClasses.containsKey(lambdaCapturingClass); } @Override @@ -200,4 +203,10 @@ public Object getSerializationConstructorAccessor(Class rawDeclaringClass, Cl return null; } } + + @Override + public boolean isRegisteredForSerialization(Class jClass) { + var conditionSet = classes.get(jClass); + return conditionSet != null && conditionSet.satisfied(); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java index c116a3857ba5..dbb6ec457015 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java @@ -34,6 +34,8 @@ import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.impl.ConfigurationCondition; +import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; + public abstract class ConditionalConfigurationRegistry { private final Map, Collection> pendingReachabilityHandlers = new ConcurrentHashMap<>(); @@ -46,7 +48,13 @@ protected void registerConditionalConfiguration(ConfigurationCondition condition } else { Collection handlers = pendingReachabilityHandlers.computeIfAbsent(condition.getType(), key -> new ConcurrentLinkedQueue<>()); ConfigurationCondition runtimeCondition; - runtimeCondition = condition.isRuntimeChecked() ? condition : ConfigurationCondition.alwaysTrue(); + if (condition.isRuntimeChecked()) { + ClassInitializationSupport.singleton().addForTypeReachedTracking(condition.getType()); + runtimeCondition = condition; + } else { + runtimeCondition = ConfigurationCondition.alwaysTrue(); + } + handlers.add(() -> consumer.accept(runtimeCondition)); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java index ffa154d68ca6..060556a3c57b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java @@ -36,15 +36,12 @@ import java.util.Set; import java.util.stream.Collectors; -import org.graalvm.nativeimage.ImageSingletons; - import com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.reflect.serialize.SerializationRegistry; import com.oracle.svm.core.reflect.serialize.SerializationSupport; import com.oracle.svm.hosted.code.FactoryMethod; import com.oracle.svm.hosted.code.IncompatibleClassChangeFallbackMethod; @@ -121,7 +118,7 @@ public String getMethodIdentifier(AnalysisMethod method) { */ private static String getGeneratedSerializationName(AnalysisType type) { Class constructorAccessor = type.getJavaClass(); - SerializationSupport serializationRegistry = (SerializationSupport) ImageSingletons.lookup(SerializationRegistry.class); + SerializationSupport serializationRegistry = SerializationSupport.singleton(); SerializationSupport.SerializationLookupKey serializationLookupKey = serializationRegistry.getKeyFromConstructorAccessorClass(constructorAccessor); return generatedSerializationClassName(serializationLookupKey); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index 1fca1967bab0..c2250248238a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -44,7 +44,6 @@ import java.util.function.Function; import java.util.stream.Collectors; -import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.CEntryPointLiteral; import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.function.CFunctionPointer; @@ -86,7 +85,6 @@ import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.reflect.SubstrateConstructorAccessor; import com.oracle.svm.core.reflect.SubstrateMethodAccessor; -import com.oracle.svm.core.reflect.serialize.SerializationRegistry; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; import com.oracle.svm.hosted.HostedConfiguration; @@ -908,10 +906,8 @@ private void buildHubs() { long referenceMapIndex = referenceMapEncoder.lookupEncoding(referenceMap); DynamicHub hub = type.getHub(); - SerializationRegistry s = ImageSingletons.lookup(SerializationRegistry.class); hub.setSharedData(layoutHelper, monitorOffset, identityHashOffset, - referenceMapIndex, type.isInstantiated(), canUnsafeInstantiateAsInstance, - s.isRegisteredForSerialization(type.getJavaClass())); + referenceMapIndex, type.isInstantiated(), canUnsafeInstantiateAsInstance); if (SubstrateOptions.closedTypeWorld()) { CFunctionPointer[] vtable = new CFunctionPointer[type.closedTypeWorldVTable.length]; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java index 724952f45132..045e3ea3436d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java @@ -33,6 +33,7 @@ import java.io.Serializable; import java.lang.invoke.SerializedLambda; import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; @@ -213,7 +214,7 @@ private static void registerLambdasFromConstantNodesInGraph(StructuredGraph grap if (lambdaClass != null && Serializable.class.isAssignableFrom(lambdaClass)) { RuntimeReflection.register(ReflectionUtil.lookupMethod(lambdaClass, "writeReplace")); SerializationBuilder.registerSerializationUIDElements(lambdaClass, false); - serializationBuilder.serializationSupport.registerSerializationTargetClass(lambdaClass); + serializationBuilder.serializationSupport.registerSerializationTargetClass(ConfigurationCondition.alwaysTrue(), lambdaClass); } } } @@ -479,7 +480,7 @@ public void registerLambdaCapturingClass(ConfigurationCondition condition, Strin ImageSingletons.lookup(SerializationFeature.class).capturingClasses.add(lambdaCapturingClass); RuntimeReflection.register(lambdaCapturingClass); RuntimeReflection.register(ReflectionUtil.lookupMethod(lambdaCapturingClass, "$deserializeLambda$", SerializedLambda.class)); - SerializationSupport.registerLambdaCapturingClass(lambdaCapturingClassName); + SerializationSupport.singleton().registerLambdaCapturingClass(cnd, lambdaCapturingClassName); }); } @@ -487,7 +488,7 @@ public void registerLambdaCapturingClass(ConfigurationCondition condition, Strin public void registerProxyClass(ConfigurationCondition condition, List implementedInterfaces) { registerConditionalConfiguration(condition, (cnd) -> { Class proxyClass = proxyRegistry.createProxyClassForSerialization(implementedInterfaces); - registerWithTargetConstructorClass(ConfigurationCondition.alwaysTrue(), proxyClass, Object.class); + registerWithTargetConstructorClass(cnd, proxyClass, Object.class); }); } @@ -515,47 +516,50 @@ public void registerWithTargetConstructorClass(ConfigurationCondition condition, @Override public void registerWithTargetConstructorClass(ConfigurationCondition condition, Class serializationTargetClass, Class customTargetConstructorClass) { abortIfSealed(); - /* - * Register class for reflection as it is needed when the class-value itself is serialized. - */ - ImageSingletons.lookup(RuntimeReflectionSupport.class).register(condition, serializationTargetClass); - - if (!Serializable.class.isAssignableFrom(serializationTargetClass)) { - return; - } - /* - * Making this class reachable as it will end up in the image heap without the analysis - * knowing. - */ - RuntimeReflection.register(java.io.ObjectOutputStream.class); + registerConditionalConfiguration(condition, (cnd) -> { + /* + * Register class for reflection as it is needed when the class-value itself is + * serialized. + */ + ImageSingletons.lookup(RuntimeReflectionSupport.class).register(condition, serializationTargetClass); - if (denyRegistry.isAllowed(serializationTargetClass)) { - if (customTargetConstructorClass != null) { - if (!customTargetConstructorClass.isAssignableFrom(serializationTargetClass)) { - LogUtils.warning("The given customTargetConstructorClass %s is not a superclass of the serialization target %s.", customTargetConstructorClass.getName(), serializationTargetClass); - return; - } - if (ReflectionUtil.lookupConstructor(true, customTargetConstructorClass) == null) { - LogUtils.warning("The given customTargetConstructorClass %s does not declare a parameterless constructor.", customTargetConstructorClass.getName()); - return; - } + if (!Serializable.class.isAssignableFrom(serializationTargetClass)) { + return; } - registerConditionalConfiguration(condition, (cnd) -> { - Optional.ofNullable(addConstructorAccessor(serializationTargetClass, customTargetConstructorClass)) + /* + * Making this class reachable as it will end up in the image heap without the analysis + * knowing. + */ + RuntimeReflection.register(java.io.ObjectOutputStream.class); + + if (denyRegistry.isAllowed(serializationTargetClass)) { + if (customTargetConstructorClass != null) { + if (!customTargetConstructorClass.isAssignableFrom(serializationTargetClass)) { + LogUtils.warning("The given customTargetConstructorClass %s is not a superclass of the serialization target %s.", customTargetConstructorClass.getName(), + serializationTargetClass); + return; + } + if (ReflectionUtil.lookupConstructor(true, customTargetConstructorClass) == null) { + LogUtils.warning("The given customTargetConstructorClass %s does not declare a parameterless constructor.", customTargetConstructorClass.getName()); + return; + } + } + Optional.ofNullable(addConstructorAccessor(cnd, serializationTargetClass, customTargetConstructorClass)) .map(ReflectionUtil::lookupConstructor) - .ifPresent(RuntimeReflection::register); + .ifPresent(methods -> ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationCondition.alwaysTrue(), false, methods)); Class superclass = serializationTargetClass.getSuperclass(); if (superclass != null) { - RuntimeReflection.registerAllDeclaredConstructors(superclass); - RuntimeReflection.registerMethodLookup(superclass, "writeReplace"); - RuntimeReflection.registerMethodLookup(superclass, "readResolve"); + ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredConstructorsQuery(ConfigurationCondition.alwaysTrue(), true, superclass); + ImageSingletons.lookup(RuntimeReflectionSupport.class).registerMethodLookup(ConfigurationCondition.alwaysTrue(), superclass, "writeReplace"); + ImageSingletons.lookup(RuntimeReflectionSupport.class).registerMethodLookup(ConfigurationCondition.alwaysTrue(), superclass, "readResolve"); } - registerForSerialization(serializationTargetClass); - registerForDeserialization(serializationTargetClass); - }); - } + registerForSerialization(cnd, serializationTargetClass); + registerForDeserialization(cnd, serializationTargetClass); + + } + }); } private static void registerQueriesForInheritableMethod(Class clazz, String methodName, Class... args) { @@ -571,16 +575,16 @@ private static void registerQueriesForInheritableMethod(Class clazz, String m } } - private static void registerMethod(Class clazz, String methodName, Class... args) { + private static void registerMethod(ConfigurationCondition cnd, Class clazz, String methodName, Class... args) { Method method = ReflectionUtil.lookupMethod(true, clazz, methodName, args); if (method != null) { - RuntimeReflection.register(method); + ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, method); } else { RuntimeReflection.registerMethodLookup(clazz, methodName, args); } } - private void registerForSerialization(Class serializationTargetClass) { + private void registerForSerialization(ConfigurationCondition cnd, Class serializationTargetClass) { if (Serializable.class.isAssignableFrom(serializationTargetClass)) { /* @@ -588,7 +592,7 @@ private void registerForSerialization(Class serializationTargetClass) { * serialization class consistency, so need to register all constructors, methods and * fields. */ - registerSerializationUIDElements(serializationTargetClass, true); + registerSerializationUIDElements(serializationTargetClass, true); // if MRE /* * Required by jdk.internal.reflect.ReflectionFactory.newConstructorForSerialization @@ -628,11 +632,12 @@ private void registerForSerialization(Class serializationTargetClass) { registerQueriesForInheritableMethod(serializationTargetClass, "writeReplace"); registerQueriesForInheritableMethod(serializationTargetClass, "readResolve"); - registerMethod(serializationTargetClass, "writeObject", ObjectOutputStream.class); - registerMethod(serializationTargetClass, "readObjectNoData"); - registerMethod(serializationTargetClass, "readObject", ObjectInputStream.class); + registerMethod(cnd, serializationTargetClass, "writeObject", ObjectOutputStream.class); + registerMethod(cnd, serializationTargetClass, "readObjectNoData"); + registerMethod(cnd, serializationTargetClass, "readObject", ObjectInputStream.class); } + @SuppressWarnings("unused") static void registerSerializationUIDElements(Class serializationTargetClass, boolean fullyRegister) { RuntimeReflection.registerAllDeclaredConstructors(serializationTargetClass); RuntimeReflection.registerAllDeclaredMethods(serializationTargetClass); @@ -650,25 +655,27 @@ public void afterAnalysis() { sealed = true; } - private static void registerForDeserialization(Class serializationTargetClass) { - RuntimeReflection.register(serializationTargetClass); + private static void registerForDeserialization(ConfigurationCondition cnd, Class serializationTargetClass) { + ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, serializationTargetClass); if (serializationTargetClass.isRecord()) { /* Serialization for records uses the canonical record constructor directly. */ - RuntimeReflection.register(RecordUtils.getCanonicalRecordConstructor(serializationTargetClass)); + Executable[] methods = new Executable[]{RecordUtils.getCanonicalRecordConstructor(serializationTargetClass)}; + ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, methods); /* * Serialization for records invokes Class.getRecordComponents(). Registering all record * component accessor methods for reflection ensures that the record components are * available at run time. */ - RuntimeReflection.registerAllRecordComponents(serializationTargetClass); - RuntimeReflection.register(RecordUtils.getRecordComponentAccessorMethods(serializationTargetClass)); + ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllRecordComponentsQuery(cnd, serializationTargetClass); + Executable[] methods1 = RecordUtils.getRecordComponentAccessorMethods(serializationTargetClass); + ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, methods1); } else if (Externalizable.class.isAssignableFrom(serializationTargetClass)) { RuntimeReflection.registerConstructorLookup(serializationTargetClass); } - registerMethod(serializationTargetClass, "readObject", ObjectInputStream.class); - registerMethod(serializationTargetClass, "readResolve"); + registerMethod(cnd, serializationTargetClass, "readObject", ObjectInputStream.class); + registerMethod(cnd, serializationTargetClass, "readResolve"); } private static Constructor newConstructorForSerialization(Class serializationTargetClass, Constructor customConstructorToCall) { @@ -695,8 +702,8 @@ private static Constructor getExternalizableConstructor(Class serializatio } } - Class addConstructorAccessor(Class serializationTargetClass, Class customTargetConstructorClass) { - serializationSupport.registerSerializationTargetClass(serializationTargetClass); + Class addConstructorAccessor(ConfigurationCondition cnd, Class serializationTargetClass, Class customTargetConstructorClass) { + serializationSupport.registerSerializationTargetClass(cnd, serializationTargetClass); if (serializationTargetClass.isArray() || Enum.class.isAssignableFrom(serializationTargetClass)) { return null; } From 180b59caaebe1590bc0dc91ec6969190c9c96999 Mon Sep 17 00:00:00 2001 From: Vojin Jovanovic Date: Fri, 24 May 2024 19:47:30 +0200 Subject: [PATCH 2/4] typeReached for resources --- .../hosted/RuntimeResourceAccess.java | 4 +- .../impl/RuntimeResourceSupport.java | 20 ++++- .../config/ResourceConfigurationTest.java | 8 +- .../config/ResourceConfiguration.java | 8 +- .../oracle/svm/core/ClassLoaderSupport.java | 2 +- .../com/oracle/svm/core/jdk/Resources.java | 86 ++++++++++--------- .../jdk/localization/LocalizationSupport.java | 1 + .../MissingResourceRegistrationUtils.java | 2 +- .../NativeImageResourceFileSystem.java | 9 +- .../NativeImageResourceFileSystemUtil.java | 4 +- .../jdk/resources/ResourceURLConnection.java | 2 +- .../serialize/SerializationSupport.java | 4 +- .../svm/hosted/ClassLoaderSupportImpl.java | 6 +- .../svm/hosted/EmbeddedResourceExporter.java | 5 +- .../svm/hosted/HeapBreakdownProvider.java | 7 +- .../oracle/svm/hosted/ResourcesFeature.java | 41 +++++---- 16 files changed, 124 insertions(+), 85 deletions(-) diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java index 590952b8edf3..bcfe6e015e61 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java @@ -83,8 +83,8 @@ public static void addResource(Module module, String resourcePath, byte[] resour Objects.requireNonNull(module); Objects.requireNonNull(resourcePath); Objects.requireNonNull(resourceContent); - ImageSingletons.lookup(RuntimeResourceSupport.class).injectResource( - module, resourcePath, resourceContent); + ImageSingletons.lookup(RuntimeResourceSupport.class).injectResource(module, resourcePath, resourceContent); + ImageSingletons.lookup(RuntimeResourceSupport.class).addCondition(ConfigurationCondition.alwaysTrue(), module, resourcePath); } /** diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java index 968437f59869..f852d23ba999 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java @@ -54,15 +54,27 @@ static RuntimeResourceSupport singleton() { void addResources(C condition, String pattern); - void addResource(Module module, String resourcePath); - void addGlob(C condition, String module, String glob); - void injectResource(Module module, String resourcePath, byte[] resourceContent); - void ignoreResources(C condition, String pattern); void addResourceBundles(C condition, String name); void addResourceBundles(C condition, String basename, Collection locales); + + /* Following functions are used only from features */ + void addCondition(ConfigurationCondition configurationCondition, Module module, String resourcePath); + + void addResourceEntry(Module module, String resourcePath); + + default void addResource(Module module, String resourcePath) { + addResource(ConfigurationCondition.alwaysTrue(), module, resourcePath); + } + + default void addResource(ConfigurationCondition condition, Module module, String resourcePath) { + addResourceEntry(module, resourcePath); + addCondition(condition, module, resourcePath); + } + + void injectResource(Module module, String resourcePath, byte[] resourceContent); } diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java index 64dc835d313f..542e5de85ebd 100644 --- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Locale; +import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import org.junit.Assert; import org.junit.Test; @@ -102,7 +103,7 @@ public void addGlob(UnresolvedConfigurationCondition condition, String module, S } @Override - public void addResource(Module module, String resourcePath) { + public void addResourceEntry(Module module, String resourcePath) { throw VMError.shouldNotReachHere("Unused function."); } @@ -124,6 +125,11 @@ public void addResourceBundles(UnresolvedConfigurationCondition condition, Strin } + @Override + public void addCondition(ConfigurationCondition configurationCondition, Module module, String resourcePath) { + + } + @Override public void addClassBasedResourceBundle(UnresolvedConfigurationCondition condition, String basename, String className) { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index d9345d3139a3..a69206919deb 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; +import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.ConfigurationBase; @@ -69,7 +70,12 @@ public void addGlob(UnresolvedConfigurationCondition condition, String module, S } @Override - public void addResource(Module module, String resourcePath) { + public void addResourceEntry(Module module, String resourcePath) { + throw VMError.shouldNotReachHere("Unused function."); + } + + @Override + public void addCondition(ConfigurationCondition condition, Module module, String resourcePath) { throw VMError.shouldNotReachHere("Unused function."); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ClassLoaderSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ClassLoaderSupport.java index aa53efe3871a..b86d7b2b99f3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ClassLoaderSupport.java @@ -55,7 +55,7 @@ public boolean isNativeImageClassLoader(ClassLoader classLoader) { public interface ResourceCollector { List isIncluded(Module module, String resourceName, URI resourceURI); - void addResource(Module module, String resourceName); + void addResourceEntry(Module module, String resourceName); void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java index 5acdb00f6632..503996f2991e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java @@ -47,10 +47,13 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.MissingRegistrationUtils; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.configure.ConditionalRuntimeValue; +import com.oracle.svm.core.configure.RuntimeConditionSet; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.jdk.resources.MissingResourceRegistrationError; @@ -90,8 +93,8 @@ public static Resources singleton() { * ends up in the image heap is computed after the runtime module instances have been computed * {see com.oracle.svm.hosted.ModuleLayerFeature}. */ - private final EconomicMap resources = ImageHeapMap.create(); - private final EconomicMap requestedPatterns = ImageHeapMap.create(); + private final EconomicMap> resources = ImageHeapMap.create(); + private final EconomicMap requestedPatterns = ImageHeapMap.create(); public record RequestedPattern(String module, String resource) { } @@ -108,9 +111,9 @@ public record ModuleResourceKey(Module module, String resource) { /** * The object used to detect that the resource is not reachable according to the metadata. It - * can be returned by the {@link Resources#get} method if the resource was not correctly - * specified in the configuration, but we do not want to throw directly (for example when we try - * to check all the modules for a resource). + * can be returned by the {@link Resources#getAtRuntime} method if the resource was not + * correctly specified in the configuration, but we do not want to throw directly (for example + * when we try to check all the modules for a resource). */ private static final ResourceStorageEntryBase MISSING_METADATA_MARKER = new ResourceStorageEntryBase(); @@ -124,11 +127,11 @@ public record ModuleResourceKey(Module module, String resource) { Resources() { } - public EconomicMap getResourceStorage() { + public EconomicMap> getResourceStorage() { return resources; } - public Iterable resources() { + public Iterable> resources() { return resources.getValues(); } @@ -182,17 +185,18 @@ private void addEntry(Module module, String resourceName, boolean isDirectory, b Module m = module != null && module.isNamed() ? module : null; synchronized (resources) { ModuleResourceKey key = createStorageKey(m, resourceName); - ResourceStorageEntryBase entry = resources.get(key); + RuntimeConditionSet conditionSet = RuntimeConditionSet.emptySet(); + ConditionalRuntimeValue entry = resources.get(key); if (isNegativeQuery) { if (entry == null) { - resources.put(key, NEGATIVE_QUERY_MARKER); + resources.put(key, new ConditionalRuntimeValue<>(conditionSet, NEGATIVE_QUERY_MARKER)); } return; } - if (entry == null || entry == NEGATIVE_QUERY_MARKER) { + if (entry == null || entry.getValueUnconditionally() == NEGATIVE_QUERY_MARKER) { updateTimeStamp(); - entry = new ResourceStorageEntry(isDirectory, fromJar); + entry = new ConditionalRuntimeValue<>(conditionSet, new ResourceStorageEntry(isDirectory, fromJar)); resources.put(key, entry); } else { if (key.module() != null) { @@ -201,8 +205,7 @@ private void addEntry(Module module, String resourceName, boolean isDirectory, b return; } } - - entry.addData(data); + entry.getValueUnconditionally().addData(data); } } @@ -243,7 +246,7 @@ public void registerIOException(Module module, String resourceName, IOException ModuleResourceKey key = createStorageKey(module, resourceName); synchronized (resources) { updateTimeStamp(); - resources.put(key, new ResourceExceptionEntry(e)); + resources.put(key, new ConditionalRuntimeValue<>(RuntimeConditionSet.emptySet(), new ResourceExceptionEntry(e))); } } @@ -258,16 +261,11 @@ public void registerNegativeQuery(Module module, String resourceName) { } @Platforms(Platform.HOSTED_ONLY.class) - public void registerIncludePattern(String pattern) { - registerIncludePattern(null, pattern); - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void registerIncludePattern(String module, String pattern) { + public void registerIncludePattern(ConfigurationCondition condition, String module, String pattern) { assert MissingRegistrationUtils.throwMissingRegistrationErrors(); synchronized (requestedPatterns) { updateTimeStamp(); - requestedPatterns.put(new RequestedPattern(module, handleEscapedCharacters(pattern)), Boolean.TRUE); + requestedPatterns.put(new RequestedPattern(module, handleEscapedCharacters(pattern)), RuntimeConditionSet.createHosted(condition)); } } @@ -310,8 +308,8 @@ private static boolean wasAlreadyInCanonicalForm(String resourceName, String can return resourceName.equals(canonicalResourceName) || removeTrailingSlash(resourceName).equals(canonicalResourceName); } - public ResourceStorageEntryBase get(String name, boolean throwOnMissing) { - return get(null, name, throwOnMissing); + public ResourceStorageEntryBase getAtRuntime(String name, boolean throwOnMissing) { + return getAtRuntime(null, name, throwOnMissing); } /** @@ -320,17 +318,18 @@ public ResourceStorageEntryBase get(String name, boolean throwOnMissing) { * {@link MissingResourceRegistrationError}. This is needed because different modules can be * tried on the same resource name, causing an unexpected exception if we throw directly. */ - public ResourceStorageEntryBase get(Module module, String resourceName, boolean throwOnMissing) { + public ResourceStorageEntryBase getAtRuntime(Module module, String resourceName, boolean throwOnMissing) { String canonicalResourceName = toCanonicalForm(resourceName); String moduleName = moduleName(module); - ResourceStorageEntryBase entry = resources.get(createStorageKey(module, canonicalResourceName)); + ConditionalRuntimeValue entry = resources.get(createStorageKey(module, canonicalResourceName)); if (entry == null) { if (MissingRegistrationUtils.throwMissingRegistrationErrors()) { - MapCursor cursor = requestedPatterns.getEntries(); + MapCursor cursor = requestedPatterns.getEntries(); while (cursor.advance()) { RequestedPattern moduleResourcePair = cursor.getKey(); if (Objects.equals(moduleName, moduleResourcePair.module) && - (matchResource(moduleResourcePair.resource, resourceName) || matchResource(moduleResourcePair.resource, canonicalResourceName))) { + ((matchResource(moduleResourcePair.resource, resourceName) || matchResource(moduleResourcePair.resource, canonicalResourceName)) && + cursor.getValue().satisfied())) { return null; } } @@ -348,27 +347,33 @@ public ResourceStorageEntryBase get(Module module, String resourceName, boolean return null; } } - if (entry.isException()) { - throw new RuntimeException(entry.getException()); + if (!entry.getConditions().satisfied()) { + return missingMetadata(resourceName, throwOnMissing); + } + + ResourceStorageEntryBase unconditionalEntry = entry.getValue(); + assert unconditionalEntry != null : "Already checked above that the condition is satisfied"; + if (unconditionalEntry.isException()) { + throw new RuntimeException(unconditionalEntry.getException()); } - if (entry == NEGATIVE_QUERY_MARKER) { + if (unconditionalEntry == NEGATIVE_QUERY_MARKER) { return null; } - if (entry.isFromJar() && !wasAlreadyInCanonicalForm(resourceName, canonicalResourceName)) { + if (unconditionalEntry.isFromJar() && !wasAlreadyInCanonicalForm(resourceName, canonicalResourceName)) { /* * The resource originally came from a jar file, thus behave like ZipFileSystem behaves * for non-canonical paths. */ return null; } - if (!entry.isDirectory() && hasTrailingSlash(resourceName)) { + if (!unconditionalEntry.isDirectory() && hasTrailingSlash(resourceName)) { /* * If this is an actual resource file (not a directory) we do not tolerate a trailing * slash. */ return null; } - return entry; + return unconditionalEntry; } private static ResourceStorageEntryBase missingMetadata(String resourceName, boolean throwOnMissing) { @@ -412,7 +417,7 @@ public InputStream createInputStream(Module module, String resourceName) { return null; } - ResourceStorageEntryBase entry = get(module, resourceName, false); + ResourceStorageEntryBase entry = getAtRuntime(module, resourceName, false); boolean isInMetadata = entry != MISSING_METADATA_MARKER; if (moduleName(module) == null && (entry == MISSING_METADATA_MARKER || entry == null)) { /* @@ -420,7 +425,7 @@ public InputStream createInputStream(Module module, String resourceName) { * classpath-resource we have to search for the resource in all modules in the image. */ for (Module m : RuntimeModuleSupport.instance().getBootLayer().modules()) { - entry = get(m, resourceName, false); + entry = getAtRuntime(m, resourceName, false); if (entry != MISSING_METADATA_MARKER) { isInMetadata = true; } @@ -458,7 +463,7 @@ public Enumeration createURLs(Module module, String resourceName) { /* If moduleName was unspecified we have to consider all modules in the image */ if (moduleName(module) == null) { for (Module m : RuntimeModuleSupport.instance().getBootLayer().modules()) { - ResourceStorageEntryBase entry = get(m, resourceName, false); + ResourceStorageEntryBase entry = getAtRuntime(m, resourceName, false); if (entry == MISSING_METADATA_MARKER) { continue; } @@ -466,7 +471,7 @@ public Enumeration createURLs(Module module, String resourceName) { addURLEntries(resourcesURLs, (ResourceStorageEntry) entry, m, shouldAppendTrailingSlash ? canonicalResourceName + '/' : canonicalResourceName); } } - ResourceStorageEntryBase explicitEntry = get(module, resourceName, false); + ResourceStorageEntryBase explicitEntry = getAtRuntime(module, resourceName, false); if (explicitEntry != MISSING_METADATA_MARKER) { missingMetadata = false; addURLEntries(resourcesURLs, (ResourceStorageEntry) explicitEntry, module, shouldAppendTrailingSlash ? canonicalResourceName + '/' : canonicalResourceName); @@ -540,9 +545,10 @@ public void afterCompilation(AfterCompilationAccess access) { * of lazily initialized fields. Only the byte[] arrays themselves can be safely made * read-only. */ - for (ResourceStorageEntryBase entry : Resources.singleton().resources()) { - if (entry.hasData()) { - for (byte[] resource : entry.getData()) { + for (ConditionalRuntimeValue entry : Resources.singleton().resources()) { + var unconditionalEntry = entry.getValueUnconditionally(); + if (unconditionalEntry.hasData()) { + for (byte[] resource : unconditionalEntry.getData()) { access.registerAsImmutable(resource); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java index 45f8089b6507..8db98211cbd7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java @@ -161,6 +161,7 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function module = findModule.apply(bundleNameWithModule[0]); String finalResourceName = resourceName; module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, finalResourceName)); + } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/MissingResourceRegistrationUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/MissingResourceRegistrationUtils.java index 5de9822f8093..4219ac580c07 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/MissingResourceRegistrationUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/MissingResourceRegistrationUtils.java @@ -61,7 +61,7 @@ private static String errorMessage(String type, String resourcePath) { ERROR_EMPHASIS_INDENT + resourcePath + System.lineSeparator() + System.lineSeparator() + - " without it being registered as reachable. Add it to the resource metadata to solve this problem. " + + "without it being registered as reachable. Add it to the resource metadata to solve this problem. " + "See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#resources-and-resource-bundles for help"; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourceFileSystem.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourceFileSystem.java index 8321db7613cd..18b49814b1ad 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourceFileSystem.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourceFileSystem.java @@ -84,6 +84,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.regex.Pattern; +import com.oracle.svm.core.configure.ConditionalRuntimeValue; import org.graalvm.collections.MapCursor; import com.oracle.svm.core.MissingRegistrationUtils; @@ -575,7 +576,7 @@ IndexNode getInode(byte[] path) { IndexNode indexNode = inodes.get(IndexNode.keyOf(path)); if (indexNode == null && MissingRegistrationUtils.throwMissingRegistrationErrors()) { // Try to access the resource to see if the metadata is present - Resources.singleton().get(getString(path), true); + Resources.singleton().getAtRuntime(getString(path), true); } return indexNode; } @@ -656,11 +657,11 @@ private void update(Entry e) { } private void readAllEntries() { - MapCursor entries = Resources.singleton().getResourceStorage().getEntries(); + MapCursor> entries = Resources.singleton().getResourceStorage().getEntries(); while (entries.advance()) { byte[] name = getBytes(entries.getKey().resource()); - ResourceStorageEntryBase entry = entries.getValue(); - if (entry.hasData()) { + ResourceStorageEntryBase entry = entries.getValue().getValue(); + if (entry != null && entry.hasData()) { IndexNode newIndexNode = new IndexNode(name, entry.isDirectory(), true); inodes.put(newIndexNode, newIndexNode); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourceFileSystemUtil.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourceFileSystemUtil.java index 8b5713373685..82eea6722853 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourceFileSystemUtil.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/NativeImageResourceFileSystemUtil.java @@ -36,7 +36,7 @@ private NativeImageResourceFileSystemUtil() { } public static byte[] getBytes(String resourceName, boolean readOnly) { - Object entry = Resources.singleton().get(resourceName, true); + Object entry = Resources.singleton().getAtRuntime(resourceName, true); if (entry == null) { return new byte[0]; } @@ -49,7 +49,7 @@ public static byte[] getBytes(String resourceName, boolean readOnly) { } public static int getSize(String resourceName) { - Object entry = Resources.singleton().get(resourceName, true); + Object entry = Resources.singleton().getAtRuntime(resourceName, true); if (entry == null) { return 0; } else { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/ResourceURLConnection.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/ResourceURLConnection.java index 74d8b7268eff..c4dc888f5897 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/ResourceURLConnection.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/ResourceURLConnection.java @@ -74,7 +74,7 @@ public void connect() { String resourceName = urlPath.substring(1); Module module = hostNameOrNull != null ? ModuleLayer.boot().findModule(hostNameOrNull).orElse(null) : null; - Object entry = Resources.singleton().get(module, resourceName, true); + Object entry = Resources.singleton().getAtRuntime(module, resourceName, true); if (entry != null) { ResourceStorageEntry resourceStorageEntry = (ResourceStorageEntry) entry; List bytes = resourceStorageEntry.getData(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java index 3f673afaf549..400568dbc29b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java @@ -162,13 +162,13 @@ public SerializationLookupKey getKeyFromConstructorAccessorClass(Class constr @Platforms(Platform.HOSTED_ONLY.class) public void registerSerializationTargetClass(ConfigurationCondition cnd, Class serializationTargetClass) { - classes.computeIfAbsent(serializationTargetClass, k -> RuntimeConditionSet.createHosted()) + classes.computeIfAbsent(serializationTargetClass, k -> RuntimeConditionSet.emptySet()) .addCondition(cnd); } @Platforms(Platform.HOSTED_ONLY.class) public void registerLambdaCapturingClass(ConfigurationCondition cnd, String lambdaCapturingClass) { - lambdaCapturingClasses.computeIfAbsent(lambdaCapturingClass, k -> RuntimeConditionSet.createHosted()) + lambdaCapturingClasses.computeIfAbsent(lambdaCapturingClass, k -> RuntimeConditionSet.emptySet()) .addCondition(cnd); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java index 82a18a81a6a6..d25a48c71883 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java @@ -220,11 +220,7 @@ private static void scanJar(Path jarPath, ResourceCollector collector, boolean i } private static void includeResource(ResourceCollector collector, Module module, String name, ConfigurationCondition condition) { - if (condition.isAlwaysTrue()) { - collector.addResource(module, name); - } else { - collector.addResourceConditionally(module, name, condition); - } + collector.addResourceConditionally(module, name, condition); } private static List shouldIncludeEntry(Module module, ResourceCollector collector, String fileName, URI uri, boolean includeCurrent) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourceExporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourceExporter.java index 0933188c36a2..bab9b935c97b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourceExporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourceExporter.java @@ -37,6 +37,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.core.configure.ConditionalRuntimeValue; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.jdk.resources.ResourceStorageEntryBase; import com.oracle.svm.core.util.VMError; @@ -106,12 +107,12 @@ private static List getResourceReportEntryList(ConcurrentHa } List resourceInfoList = new ArrayList<>(); - EconomicMap resourceStorage = Resources.singleton().getResourceStorage(); + EconomicMap> resourceStorage = Resources.singleton().getResourceStorage(); resourceStorage.getKeys().forEach(key -> { Module module = key.module(); String resourceName = key.resource(); - ResourceStorageEntryBase storageEntry = resourceStorage.get(key); + ResourceStorageEntryBase storageEntry = resourceStorage.get(key).getValueUnconditionally(); List registeredEntrySources = collection.get(key); if (registeredEntrySources == null && storageEntry != NEGATIVE_QUERY_MARKER) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HeapBreakdownProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HeapBreakdownProvider.java index c7d410519ada..0377a0d7eeb7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HeapBreakdownProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HeapBreakdownProvider.java @@ -38,6 +38,7 @@ import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.config.ObjectLayout; +import com.oracle.svm.core.configure.ConditionalRuntimeValue; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.jdk.resources.ResourceStorageEntryBase; import com.oracle.svm.core.reflect.RuntimeMetadataDecoder; @@ -149,9 +150,9 @@ protected void calculate(BeforeImageWriteAccessImpl access) { /* Extract byte[] for resources. */ long resourcesByteArraySize = 0; int resourcesByteArrayCount = 0; - for (ResourceStorageEntryBase resourceList : Resources.singleton().resources()) { - if (resourceList.hasData()) { - for (byte[] resource : resourceList.getData()) { + for (ConditionalRuntimeValue resourceList : Resources.singleton().resources()) { + if (resourceList.getValueUnconditionally().hasData()) { + for (byte[] resource : resourceList.getValueUnconditionally().getData()) { resourcesByteArraySize += objectLayout.getArraySize(JavaKind.Byte, resource.length, true); resourcesByteArrayCount++; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index a6eccd09c233..9e334d20fd23 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -26,6 +26,7 @@ package com.oracle.svm.hosted; import static com.oracle.svm.core.jdk.Resources.RESOURCES_INTERNAL_PATH_SEPARATOR; +import static com.oracle.svm.core.jdk.Resources.createStorageKey; import java.io.IOException; import java.io.InputStream; @@ -194,9 +195,14 @@ public void addGlob(ConfigurationCondition condition, String module, String glob globWorkSet.add(new ConditionalPattern(condition, resolvedGlob)); } + public void addCondition(ConfigurationCondition condition, Module module, String resourcePath) { + var conditionalResource = Resources.singleton().getResourceStorage().get(createStorageKey(module, resourcePath)); + conditionalResource.getConditions().addCondition(condition); + } + /* Adds single resource defined with its module and name */ @Override - public void addResource(Module module, String resourcePath) { + public void addResourceEntry(Module module, String resourcePath) { if (!shouldRegisterResource(module, resourcePath)) { return; } @@ -412,25 +418,23 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { excludedResourcePatterns.addAll(Options.ExcludeResources.getValue().values()); ResourcePattern[] excludePatterns = compilePatterns(excludedResourcePatterns); + FeatureImpl.BeforeAnalysisAccessImpl beforeAnalysisAccess = (FeatureImpl.BeforeAnalysisAccessImpl) access; + ResourceCollectorImpl collector = new ResourceCollectorImpl(includePatterns, excludePatterns, beforeAnalysisAccess.getImageClassLoader()); /* * register all included patterns in Resources singleton (if we are throwing * MissingRegistrationErrors), so they can be queried at runtime to detect missing entries */ if (MissingRegistrationUtils.throwMissingRegistrationErrors()) { - includePatterns.stream() - .map(pattern -> pattern.compiledPattern) - .forEach(resourcePattern -> { - Resources.singleton().registerIncludePattern(resourcePattern.moduleName, resourcePattern.pattern.pattern()); - }); + includePatterns.forEach(resourcePattern -> collector.registerIncludePattern(resourcePattern.condition, resourcePattern.compiledPattern.moduleName(), + resourcePattern.compiledPattern.pattern.pattern())); } /* if we have any entry in resource config file we should collect resources */ if (!resourcePatternWorkSet.isEmpty() || !globWorkSet.isEmpty()) { - FeatureImpl.BeforeAnalysisAccessImpl beforeAnalysisAccess = (FeatureImpl.BeforeAnalysisAccessImpl) access; - ResourceCollectorImpl collector = new ResourceCollectorImpl(includePatterns, excludePatterns, beforeAnalysisAccess); try { collector.prepareProgressReporter(); ImageSingletons.lookup(ClassLoaderSupport.class).collectResources(collector); + collector.flushConditionalConfiguration(access); } finally { collector.shutDownProgressReporter(); } @@ -447,28 +451,26 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { resourceRegistryImpl().flushConditionalConfiguration(access); } - private static final class ResourceCollectorImpl implements ResourceCollector { + private static final class ResourceCollectorImpl extends ConditionalConfigurationRegistry implements ResourceCollector { private final Set includePatterns; private final ResourcePattern[] excludePatterns; private static final int WATCHDOG_RESET_AFTER_EVERY_N_RESOURCES = 1000; private static final int WATCHDOG_INITIAL_WARNING_AFTER_N_SECONDS = 60; private static final int WATCHDOG_WARNING_AFTER_EVERY_N_SECONDS = 20; - private final FeatureImpl.BeforeAnalysisAccessImpl access; private final LongAdder reachedResourceEntries; private boolean initialReport; private volatile String currentlyProcessedEntry; ScheduledExecutorService scheduledExecutor; ConfigurationConditionResolver conditionResolver; - private ResourceCollectorImpl(Set includePatterns, ResourcePattern[] excludePatterns, FeatureImpl.BeforeAnalysisAccessImpl access) { + private ResourceCollectorImpl(Set includePatterns, ResourcePattern[] excludePatterns, ImageClassLoader imageClassLoader) { this.includePatterns = includePatterns; this.excludePatterns = excludePatterns; - this.access = access; this.reachedResourceEntries = new LongAdder(); this.initialReport = true; this.currentlyProcessedEntry = null; - this.conditionResolver = new NativeImageConditionResolver(access.getImageClassLoader(), ClassInitializationSupport.singleton()); + this.conditionResolver = new NativeImageConditionResolver(imageClassLoader, ClassInitializationSupport.singleton()); } private void prepareProgressReporter() { @@ -548,13 +550,16 @@ private List getConditionsFromGlobTrie(Module module, St } @Override - public void addResource(Module module, String resourceName) { - ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourceName); + public void addResourceEntry(Module module, String resourceName) { + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceEntry(module, resourceName); } @Override public void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition) { - access.registerReachabilityHandler(e -> addResource(module, resourceName), condition.getType()); + registerConditionalConfiguration(condition, cnd -> { + addResourceEntry(module, resourceName); + ImageSingletons.lookup(RuntimeResourceSupport.class).addCondition(cnd, module, resourceName); + }); } @Override @@ -567,6 +572,10 @@ public void registerNegativeQuery(Module module, String resourceName) { EmbeddedResourcesInfo.singleton().declareResourceAsRegistered(module, resourceName, ""); Resources.singleton().registerNegativeQuery(module, resourceName); } + + public void registerIncludePattern(ConfigurationCondition condition, String module, String pattern) { + registerConditionalConfiguration(condition, cnd -> Resources.singleton().registerIncludePattern(cnd, module, pattern)); + } } private ResourcePattern[] compilePatterns(Set patterns) { From 45fb2385e2b543fdc38e276759d91f6604fcb9b1 Mon Sep 17 00:00:00 2001 From: Vojin Jovanovic Date: Fri, 24 May 2024 21:20:08 +0200 Subject: [PATCH 3/4] typeReached for resource bundles --- .../jdk/localization/LocalizationSupport.java | 13 +++-- .../oracle/svm/hosted/ResourcesFeature.java | 13 +++-- .../jdk/localization/LocalizationFeature.java | 51 ++++++++++--------- 3 files changed, 44 insertions(+), 33 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java index 8db98211cbd7..8def2c7a737f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java @@ -45,7 +45,7 @@ import java.util.function.Function; import java.util.stream.Collectors; -import org.graalvm.collections.EconomicSet; +import org.graalvm.collections.EconomicMap; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -56,6 +56,7 @@ import com.oracle.svm.core.ClassLoaderSupport; import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.configure.RuntimeConditionSet; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ReflectionUtil; @@ -87,7 +88,7 @@ public class LocalizationSupport { public final Charset defaultCharset; - private final EconomicSet registeredBundles = EconomicSet.create(); + private final EconomicMap registeredBundles = EconomicMap.create(); public LocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset) { this.defaultLocale = defaultLocale; @@ -322,8 +323,10 @@ private static void registerNullaryConstructor(Class bundleClass) { } @Platforms(Platform.HOSTED_ONLY.class) - public void registerBundleLookup(String baseName) { - registeredBundles.add(baseName); + public void registerBundleLookup(ConfigurationCondition condition, String baseName) { + RuntimeConditionSet conditionSet = RuntimeConditionSet.emptySet(); + var registered = registeredBundles.putIfAbsent(baseName, conditionSet); + (registered == null ? conditionSet : registered).addCondition(condition); } public boolean isRegisteredBundleLookup(String baseName, Locale locale, Object controlOrStrategy) { @@ -331,6 +334,6 @@ public boolean isRegisteredBundleLookup(String baseName, Locale locale, Object c /* Those cases will throw a NullPointerException before any lookup */ return true; } - return registeredBundles.contains(baseName); + return registeredBundles.containsKey(baseName) && registeredBundles.get(baseName).satisfied(); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index 9e334d20fd23..e1227845848d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -197,7 +197,9 @@ public void addGlob(ConfigurationCondition condition, String module, String glob public void addCondition(ConfigurationCondition condition, Module module, String resourcePath) { var conditionalResource = Resources.singleton().getResourceStorage().get(createStorageKey(module, resourcePath)); - conditionalResource.getConditions().addCondition(condition); + if (conditionalResource != null) { + conditionalResource.getConditions().addCondition(condition); + } } /* Adds single resource defined with its module and name */ @@ -231,7 +233,7 @@ public void ignoreResources(ConfigurationCondition condition, String pattern) { @Override public void addResourceBundles(ConfigurationCondition condition, String name) { - registerConditionalConfiguration(condition, (cnd) -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(name)); + registerConditionalConfiguration(condition, (cnd) -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(cnd, name)); } @Override @@ -241,7 +243,7 @@ public void addClassBasedResourceBundle(ConfigurationCondition condition, String @Override public void addResourceBundles(ConfigurationCondition condition, String basename, Collection locales) { - registerConditionalConfiguration(condition, (cnd) -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(basename, locales)); + registerConditionalConfiguration(condition, (cnd) -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(cnd, basename, locales)); } /* @@ -451,6 +453,11 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { resourceRegistryImpl().flushConditionalConfiguration(access); } + @Override + public void duringAnalysis(DuringAnalysisAccess access) { + resourceRegistryImpl().flushConditionalConfiguration(access); + } + private static final class ResourceCollectorImpl extends ConditionalConfigurationRegistry implements ResourceCollector { private final Set includePatterns; private final ResourcePattern[] excludePatterns; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java index f4dfd6fbf5ef..5dc9eb644102 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java @@ -64,6 +64,7 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport; import com.oracle.graal.pointsto.ObjectScanner; @@ -512,7 +513,7 @@ protected void addResourceBundles() { * Locale data bundle class names do not contain underscores */ String baseName = e.getClassName().split("_")[0]; - prepareNegativeBundle(baseName, locale, true); + prepareNegativeBundle(ConfigurationCondition.alwaysTrue(), baseName, locale, true); continue; /* No bundle for this `locale`. */ } if (bundle instanceof ParallelListResourceBundle) { @@ -529,14 +530,14 @@ protected void addResourceBundles() { * No eager loading of bundle content, so we need to include the * `sun.text.resources.FormatData` bundle supplement as well. */ - prepareBundle("sun.text.resources.JavaTimeSupplementary"); + prepareBundle(ConfigurationCondition.alwaysTrue(), "sun.text.resources.JavaTimeSupplementary"); } final String[] alwaysRegisteredResourceBundles = new String[]{ "sun.util.logging.resources.logging" }; for (String bundleName : alwaysRegisteredResourceBundles) { - prepareBundle(bundleName); + prepareBundle(ConfigurationCondition.alwaysTrue(), bundleName); } for (String bundleName : Options.IncludeResourceBundles.getValue().values()) { @@ -549,7 +550,7 @@ private void processRequestedBundle(String input) { int splitIndex = input.indexOf('_'); boolean specificLocaleRequested = splitIndex != -1; if (!specificLocaleRequested) { - prepareBundle(input, allLocales); + prepareBundle(ConfigurationCondition.alwaysTrue(), input, allLocales); return; } Locale locale = splitIndex + 1 < input.length() ? LocalizationSupport.parseLocaleFromTag(input.substring(splitIndex + 1)) : Locale.ROOT; @@ -559,7 +560,7 @@ private void processRequestedBundle(String input) { } /*- Get rid of locale specific suffix. */ String baseName = input.substring(0, splitIndex); - prepareBundle(baseName, Collections.singletonList(locale)); + prepareBundle(ConfigurationCondition.alwaysTrue(), baseName, Collections.singletonList(locale)); } @Platforms(Platform.HOSTED_ONLY.class) @@ -572,8 +573,8 @@ public void prepareClassResourceBundle(String basename, String className) { } @Platforms(Platform.HOSTED_ONLY.class) - public void prepareBundle(String baseName) { - prepareBundle(baseName, allLocales); + public void prepareBundle(ConfigurationCondition condition, String baseName) { + prepareBundle(condition, baseName, allLocales); } private static final String[] RESOURCE_EXTENSION_PREFIXES = new String[]{ @@ -584,37 +585,37 @@ public void prepareBundle(String baseName) { }; @Platforms(Platform.HOSTED_ONLY.class) - public void prepareBundle(String baseName, Collection wantedLocales) { - prepareBundleInternal(baseName, wantedLocales); + public void prepareBundle(ConfigurationCondition condition, String baseName, Collection wantedLocales) { + prepareBundleInternal(condition, baseName, wantedLocales); String alternativeBundleName = null; - for (String resourceExtentionPrefix : RESOURCE_EXTENSION_PREFIXES) { - if (baseName.startsWith(resourceExtentionPrefix) && !baseName.startsWith(resourceExtentionPrefix + ".ext")) { - alternativeBundleName = baseName.replace(resourceExtentionPrefix, resourceExtentionPrefix + ".ext"); + for (String resourceExtensionPrefix : RESOURCE_EXTENSION_PREFIXES) { + if (baseName.startsWith(resourceExtensionPrefix) && !baseName.startsWith(resourceExtensionPrefix + ".ext")) { + alternativeBundleName = baseName.replace(resourceExtensionPrefix, resourceExtensionPrefix + ".ext"); break; } } if (alternativeBundleName != null) { - prepareBundleInternal(alternativeBundleName, wantedLocales); + prepareBundleInternal(condition, alternativeBundleName, wantedLocales); } } - private void prepareBundleInternal(String baseName, Collection wantedLocales) { + private void prepareBundleInternal(ConfigurationCondition condition, String baseName, Collection wantedLocales) { boolean somethingFound = false; for (Locale locale : wantedLocales) { - support.registerBundleLookup(baseName); + support.registerBundleLookup(condition, baseName); List resourceBundle; try { resourceBundle = ImageSingletons.lookup(ClassLoaderSupport.class).getResourceBundle(baseName, locale); } catch (MissingResourceException mre) { for (Locale candidateLocale : support.control.getCandidateLocales(baseName, locale)) { - prepareNegativeBundle(baseName, candidateLocale, false); + prepareNegativeBundle(condition, baseName, candidateLocale, false); } continue; } somethingFound |= !resourceBundle.isEmpty(); for (ResourceBundle bundle : resourceBundle) { - prepareBundle(baseName, bundle, locale, false); + prepareBundle(condition, baseName, bundle, locale, false); } } @@ -643,33 +644,33 @@ private void prepareBundleInternal(String baseName, Collection wantedLoc "If the bundle is part of a module, verify the bundle name is a fully qualified class name. Otherwise " + "verify the bundle path is accessible in the classpath."; trace(errorMessage); - prepareNegativeBundle(baseName, Locale.ROOT, false); + prepareNegativeBundle(condition, baseName, Locale.ROOT, false); for (String language : wantedLocales.stream().map(Locale::getLanguage).collect(Collectors.toSet())) { - prepareNegativeBundle(baseName, Locale.of(language), false); + prepareNegativeBundle(condition, baseName, Locale.of(language), false); } for (Locale locale : wantedLocales) { if (!locale.getCountry().isEmpty()) { - prepareNegativeBundle(baseName, locale, false); + prepareNegativeBundle(condition, baseName, locale, false); } } } } @Platforms(Platform.HOSTED_ONLY.class) - protected void prepareNegativeBundle(String baseName, Locale locale, boolean jdkBundle) { - support.registerBundleLookup(baseName); + protected void prepareNegativeBundle(ConfigurationCondition condition, String baseName, Locale locale, boolean jdkBundle) { + support.registerBundleLookup(condition, baseName); support.registerRequiredReflectionAndResourcesForBundleAndLocale(baseName, locale, jdkBundle); } @Platforms(Platform.HOSTED_ONLY.class) protected void prepareJDKBundle(ResourceBundle bundle, Locale locale) { String baseName = bundle.getBaseBundleName(); - prepareBundle(baseName, bundle, locale, true); + prepareBundle(ConfigurationCondition.alwaysTrue(), baseName, bundle, locale, true); } @Platforms(Platform.HOSTED_ONLY.class) - private void prepareBundle(String bundleName, ResourceBundle bundle, Locale locale, boolean jdkBundle) { - trace("Adding bundle " + bundleName + ", locale " + locale); + private void prepareBundle(ConfigurationCondition condition, String bundleName, ResourceBundle bundle, Locale locale, boolean jdkBundle) { + trace("Adding bundle " + bundleName + ", locale " + locale + " with condition " + condition); /* * Ensure that the bundle contents are loaded. We need to walk the whole bundle parent chain * down to the root. From c1f00ee5dbb958a68343798a0a15b72288a8fff0 Mon Sep 17 00:00:00 2001 From: Vojin Jovanovic Date: Thu, 30 May 2024 05:53:58 +0200 Subject: [PATCH 4/4] Track run-time conditions when creating handler --- .../impl/ConfigurationCondition.java | 7 ++++ .../core/configure/ConfigurationFiles.java | 6 ++-- .../com/oracle/svm/core/jdk/Resources.java | 1 + .../jdk/localization/LocalizationSupport.java | 1 - .../serialize/SerializationRegistry.java | 3 +- .../serialize/SerializationSupport.java | 26 +++++++++------ .../ConditionalConfigurationRegistry.java | 32 ++++++++++++------- .../oracle/svm/hosted/ResourcesFeature.java | 10 ++---- .../ClassInitializationSupport.java | 6 ++-- .../svm/hosted/jni/JNIAccessFeature.java | 8 +---- .../hosted/reflect/ReflectionDataBuilder.java | 3 +- .../svm/hosted/reflect/ReflectionFeature.java | 7 +--- .../reflect/proxy/DynamicProxyFeature.java | 3 +- .../serialize/SerializationFeature.java | 3 +- 14 files changed, 62 insertions(+), 54 deletions(-) diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ConfigurationCondition.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ConfigurationCondition.java index f2ed67daf0bc..f6deb0279b14 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ConfigurationCondition.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ConfigurationCondition.java @@ -105,4 +105,11 @@ public int hashCode() { return Objects.hash(type, runtimeChecked); } + @Override + public String toString() { + return "ConfigurationCondition(" + + "type=" + type + + ", runtimeChecked=" + runtimeChecked + + ')'; + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java index 328669446426..663b62dc4427 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java @@ -125,16 +125,16 @@ public static final class Options { @Option(help = "When configuration files do not match their schema, abort the image build instead of emitting a warning.")// public static final HostedOptionKey StrictConfiguration = new HostedOptionKey<>(false); - @Option(help = "Testing flag: the typeReachable condition is treated as typeReached so the semantics of programs can change.")// + @Option(help = "Testing flag: the 'typeReachable' condition is treated as typeReached so the semantics of programs can change.")// public static final HostedOptionKey TreatAllTypeReachableConditionsAsTypeReached = new HostedOptionKey<>(false); - @Option(help = "Testing flag: the 'name' is treated as 'type' reflection configuration.")// + @Option(help = "Testing flag: the 'name' is treated as 'type' in reflection configuration.")// public static final HostedOptionKey TreatAllNameEntriesAsType = new HostedOptionKey<>(false); @Option(help = "Testing flag: the 'typeReached' condition is always satisfied however it prints the stack trace where it would not be satisfied.")// public static final HostedOptionKey TrackUnsatisfiedTypeReachedConditions = new HostedOptionKey<>(false); - @Option(help = "Testing flag: print 'typeReached' conditions that are used on interfaces at build time.")// + @Option(help = "Testing flag: print 'typeReached' conditions that are used on interfaces without default methods at build time.")// public static final HostedOptionKey TrackTypeReachedOnInterfaces = new HostedOptionKey<>(false); @Option(help = "Testing flag: every type is considered as it participates in a typeReachable condition.")// diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java index 503996f2991e..3f8ff5b094e9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java @@ -319,6 +319,7 @@ public ResourceStorageEntryBase getAtRuntime(String name, boolean throwOnMissing * tried on the same resource name, causing an unexpected exception if we throw directly. */ public ResourceStorageEntryBase getAtRuntime(Module module, String resourceName, boolean throwOnMissing) { + VMError.guarantee(ImageInfo.inImageRuntimeCode(), "This function should be used only at runtime."); String canonicalResourceName = toCanonicalForm(resourceName); String moduleName = moduleName(module); ConditionalRuntimeValue entry = resources.get(createStorageKey(module, canonicalResourceName)); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java index 8def2c7a737f..ce17d368cd70 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java @@ -162,7 +162,6 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function module = findModule.apply(bundleNameWithModule[0]); String finalResourceName = resourceName; module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, finalResourceName)); - } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationRegistry.java index b991d397b1e4..6547a178e55b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationRegistry.java @@ -27,7 +27,8 @@ public interface SerializationRegistry { + boolean isRegisteredForSerialization(Class cl); + Object getSerializationConstructorAccessor(Class serializationTargetClass, Class targetConstructorClass); - boolean isRegisteredForSerialization(Class aClass); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java index 400568dbc29b..761bf0c714bd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java @@ -31,9 +31,7 @@ import java.lang.invoke.SerializedLambda; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; -import java.util.Map; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; @@ -157,19 +155,27 @@ public SerializationLookupKey getKeyFromConstructorAccessorClass(Class constr return null; } - private final Map, RuntimeConditionSet> classes = new ConcurrentHashMap<>(); - private final Map lambdaCapturingClasses = new ConcurrentHashMap<>(); + private final EconomicMap, RuntimeConditionSet> classes = EconomicMap.create(); + private final EconomicMap lambdaCapturingClasses = EconomicMap.create(); @Platforms(Platform.HOSTED_ONLY.class) public void registerSerializationTargetClass(ConfigurationCondition cnd, Class serializationTargetClass) { - classes.computeIfAbsent(serializationTargetClass, k -> RuntimeConditionSet.emptySet()) - .addCondition(cnd); + synchronized (classes) { + var previous = classes.putIfAbsent(serializationTargetClass, RuntimeConditionSet.createHosted(cnd)); + if (previous != null) { + previous.addCondition(cnd); + } + } } @Platforms(Platform.HOSTED_ONLY.class) public void registerLambdaCapturingClass(ConfigurationCondition cnd, String lambdaCapturingClass) { - lambdaCapturingClasses.computeIfAbsent(lambdaCapturingClass, k -> RuntimeConditionSet.emptySet()) - .addCondition(cnd); + synchronized (lambdaCapturingClasses) { + var previousConditions = lambdaCapturingClasses.putIfAbsent(lambdaCapturingClass, RuntimeConditionSet.createHosted(cnd)); + if (previousConditions != null) { + previousConditions.addCondition(cnd); + } + } } @Platforms(Platform.HOSTED_ONLY.class) @@ -205,8 +211,8 @@ public Object getSerializationConstructorAccessor(Class rawDeclaringClass, Cl } @Override - public boolean isRegisteredForSerialization(Class jClass) { - var conditionSet = classes.get(jClass); + public boolean isRegisteredForSerialization(Class clazz) { + var conditionSet = classes.get(clazz); return conditionSet != null && conditionSet.satisfied(); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java index dbb6ec457015..df72aa376f5d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java @@ -34,44 +34,52 @@ import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.impl.ConfigurationCondition; +import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; public abstract class ConditionalConfigurationRegistry { + private Feature.BeforeAnalysisAccess beforeAnalysisAccess; private final Map, Collection> pendingReachabilityHandlers = new ConcurrentHashMap<>(); protected void registerConditionalConfiguration(ConfigurationCondition condition, Consumer consumer) { Objects.requireNonNull(condition, "Cannot use null value as condition for conditional configuration. Please ensure that you register a non-null condition."); Objects.requireNonNull(consumer, "Cannot use null value as runnable for conditional configuration. Please ensure that you register a non-null runnable."); + if (condition.isRuntimeChecked() && !condition.isAlwaysTrue()) { + /* + * We do this before the type is reached as the handler runs during analysis when it is + * too late to register types for reached tracking. If the type is never reached, there + * is no damage as subtypes will also never be reached. + */ + ClassInitializationSupport.singleton().addForTypeReachedTracking(condition.getType()); + } if (ConfigurationCondition.alwaysTrue().equals(condition)) { /* analysis optimization to include new types as early as possible */ consumer.accept(ConfigurationCondition.alwaysTrue()); } else { - Collection handlers = pendingReachabilityHandlers.computeIfAbsent(condition.getType(), key -> new ConcurrentLinkedQueue<>()); ConfigurationCondition runtimeCondition; if (condition.isRuntimeChecked()) { - ClassInitializationSupport.singleton().addForTypeReachedTracking(condition.getType()); runtimeCondition = condition; } else { runtimeCondition = ConfigurationCondition.alwaysTrue(); } + if (beforeAnalysisAccess == null) { + Collection handlers = pendingReachabilityHandlers.computeIfAbsent(condition.getType(), key -> new ConcurrentLinkedQueue<>()); + handlers.add(() -> consumer.accept(runtimeCondition)); + } else { + beforeAnalysisAccess.registerReachabilityHandler(access -> consumer.accept(runtimeCondition), condition.getType()); + } - handlers.add(() -> consumer.accept(runtimeCondition)); } } - public void flushConditionalConfiguration(Feature.BeforeAnalysisAccess b) { + public void setAnalysisAccess(Feature.BeforeAnalysisAccess beforeAnalysisAccess) { + VMError.guarantee(this.beforeAnalysisAccess == null, "Analysis access can be set only once."); + this.beforeAnalysisAccess = Objects.requireNonNull(beforeAnalysisAccess); for (Map.Entry, Collection> reachabilityEntry : pendingReachabilityHandlers.entrySet()) { - b.registerReachabilityHandler(access -> reachabilityEntry.getValue().forEach(Runnable::run), reachabilityEntry.getKey()); + this.beforeAnalysisAccess.registerReachabilityHandler(access -> reachabilityEntry.getValue().forEach(Runnable::run), reachabilityEntry.getKey()); } pendingReachabilityHandlers.clear(); } - public void flushConditionalConfiguration(Feature.DuringAnalysisAccess b) { - if (!pendingReachabilityHandlers.isEmpty()) { - b.requireAnalysisIteration(); - } - flushConditionalConfiguration((Feature.BeforeAnalysisAccess) b); - } - } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index e1227845848d..92e689630ec0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -195,6 +195,7 @@ public void addGlob(ConfigurationCondition condition, String module, String glob globWorkSet.add(new ConditionalPattern(condition, resolvedGlob)); } + @Override public void addCondition(ConfigurationCondition condition, Module module, String resourcePath) { var conditionalResource = Resources.singleton().getResourceStorage().get(createStorageKey(module, resourcePath)); if (conditionalResource != null) { @@ -436,7 +437,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { try { collector.prepareProgressReporter(); ImageSingletons.lookup(ClassLoaderSupport.class).collectResources(collector); - collector.flushConditionalConfiguration(access); + collector.setAnalysisAccess(access); } finally { collector.shutDownProgressReporter(); } @@ -450,12 +451,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { resourcePatternWorkSet = Set.of(); globWorkSet = Set.of(); - resourceRegistryImpl().flushConditionalConfiguration(access); - } - - @Override - public void duringAnalysis(DuringAnalysisAccess access) { - resourceRegistryImpl().flushConditionalConfiguration(access); + resourceRegistryImpl().setAnalysisAccess(access); } private static final class ResourceCollectorImpl extends ConditionalConfigurationRegistry implements ResourceCollector { 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 fc06c960ee33..04ed3d96fb0a 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 @@ -495,11 +495,13 @@ private static void addAllInterfaces(Class clazz, EconomicSet> resul public void addForTypeReachedTracking(Class clazz) { if (TrackTypeReachedOnInterfaces.getValue() && clazz.isInterface() && !metaAccess.lookupJavaType(clazz).declaresDefaultMethods()) { - LogUtils.info("Detected 'typeReached' on interface type without default methods: " + clazz); + LogUtils.info("Detected 'typeReached' on interface type without default methods: %s", clazz.getName()); } if (!isAlwaysReached(clazz)) { - UserError.guarantee(!configurationSealed, "It is not possible to register types for reachability tracking after the analysis has started."); + UserError.guarantee(!configurationSealed || typesRequiringReachability.contains(clazz), + "It is not possible to register types for reachability tracking after the analysis has started if they were not registered before analysis started. Trying to register: %s", + clazz.getName()); typesRequiringReachability.add(clazz); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java index 530311c86e8a..063b1bd4ab6b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java @@ -257,12 +257,7 @@ public void beforeAnalysis(BeforeAnalysisAccess arg) { registerJavaCallTrampoline(access, variant, true); } - /* duplicated to reduce the number of analysis iterations */ - getConditionalConfigurationRegistry().flushConditionalConfiguration(access); - } - - private static ConditionalConfigurationRegistry getConditionalConfigurationRegistry() { - return singleton().runtimeSupport; + singleton().runtimeSupport.setAnalysisAccess(access); } private static void registerJavaCallTrampoline(BeforeAnalysisAccessImpl access, CallVariant variant, boolean nonVirtual) { @@ -319,7 +314,6 @@ private boolean wereElementsAdded() { @Override public void duringAnalysis(DuringAnalysisAccess a) { - getConditionalConfigurationRegistry().flushConditionalConfiguration(a); DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; if (!wereElementsAdded()) { return; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java index cceba8eb62f9..34208b81ebb4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java @@ -601,7 +601,8 @@ private void processAnnotationMethod(boolean queriedOnly, Method method) { Class annotationClass = method.getDeclaringClass(); Class proxyClass = Proxy.getProxyClass(annotationClass.getClassLoader(), annotationClass); try { - var condition = ConfigurationCondition.create(proxyClass, true); + /* build-time condition as it is registered during analysis */ + var condition = ConfigurationCondition.create(proxyClass, false); register(condition, queriedOnly, proxyClass.getDeclaredMethod(method.getName(), method.getParameterTypes())); } catch (NoSuchMethodException e) { /* diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java index e0a42e10d187..7207367189e4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java @@ -321,7 +321,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { metaAccess = analysisAccess.getMetaAccess(); reflectionData.beforeAnalysis(analysisAccess); /* duplicated to reduce the number of analysis iterations */ - reflectionData.flushConditionalConfiguration(access); + reflectionData.setAnalysisAccess(access); /* * This has to be registered before registering methods below since this causes the analysis @@ -336,11 +336,6 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { RuntimeReflection.register(Object.class.getDeclaredMethods()); } - @Override - public void duringAnalysis(DuringAnalysisAccess access) { - reflectionData.flushConditionalConfiguration(access); - } - @Override public void afterAnalysis(AfterAnalysisAccess access) { analysisAccess = null; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java index e4c64e099cd4..b8367d0ffe59 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java @@ -76,14 +76,13 @@ private static ProxyRegistry proxyRegistry() { @Override public void beforeAnalysis(BeforeAnalysisAccess access) { - proxyRegistry().flushConditionalConfiguration(access); + proxyRegistry().setAnalysisAccess(access); } @Override public void duringAnalysis(DuringAnalysisAccess a) { DuringAnalysisAccessImpl access = (DuringAnalysisAccessImpl) a; access.rescanField(ImageSingletons.lookup(DynamicProxyRegistry.class), proxyCacheField); - proxyRegistry().flushConditionalConfiguration(a); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java index 045e3ea3436d..dfa5ba0b9744 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java @@ -255,14 +255,13 @@ private static void registerLambdasFromMethod(ResolvedJavaMethod method, Seriali @Override public void beforeAnalysis(BeforeAnalysisAccess access) { - serializationBuilder.flushConditionalConfiguration(access); + serializationBuilder.setAnalysisAccess(access); } @Override public void duringAnalysis(DuringAnalysisAccess access) { FeatureImpl.DuringAnalysisAccessImpl impl = (FeatureImpl.DuringAnalysisAccessImpl) access; OptionValues options = impl.getBigBang().getOptions(); - serializationBuilder.flushConditionalConfiguration(access); /* * In order to serialize lambda classes we need to register proper methods for reflection.