Skip to content

Commit 7450b85

Browse files
committed
Track preserved JNI elements
1 parent 6efd91e commit 7450b85

File tree

9 files changed

+128
-53
lines changed

9 files changed

+128
-53
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleClass.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,21 @@
3939
/**
4040
* Information on a class that can be looked up and accessed via JNI.
4141
*/
42-
public final class JNIAccessibleClass {
42+
public final class JNIAccessibleClass extends JNIAccessibleElement {
4343
private final Class<?> classObject;
4444
private EconomicMap<JNIAccessibleMethodDescriptor, JNIAccessibleMethod> methods;
4545
private EconomicMap<CharSequence, JNIAccessibleField> fields;
4646

4747
@Platforms(HOSTED_ONLY.class)
48-
public JNIAccessibleClass(Class<?> clazz) {
48+
public JNIAccessibleClass(Class<?> clazz, boolean preserved) {
49+
super(preserved);
4950
assert clazz != null;
5051
this.classObject = clazz;
5152
}
5253

5354
@Platforms(HOSTED_ONLY.class)
5455
JNIAccessibleClass() {
56+
super(false);
5557
/* For negative queries */
5658
this.classObject = null;
5759
}
@@ -73,22 +75,28 @@ public JNIAccessibleField getField(CharSequence name) {
7375
}
7476

7577
@Platforms(HOSTED_ONLY.class)
76-
public void addFieldIfAbsent(String name, Function<String, JNIAccessibleField> mappingFunction) {
78+
public void addOrUpdateField(String name, boolean updatedPreserved, Function<String, JNIAccessibleField> mappingFunction) {
7779
if (fields == null) {
7880
fields = ImageHeapMap.createNonLayeredMap(JNIReflectionDictionary.WRAPPED_CSTRING_EQUIVALENCE);
7981
}
80-
if (!fields.containsKey(name)) {
82+
JNIAccessibleField existing = fields.get(name);
83+
if (existing == null) {
8184
fields.put(name, mappingFunction.apply(name));
85+
} else {
86+
existing.reportReregistered(updatedPreserved);
8287
}
8388
}
8489

8590
@Platforms(HOSTED_ONLY.class)
86-
public void addMethodIfAbsent(JNIAccessibleMethodDescriptor descriptor, Function<JNIAccessibleMethodDescriptor, JNIAccessibleMethod> mappingFunction) {
91+
public void addOrUpdateMethod(JNIAccessibleMethodDescriptor descriptor, boolean updatedPreserved, Function<JNIAccessibleMethodDescriptor, JNIAccessibleMethod> mappingFunction) {
8792
if (methods == null) {
8893
methods = ImageHeapMap.createNonLayeredMap();
8994
}
90-
if (!methods.containsKey(descriptor)) {
95+
JNIAccessibleMethod existing = methods.get(descriptor);
96+
if (existing == null) {
9197
methods.put(descriptor, mappingFunction.apply(descriptor));
98+
} else {
99+
existing.reportReregistered(updatedPreserved);
92100
}
93101
}
94102

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.jni.access;
26+
27+
public abstract class JNIAccessibleElement {
28+
private volatile boolean preserved;
29+
30+
protected JNIAccessibleElement(boolean preserved) {
31+
this.preserved = preserved;
32+
}
33+
34+
public void reportReregistered(boolean reregisterPreserved) {
35+
// State can only ever go from "preserved" to "not preserved".
36+
if (!reregisterPreserved) {
37+
this.preserved = false;
38+
}
39+
}
40+
41+
public boolean isPreserved() {
42+
return preserved;
43+
}
44+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleField.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public final class JNIAccessibleField extends JNIAccessibleMember {
6161
private static final UnsignedWord ID_OFFSET_MASK = ID_LAYER_NUMBER_MASK.subtract(1);
6262

6363
public static JNIAccessibleField negativeFieldQuery(JNIAccessibleClass jniClass) {
64-
return new JNIAccessibleField(jniClass, null, 0);
64+
return new JNIAccessibleField(false, jniClass, null, 0);
6565
}
6666

6767
/**
@@ -116,8 +116,8 @@ private static int getLayerNumberFromId(UnsignedWord id) {
116116
private UnsignedWord id = Word.zero();
117117

118118
@Platforms(HOSTED_ONLY.class)
119-
public JNIAccessibleField(JNIAccessibleClass declaringClass, JavaKind kind, int modifiers) {
120-
super(declaringClass);
119+
public JNIAccessibleField(boolean preserved, JNIAccessibleClass declaringClass, JavaKind kind, int modifiers) {
120+
super(preserved, declaringClass);
121121

122122
UnsignedWord bits = Modifier.isStatic(modifiers) ? ID_STATIC_FLAG : Word.zero();
123123
if (kind == null) {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMember.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@
3131
import com.oracle.svm.core.BuildPhaseProvider.ReadyForCompilation;
3232
import com.oracle.svm.core.heap.UnknownObjectField;
3333

34-
abstract class JNIAccessibleMember {
34+
abstract class JNIAccessibleMember extends JNIAccessibleElement {
3535
private final JNIAccessibleClass declaringClass;
3636

3737
@UnknownObjectField(fullyQualifiedTypes = "org.graalvm.collections.EconomicMapImpl", canBeNull = true, availability = ReadyForCompilation.class) //
3838
private EconomicSet<Class<?>> hidingSubclasses;
3939

40-
JNIAccessibleMember(JNIAccessibleClass declaringClass) {
40+
JNIAccessibleMember(boolean preserved, JNIAccessibleClass declaringClass) {
41+
super(preserved);
4142
this.declaringClass = declaringClass;
4243
}
4344

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethod.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public final class JNIAccessibleMethod extends JNIAccessibleMember {
5757
public static final int NEW_OBJECT_TARGET_INVALID_FOR_ABSTRACT_TYPE = -1;
5858

5959
public static JNIAccessibleMethod negativeMethodQuery(JNIAccessibleClass jniClass) {
60-
return new JNIAccessibleMethod(jniClass, RuntimeMetadataDecoderImpl.NEGATIVE_FLAG_MASK);
60+
return new JNIAccessibleMethod(false, jniClass, RuntimeMetadataDecoderImpl.NEGATIVE_FLAG_MASK);
6161
}
6262

6363
@Platforms(HOSTED_ONLY.class)
@@ -104,8 +104,8 @@ public static ResolvedJavaField getCallVariantWrapperField(MetaAccessProvider me
104104
@SuppressWarnings("unused") private CodePointer valistNonvirtualWrapper;
105105

106106
@Platforms(HOSTED_ONLY.class)
107-
public JNIAccessibleMethod(JNIAccessibleClass declaringClass, int modifiers) {
108-
super(declaringClass);
107+
public JNIAccessibleMethod(boolean preserved, JNIAccessibleClass declaringClass, int modifiers) {
108+
super(preserved, declaringClass);
109109
this.modifiers = modifiers;
110110
}
111111

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleMethodDescriptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public static JNIAccessibleMethodDescriptor of(String methodName, Class<?>[] par
6969
return of(methodName, parameterTypes, null);
7070
}
7171

72-
private static JNIAccessibleMethodDescriptor of(String methodName, Class<?>[] parameterTypes, Class<?> returnType) {
72+
public static JNIAccessibleMethodDescriptor of(String methodName, Class<?>[] parameterTypes, Class<?> returnType) {
7373
StringBuilder sb = new StringBuilder("(");
7474
for (Class<?> type : parameterTypes) {
7575
sb.append(MetaUtil.toInternalName(type.getName()));

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIReflectionDictionary.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,18 @@ private static void dump(boolean condition, String label) {
155155
}
156156

157157
@Platforms(HOSTED_ONLY.class)
158-
public JNIAccessibleClass addClassIfAbsent(Class<?> classObj, Function<Class<?>, JNIAccessibleClass> mappingFunction) {
159-
if (!classesByClassObject.containsKey(classObj)) {
158+
public JNIAccessibleClass addOrUpdateClass(Class<?> classObj, boolean updatedPreserved, Function<Class<?>, JNIAccessibleClass> mappingFunction) {
159+
JNIAccessibleClass existing = classesByClassObject.get(classObj);
160+
if (existing == null) {
160161
JNIAccessibleClass instance = mappingFunction.apply(classObj);
161162
classesByClassObject.put(classObj, instance);
162163
String name = instance.getJNIName();
163164
classesByName.put(name, instance);
165+
return instance;
166+
} else {
167+
existing.reportReregistered(updatedPreserved);
168+
return existing;
164169
}
165-
return classesByClassObject.get(classObj);
166170
}
167171

168172
@Platforms(HOSTED_ONLY.class)
@@ -181,6 +185,15 @@ public Iterable<JNIAccessibleClass> getClasses() {
181185
}
182186

183187
public static Class<?> getClassObjectByName(CharSequence name) {
188+
JNIAccessibleClass clazz = getJniAccessibleClass(name);
189+
if (clazz != null) {
190+
return clazz.getClassObject();
191+
}
192+
dump(true, "getClassObjectByName");
193+
return null;
194+
}
195+
196+
public static JNIAccessibleClass getJniAccessibleClass(CharSequence name) {
184197
for (var dictionary : layeredSingletons()) {
185198
JNIAccessibleClass clazz = dictionary.classesByName.get(name);
186199
if (clazz == null && !ClassNameSupport.isValidJNIName(name.toString())) {
@@ -189,12 +202,8 @@ public static Class<?> getClassObjectByName(CharSequence name) {
189202
// trace if class exists (positive query) or name is valid (negative query)
190203
MetadataTracer.singleton().traceJNIType(ClassNameSupport.jniNameToTypeName(name.toString()));
191204
}
192-
clazz = checkClass(clazz, name.toString());
193-
if (clazz != null) {
194-
return clazz.getClassObject();
195-
}
205+
return checkClass(clazz, name.toString());
196206
}
197-
dump(true, "getClassObjectByName");
198207
return null;
199208
}
200209

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,7 @@ protected boolean delegateLoadMethod(PersistedAnalysisMethod.Reader methodData)
940940
if (wm.isReflectionExpandSignature()) {
941941
ImageSingletons.lookup(ReflectionFeature.class).getOrCreateAccessor(member);
942942
} else if (wm.isJavaCallVariantWrapper()) {
943-
JNIAccessFeature.singleton().addMethod(member, (FeatureImpl.DuringAnalysisAccessImpl) universe.getConcurrentAnalysisAccess());
943+
JNIAccessFeature.singleton().addMethod(member, false, (FeatureImpl.DuringAnalysisAccessImpl) universe.getConcurrentAnalysisAccess());
944944
}
945945
return true;
946946
} else if (wrappedMethod.isPolymorphicSignature()) {

0 commit comments

Comments
 (0)