Skip to content

Commit 43c0d7a

Browse files
committed
Track preserved fields and methods
1 parent abe0958 commit 43c0d7a

File tree

6 files changed

+79
-31
lines changed

6 files changed

+79
-31
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeMetadataDecoderImpl.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,20 @@ public class RuntimeMetadataDecoderImpl implements RuntimeMetadataDecoder {
7171
* Error indices are less than {@link #NO_DATA}.
7272
*/
7373
public static final int FIRST_ERROR_INDEX = NO_DATA - 1;
74-
public static final int NO_METHOD_METADATA = -1;
74+
private static final int NO_METHOD_METADATA = -1;
7575
public static final int NULL_OBJECT = -1;
76-
public static final int COMPLETE_FLAG_INDEX = 31;
76+
private static final int COMPLETE_FLAG_INDEX = 31;
7777
public static final int COMPLETE_FLAG_MASK = 1 << COMPLETE_FLAG_INDEX;
78-
public static final int IN_HEAP_FLAG_INDEX = 30;
78+
private static final int IN_HEAP_FLAG_INDEX = 30;
7979
public static final int IN_HEAP_FLAG_MASK = 1 << IN_HEAP_FLAG_INDEX;
80-
public static final int HIDING_FLAG_INDEX = 29;
80+
private static final int HIDING_FLAG_INDEX = 29;
8181
public static final int HIDING_FLAG_MASK = 1 << HIDING_FLAG_INDEX;
82-
public static final int NEGATIVE_FLAG_INDEX = 28;
82+
private static final int NEGATIVE_FLAG_INDEX = 28;
8383
public static final int NEGATIVE_FLAG_MASK = 1 << NEGATIVE_FLAG_INDEX;
84+
private static final int PRESERVED_FLAG_INDEX = 27;
85+
public static final int PRESERVED_FLAG_MASK = 1 << PRESERVED_FLAG_INDEX;
8486
/* single lookup flags are filled before encoding */
85-
public static final int ALL_FLAGS_MASK = COMPLETE_FLAG_MASK | IN_HEAP_FLAG_MASK | HIDING_FLAG_MASK | NEGATIVE_FLAG_MASK;
87+
public static final int ALL_FLAGS_MASK = COMPLETE_FLAG_MASK | IN_HEAP_FLAG_MASK | HIDING_FLAG_MASK | NEGATIVE_FLAG_MASK | PRESERVED_FLAG_MASK;
8688

8789
public static final int ALL_FIELDS_FLAG = 1 << 16;
8890
public static final int ALL_DECLARED_FIELDS_FLAG = 1 << 17;
@@ -275,6 +277,11 @@ public boolean isNegative(int modifiers) {
275277
return (modifiers & NEGATIVE_FLAG_MASK) != 0;
276278
}
277279

280+
@Override
281+
public boolean isPreserved(int modifiers) {
282+
return (modifiers & PRESERVED_FLAG_MASK) != 0;
283+
}
284+
278285
public static boolean isErrorIndex(int index) {
279286
return index < NO_DATA;
280287
}
@@ -347,8 +354,9 @@ private static Object decodeField(UnsafeArrayTypeReader buf, Class<?> declaringC
347354
int modifiers = buf.getUVInt();
348355
boolean inHeap = (modifiers & IN_HEAP_FLAG_MASK) != 0;
349356
boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0;
357+
boolean preserved = (modifiers & PRESERVED_FLAG_MASK) != 0;
350358

351-
RuntimeConditionSet conditions = decodeConditions(buf, layerId);
359+
RuntimeConditionSet conditions = decodeConditions(buf, layerId, preserved);
352360
if (inHeap) {
353361
Field field = (Field) decodeObject(buf, layerId);
354362
if (publicOnly && !Modifier.isPublic(field.getModifiers())) {
@@ -402,9 +410,9 @@ private static Object decodeField(UnsafeArrayTypeReader buf, Class<?> declaringC
402410
return reflectOnly ? reflectField : new FieldDescriptor(reflectField);
403411
}
404412

405-
private static RuntimeConditionSet decodeConditions(UnsafeArrayTypeReader buf, int layerId) {
413+
private static RuntimeConditionSet decodeConditions(UnsafeArrayTypeReader buf, int layerId, boolean preserved) {
406414
var conditionTypes = decodeArray(buf, Class.class, _ -> decodeType(buf, layerId), layerId);
407-
return RuntimeConditionSet.createDecoded(conditionTypes, false);
415+
return RuntimeConditionSet.createDecoded(conditionTypes, preserved);
408416
}
409417

410418
/**
@@ -514,7 +522,8 @@ private static Object decodeExecutable(UnsafeArrayTypeReader buf, Class<?> decla
514522
int modifiers = buf.getUVInt();
515523
boolean inHeap = (modifiers & IN_HEAP_FLAG_MASK) != 0;
516524
boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0;
517-
RuntimeConditionSet conditions = decodeConditions(buf, layerId);
525+
boolean preserved = (modifiers & PRESERVED_FLAG_MASK) != 0;
526+
RuntimeConditionSet conditions = decodeConditions(buf, layerId, preserved);
518527
if (inHeap) {
519528
Executable executable = (Executable) decodeObject(buf, layerId);
520529
if (publicOnly && !Modifier.isPublic(executable.getModifiers())) {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConditionalRuntimeValue.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* @param <T> type of the stored value.
3838
*/
3939
public final class ConditionalRuntimeValue<T> {
40-
RuntimeConditionSet conditions;
40+
final RuntimeConditionSet conditions;
4141
volatile T value;
4242

4343
public ConditionalRuntimeValue(RuntimeConditionSet conditions, T value) {

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

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -178,18 +178,18 @@ public static boolean respectClassLoader() {
178178

179179
@Platforms(Platform.HOSTED_ONLY.class)
180180
public void registerClass(Class<?> clazz, ClassLoader runtimeClassLoader) {
181-
registerClass(AccessCondition.unconditional(), clazz, runtimeClassLoader);
181+
registerClass(AccessCondition.unconditional(), clazz, runtimeClassLoader, false);
182182
}
183183

184184
@Platforms(Platform.HOSTED_ONLY.class)
185-
public void registerClass(AccessCondition condition, Class<?> clazz, ClassLoader runtimeClassLoader) {
185+
public void registerClass(AccessCondition condition, Class<?> clazz, ClassLoader runtimeClassLoader, boolean preserved) {
186186
assert !clazz.isPrimitive() : "primitive classes cannot be looked up by name";
187187
if (PredefinedClassesSupport.isPredefined(clazz)) {
188188
return; // must be defined at runtime before it can be looked up
189189
}
190190
String name = clazz.getName();
191191
if (respectClassLoader()) {
192-
registerKnownClassName(condition, name);
192+
registerKnownClassName(condition, name, preserved);
193193
Class<?> elemental = clazz;
194194
while (elemental.isArray()) {
195195
elemental = elemental.getComponentType();
@@ -206,10 +206,10 @@ public void registerClass(AccessCondition condition, Class<?> clazz, ClassLoader
206206
currentValue == NEGATIVE_QUERY ||
207207
currentValue == clazz) {
208208
currentValue = clazz;
209-
var cond = updateConditionalValue(existingEntry, currentValue, condition);
209+
var cond = updateConditionalValue(existingEntry, currentValue, condition, preserved);
210210
addKnownClass(name, cond);
211211
} else if (currentValue instanceof Throwable) { // failed at linking time
212-
var cond = updateConditionalValue(existingEntry, currentValue, condition);
212+
var cond = updateConditionalValue(existingEntry, currentValue, condition, preserved);
213213
/*
214214
* If the class has already been seen as throwing an error, we don't overwrite
215215
* this error. Nevertheless, we have to update the set of conditionals to be
@@ -242,23 +242,24 @@ private void addKnownClass(String name, Consumer<EconomicMap<String, Conditional
242242
}
243243

244244
public static ConditionalRuntimeValue<Object> updateConditionalValue(ConditionalRuntimeValue<Object> existingConditionalValue, Object newValue,
245-
AccessCondition additionalCondition) {
245+
AccessCondition additionalCondition, boolean preserved) {
246246
if (existingConditionalValue == null) {
247-
return new ConditionalRuntimeValue<>(RuntimeConditionSet.createHosted(additionalCondition, false), newValue);
247+
return new ConditionalRuntimeValue<>(RuntimeConditionSet.createHosted(additionalCondition, preserved), newValue);
248248
} else {
249249
existingConditionalValue.getConditions().addCondition(additionalCondition);
250+
existingConditionalValue.getConditions().reportReregistered(preserved);
250251
existingConditionalValue.updateValue(newValue);
251252
return existingConditionalValue;
252253
}
253254
}
254255

255256
@Platforms(Platform.HOSTED_ONLY.class)
256-
public void registerExceptionForClass(AccessCondition condition, String className, Throwable t) {
257+
public void registerExceptionForClass(AccessCondition condition, String className, Throwable t, boolean preserved) {
257258
if (RuntimeClassLoading.isSupported()) {
258259
return;
259260
}
260261
if (respectClassLoader()) {
261-
registerKnownClassName(condition, className);
262+
registerKnownClassName(condition, className, preserved);
262263
synchronized (knownExceptions) {
263264
knownExceptions.put(className, t);
264265
}
@@ -270,7 +271,7 @@ public void registerExceptionForClass(AccessCondition condition, String classNam
270271
@Platforms(Platform.HOSTED_ONLY.class)
271272
public void registerNegativeQuery(AccessCondition condition, String className) {
272273
if (respectClassLoader()) {
273-
registerKnownClassName(condition, className);
274+
registerKnownClassName(condition, className, false);
274275
} else {
275276
/*
276277
* If the class is not accessible by the builder class loader, but was already
@@ -281,14 +282,15 @@ public void registerNegativeQuery(AccessCondition condition, String className) {
281282
}
282283
}
283284

284-
private void registerKnownClassName(AccessCondition condition, String className) {
285+
private void registerKnownClassName(AccessCondition condition, String className, boolean preserved) {
285286
assert respectClassLoader();
286287
synchronized (knownClassNames) {
287288
RuntimeConditionSet existingConditions = knownClassNames.get(className);
288289
if (existingConditions == null) {
289-
knownClassNames.put(className, RuntimeConditionSet.createHosted(condition, false));
290+
knownClassNames.put(className, RuntimeConditionSet.createHosted(condition, preserved));
290291
} else {
291292
existingConditions.addCondition(condition);
293+
existingConditions.reportReregistered(preserved);
292294
}
293295
}
294296
}
@@ -442,6 +444,25 @@ public static RuntimeConditionSet getConditionFor(Class<?> jClass) {
442444
}
443445
}
444446

447+
public static boolean isPreserved(Class<?> jClass) {
448+
Objects.requireNonNull(jClass);
449+
String jClassName = jClass.getName();
450+
if (respectClassLoader()) {
451+
RuntimeConditionSet conditionSet = getConditionForName(jClassName);
452+
if (conditionSet == null) {
453+
return false;
454+
}
455+
return conditionSet.preserved();
456+
}
457+
for (var singleton : layeredSingletons()) {
458+
ConditionalRuntimeValue<Object> conditionalClass = singleton.knownClasses.get(jClassName);
459+
if (conditionalClass != null) {
460+
return conditionalClass.getConditions().preserved();
461+
}
462+
}
463+
return false;
464+
}
465+
445466
public static boolean isRegisteredClass(String className) {
446467
if (respectClassLoader()) {
447468
RuntimeConditionSet conditionSet = getConditionForName(className);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/RuntimeMetadataDecoder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ public interface RuntimeMetadataDecoder {
7474

7575
boolean isNegative(int modifiers);
7676

77+
boolean isPreserved(int modifiers);
78+
7779
class ElementDescriptor {
7880
private final Class<?> declaringClass;
7981

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/RuntimeMetadataEncoderImpl.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import static com.oracle.svm.core.code.RuntimeMetadataDecoderImpl.IN_HEAP_FLAG_MASK;
3636
import static com.oracle.svm.core.code.RuntimeMetadataDecoderImpl.NEGATIVE_FLAG_MASK;
3737
import static com.oracle.svm.core.code.RuntimeMetadataDecoderImpl.NULL_OBJECT;
38+
import static com.oracle.svm.core.code.RuntimeMetadataDecoderImpl.PRESERVED_FLAG_MASK;
3839
import static com.oracle.svm.core.reflect.RuntimeMetadataDecoder.NO_DATA;
3940
import static com.oracle.svm.hosted.code.ReflectionRuntimeMetadata.AccessibleObjectMetadata;
4041
import static com.oracle.svm.hosted.code.ReflectionRuntimeMetadata.ClassMetadata;
@@ -451,7 +452,8 @@ public void addReflectionFieldMetadata(MetaAccessProvider metaAccess, HostedFiel
451452
AnnotationValue[] annotations = registerAnnotationValues(analysisField);
452453
TypeAnnotationValue[] typeAnnotations = registerTypeAnnotationValues(analysisField);
453454

454-
registerField(declaringType, reflectField, new FieldMetadata(conditions, declaringType, name, type, modifiers, trustedFinal, signature, annotations, typeAnnotations, offset, deletedReason));
455+
registerField(declaringType, reflectField, new FieldMetadata(conditions, declaringType, name, type, modifiers, trustedFinal, signature, annotations,
456+
typeAnnotations, offset, deletedReason));
455457
}
456458

457459
@Override
@@ -965,6 +967,7 @@ private void encodeField(UnsafeArrayTypeWriter buf, FieldMetadata field) {
965967
modifiers |= field.heapObject != null ? IN_HEAP_FLAG_MASK : 0;
966968
modifiers |= field.hiding ? HIDING_FLAG_MASK : 0;
967969
modifiers |= field.negative ? NEGATIVE_FLAG_MASK : 0;
970+
modifiers |= field.conditions.preserved() ? PRESERVED_FLAG_MASK : 0;
968971
buf.putUV(modifiers);
969972
encodeConditions(buf, field.conditions);
970973

@@ -1000,6 +1003,7 @@ private void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata exec
10001003
modifiers |= executable.heapObject != null ? IN_HEAP_FLAG_MASK : 0;
10011004
modifiers |= isHiding ? HIDING_FLAG_MASK : 0;
10021005
modifiers |= executable.negative ? NEGATIVE_FLAG_MASK : 0;
1006+
modifiers |= executable.conditions.preserved() ? PRESERVED_FLAG_MASK : 0;
10031007
buf.putUV(modifiers);
10041008

10051009
encodeConditions(buf, executable.conditions);

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

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ private void registerClass(AccessCondition condition, Class<?> clazz, boolean un
303303
}
304304

305305
if (allowForName) {
306-
classForNameSupport.registerClass(condition, clazz, ClassLoaderFeature.getRuntimeClassLoader(clazz.getClassLoader()));
306+
classForNameSupport.registerClass(condition, clazz, ClassLoaderFeature.getRuntimeClassLoader(clazz.getClassLoader()), preserved);
307307

308308
if (!MissingRegistrationUtils.throwMissingRegistrationErrors()) {
309309
/*
@@ -327,7 +327,7 @@ private void registerClass(AccessCondition condition, Class<?> clazz, boolean un
327327

328328
@Override
329329
public void registerClassLookupException(AccessCondition condition, String typeName, Throwable t) {
330-
runConditionalInAnalysisTask(condition, (cnd) -> classForNameSupport.registerExceptionForClass(cnd, typeName, t));
330+
runConditionalInAnalysisTask(condition, (cnd) -> classForNameSupport.registerExceptionForClass(cnd, typeName, t, false));
331331
}
332332

333333
@Override
@@ -338,7 +338,7 @@ public void registerClassLookup(AccessCondition condition, boolean preserved, St
338338
} catch (ClassNotFoundException e) {
339339
classForNameSupport.registerNegativeQuery(cnd, typeName);
340340
} catch (Throwable t) {
341-
classForNameSupport.registerExceptionForClass(cnd, typeName, t);
341+
classForNameSupport.registerExceptionForClass(cnd, typeName, t, preserved);
342342
}
343343
});
344344
}
@@ -485,6 +485,8 @@ private void registerMethod(AccessCondition cnd, boolean queriedOnly, boolean pr
485485
conditionalValue = newConditionalValue;
486486
registered = true;
487487
}
488+
} else {
489+
conditionalValue.getConditions().reportReregistered(preserved);
488490
}
489491
if (!queriedOnly) {
490492
/* queryOnly methods are conditioned by the type itself */
@@ -604,11 +606,19 @@ public void registerAllDeclaredFields(AccessCondition condition, boolean preserv
604606
private record AllDeclaredFieldsQuery(AccessCondition condition, boolean queriedOnly, Class<?> clazz) {
605607
}
606608

607-
private Set<AllDeclaredFieldsQuery> existingAllDeclaredFieldsQuery = ConcurrentHashMap.newKeySet();
609+
// Tracks types that have had all declared fields registered for reflection.
610+
// The boolean value in the map tracks whether the registration was preserved.
611+
private Map<AllDeclaredFieldsQuery, Boolean> existingAllDeclaredFieldsQuery = new ConcurrentHashMap<>();
608612

609613
public void registerAllDeclaredFieldsQuery(AccessCondition condition, boolean queriedOnly, boolean preserved, Class<?> clazz) {
610614
final var query = new AllDeclaredFieldsQuery(condition, queriedOnly, clazz);
611-
if (!existingAllDeclaredFieldsQuery.contains(query)) {
615+
/*
616+
* Register the query if it was never registered, or if it was registered by preserve but
617+
* preserved == false (re-register to unmark the fields as preserved). This limits
618+
* registration to at most twice per class.
619+
*/
620+
if (!existingAllDeclaredFieldsQuery.containsKey(query) ||
621+
existingAllDeclaredFieldsQuery.get(query) && !preserved) {
612622
runConditionalInAnalysisTask(condition, (cnd) -> {
613623
setQueryFlag(clazz, ALL_DECLARED_FIELDS_FLAG);
614624
try {
@@ -617,7 +627,7 @@ public void registerAllDeclaredFieldsQuery(AccessCondition condition, boolean qu
617627
registerLinkageError(clazz, e, fieldLookupExceptions);
618628
}
619629
});
620-
existingAllDeclaredFieldsQuery.add(query);
630+
existingAllDeclaredFieldsQuery.put(query, preserved);
621631
}
622632
}
623633

@@ -644,7 +654,9 @@ private void registerField(AccessCondition cnd, boolean queriedOnly, boolean pre
644654
boolean exists = classFields.containsKey(analysisField);
645655
boolean shouldRegisterReachabilityHandler = classFields.isEmpty();
646656
var cndValue = classFields.computeIfAbsent(analysisField, _ -> new ConditionalRuntimeValue<>(RuntimeConditionSet.emptySet(preserved), reflectField));
647-
if (!exists) {
657+
if (exists) {
658+
cndValue.getConditions().reportReregistered(preserved);
659+
} else {
648660
registerTypesForField(analysisField, reflectField, queriedOnly);
649661

650662
/*

0 commit comments

Comments
 (0)