Skip to content

Commit c5d886e

Browse files
committed
[GR-66742] Deduplicate ReflectionDataBuilder
PullRequest: graal/21312
2 parents d85380c + 2b07fbf commit c5d886e

File tree

4 files changed

+122
-1
lines changed

4 files changed

+122
-1
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ private void addKnownClass(String name, ConditionalRuntimeValue<Object> cond) {
244244

245245
private void addKnownClass(String name, Consumer<EconomicMap<String, ConditionalRuntimeValue<Object>>> callback, ConditionalRuntimeValue<Object> cond) {
246246
Boolean previousLayerData = previousLayerClasses.get(name);
247+
/* GR-66387: The runtime condition should be combined across layers. */
247248
if (previousLayerData == null || (!previousLayerData && cond.getValueUnconditionally() != NEGATIVE_QUERY)) {
248249
callback.accept(knownClasses);
249250
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ private void updateTimeStamp() {
341341

342342
private void addResource(ModuleResourceKey key, ConditionalRuntimeValue<ResourceStorageEntryBase> entry) {
343343
Boolean previousLayerData = ImageLayerBuildingSupport.buildingImageLayer() ? previousLayerResources.get(key.toString()) : null;
344+
/* GR-66387: The runtime condition should be combined across layers. */
344345
if (previousLayerData == null || (!previousLayerData && entry.getValueUnconditionally() != NEGATIVE_QUERY_MARKER)) {
345346
resources.put(key, entry);
346347
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ public void beforeAnalysis(BeforeAnalysisAccess a) {
434434
* enabled. Until a clear SVM core separation is created and included in the base layer,
435435
* those types should be manually registered as instantiated before the analysis.
436436
*/
437-
if (HostedImageLayerBuildingSupport.buildingSharedLayer()) {
437+
if (HostedImageLayerBuildingSupport.buildingImageLayer()) {
438438
String reason = "Included in the base image";
439439
access.getMetaAccess().lookupJavaType(ReflectionUtil.lookupClass(false, "com.oracle.svm.core.jdk.resources.CompressedGlobTrie.LiteralNode")).registerAsInstantiated(reason);
440440
access.getMetaAccess().lookupJavaType(ReflectionUtil.lookupClass(false, "com.oracle.svm.core.jdk.resources.CompressedGlobTrie.DoubleStarNode")).registerAsInstantiated(reason);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
import java.util.Arrays;
5858
import java.util.Collection;
5959
import java.util.Collections;
60+
import java.util.EnumSet;
61+
import java.util.HashMap;
6062
import java.util.HashSet;
6163
import java.util.List;
6264
import java.util.Map;
@@ -66,6 +68,7 @@
6668
import java.util.concurrent.Callable;
6769
import java.util.concurrent.ConcurrentHashMap;
6870
import java.util.function.Consumer;
71+
import java.util.function.Function;
6972
import java.util.stream.Collectors;
7073

7174
import org.graalvm.nativeimage.ImageSingletons;
@@ -85,8 +88,15 @@
8588
import com.oracle.svm.core.MissingRegistrationUtils;
8689
import com.oracle.svm.core.configure.ConditionalRuntimeValue;
8790
import com.oracle.svm.core.configure.RuntimeConditionSet;
91+
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
8892
import com.oracle.svm.core.hub.ClassForNameSupport;
8993
import com.oracle.svm.core.hub.DynamicHub;
94+
import com.oracle.svm.core.imagelayer.BuildingImageLayerPredicate;
95+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
96+
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader;
97+
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter;
98+
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
99+
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
90100
import com.oracle.svm.core.reflect.SubstrateAccessor;
91101
import com.oracle.svm.core.reflect.target.ReflectionSubstitutionSupport;
92102
import com.oracle.svm.core.util.VMError;
@@ -112,6 +122,7 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl
112122
private final SubstrateAnnotationExtractor annotationExtractor;
113123
private BeforeAnalysisAccessImpl analysisAccess;
114124
private final ClassForNameSupport classForNameSupport;
125+
private LayeredReflectionDataBuilder layeredReflectionDataBuilder;
115126

116127
private boolean sealed;
117128

@@ -174,6 +185,9 @@ public void duringSetup(AnalysisMetaAccess analysisMetaAccess, AnalysisUniverse
174185
registerConditionalConfiguration(conditionalTask.condition, (cnd) -> universe.getBigbang().postTask(debug -> conditionalTask.task.accept(cnd)));
175186
}
176187
pendingConditionalTasks.clear();
188+
if (ImageLayerBuildingSupport.buildingImageLayer()) {
189+
layeredReflectionDataBuilder = LayeredReflectionDataBuilder.singleton();
190+
}
177191
}
178192

179193
public void beforeAnalysis(BeforeAnalysisAccessImpl beforeAnalysisAccess) {
@@ -444,6 +458,12 @@ private void registerMethod(ConfigurationCondition cnd, boolean queriedOnly, Exe
444458

445459
AnalysisMethod analysisMethod = metaAccess.lookupJavaMethod(reflectExecutable);
446460
AnalysisType declaringType = analysisMethod.getDeclaringClass();
461+
462+
if (layeredReflectionDataBuilder != null && layeredReflectionDataBuilder.isMethodRegistered(analysisMethod)) {
463+
/* GR-66387: The runtime condition should be combined across layers. */
464+
return;
465+
}
466+
447467
var classMethods = registeredMethods.computeIfAbsent(declaringType, t -> new ConcurrentHashMap<>());
448468
var shouldRegisterReachabilityHandler = classMethods.isEmpty();
449469

@@ -606,6 +626,11 @@ private void registerField(ConfigurationCondition cnd, boolean queriedOnly, Fiel
606626
AnalysisField analysisField = metaAccess.lookupJavaField(reflectField);
607627
AnalysisType declaringClass = analysisField.getDeclaringClass();
608628

629+
if (layeredReflectionDataBuilder != null && layeredReflectionDataBuilder.isFieldRegistered(analysisField)) {
630+
/* GR-66387: The runtime condition should be combined across layers. */
631+
return;
632+
}
633+
609634
var classFields = registeredFields.computeIfAbsent(declaringClass, t -> new ConcurrentHashMap<>());
610635
boolean exists = classFields.containsKey(analysisField);
611636
boolean shouldRegisterReachabilityHandler = classFields.isEmpty();
@@ -1391,4 +1416,98 @@ public static void registerField(ReflectionDataBuilder reflectionDataBuilder, bo
13911416
reflectionDataBuilder.runConditionalInAnalysisTask(ConfigurationCondition.alwaysTrue(), (cnd) -> reflectionDataBuilder.registerField(cnd, queriedOnly, field));
13921417
}
13931418
}
1419+
1420+
@AutomaticallyRegisteredImageSingleton(onlyWith = BuildingImageLayerPredicate.class)
1421+
public static class LayeredReflectionDataBuilder implements LayeredImageSingleton {
1422+
public static final String METHODS = "methods";
1423+
public static final String FIELDS = "fields";
1424+
public static final String REFLECTION_DATA_BUILDER = "reflection data builder";
1425+
public static final String REFLECTION_DATA_BUILDER_CLASSES = REFLECTION_DATA_BUILDER + " classes";
1426+
/**
1427+
* The methods registered for reflection in the previous layers. The key of the map is the
1428+
* id of the declaring type and the set contains the method ids.
1429+
*/
1430+
private final Map<Integer, Set<Integer>> previousLayerRegisteredMethods;
1431+
/**
1432+
* The fields registered for reflection in the previous layers. The key of the map is the id
1433+
* of the declaring type and the set contains the field ids.
1434+
*/
1435+
private final Map<Integer, Set<Integer>> previousLayerRegisteredFields;
1436+
1437+
public LayeredReflectionDataBuilder() {
1438+
this(Map.of(), Map.of());
1439+
}
1440+
1441+
private LayeredReflectionDataBuilder(Map<Integer, Set<Integer>> previousLayerRegisteredMethods, Map<Integer, Set<Integer>> previousLayerRegisteredFields) {
1442+
this.previousLayerRegisteredMethods = previousLayerRegisteredMethods;
1443+
this.previousLayerRegisteredFields = previousLayerRegisteredFields;
1444+
}
1445+
1446+
public static LayeredReflectionDataBuilder singleton() {
1447+
return ImageSingletons.lookup(LayeredReflectionDataBuilder.class);
1448+
}
1449+
1450+
public boolean isMethodRegistered(AnalysisMethod analysisMethod) {
1451+
return isElementRegistered(previousLayerRegisteredMethods, analysisMethod.getDeclaringClass(), analysisMethod.getId());
1452+
}
1453+
1454+
public boolean isFieldRegistered(AnalysisField analysisField) {
1455+
return isElementRegistered(previousLayerRegisteredFields, analysisField.getDeclaringClass(), analysisField.getId());
1456+
}
1457+
1458+
private static boolean isElementRegistered(Map<Integer, Set<Integer>> previousLayerRegisteredElements, AnalysisType declaringClass, int elementId) {
1459+
Set<Integer> previousLayerRegisteredElementIds = previousLayerRegisteredElements.get(declaringClass.getId());
1460+
if (declaringClass.isInBaseLayer() && previousLayerRegisteredElementIds != null) {
1461+
return previousLayerRegisteredElementIds.contains(elementId);
1462+
}
1463+
return false;
1464+
}
1465+
1466+
@Override
1467+
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
1468+
return LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY;
1469+
}
1470+
1471+
@Override
1472+
public PersistFlags preparePersist(ImageSingletonWriter writer) {
1473+
ReflectionDataBuilder reflectionDataBuilder = (ReflectionDataBuilder) ImageSingletons.lookup(RuntimeReflectionSupport.class);
1474+
persistRegisteredElements(writer, reflectionDataBuilder.registeredMethods, AnalysisMethod::getId, METHODS);
1475+
persistRegisteredElements(writer, reflectionDataBuilder.registeredFields, AnalysisField::getId, FIELDS);
1476+
return PersistFlags.CREATE;
1477+
}
1478+
1479+
private static <T, U> void persistRegisteredElements(ImageSingletonWriter writer, Map<AnalysisType, Map<T, U>> registeredElements, Function<T, Integer> getId, String element) {
1480+
List<Integer> classes = new ArrayList<>();
1481+
for (var entry : registeredElements.entrySet()) {
1482+
classes.add(entry.getKey().getId());
1483+
writer.writeIntList(getElementKeyName(element, entry.getKey().getId()), entry.getValue().keySet().stream().map(getId).toList());
1484+
}
1485+
writer.writeIntList(getClassesKeyName(element), classes);
1486+
}
1487+
1488+
@SuppressWarnings("unused")
1489+
public static Object createFromLoader(ImageSingletonLoader loader) {
1490+
var previousLayerRegisteredMethods = loadRegisteredElements(loader, METHODS);
1491+
var previousLayerRegisteredFields = loadRegisteredElements(loader, FIELDS);
1492+
return new LayeredReflectionDataBuilder(previousLayerRegisteredMethods, previousLayerRegisteredFields);
1493+
}
1494+
1495+
private static Map<Integer, Set<Integer>> loadRegisteredElements(ImageSingletonLoader loader, String element) {
1496+
Map<Integer, Set<Integer>> previousLayerRegisteredElements = new HashMap<>();
1497+
var classes = loader.readIntList(getClassesKeyName(element));
1498+
for (int key : classes) {
1499+
var elements = loader.readIntList(getElementKeyName(element, key)).stream().collect(Collectors.toUnmodifiableSet());
1500+
previousLayerRegisteredElements.put(key, elements);
1501+
}
1502+
return Collections.unmodifiableMap(previousLayerRegisteredElements);
1503+
}
1504+
1505+
private static String getClassesKeyName(String element) {
1506+
return REFLECTION_DATA_BUILDER_CLASSES + " " + element;
1507+
}
1508+
1509+
private static String getElementKeyName(String element, int typeId) {
1510+
return REFLECTION_DATA_BUILDER + " " + element + " " + typeId;
1511+
}
1512+
}
13941513
}

0 commit comments

Comments
 (0)