Skip to content

Commit 7fc30ab

Browse files
committed
Do not require negative queries for impossible class names
1 parent 5ddfcb9 commit 7fc30ab

File tree

9 files changed

+39
-36
lines changed

9 files changed

+39
-36
lines changed

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ This changelog summarizes major changes to GraalVM Native Image.
2020
* (GR-64584) Experimental option `-H:+RelativeCodePointers` to significantly reduce relocation entries in position-independent executables and shared libraries.
2121
* (GR-60238) JNI registration is now included as part of the `"reflection"` section of _reachability-metadata.json_ using the `"jniAccessible"` attribute. Registrations performed through the `"jni"` section of _reachability-metadata.json_ and through _jni-config.json_ will still be accepted and parsed correctly.
2222
* (GR-65008) Remove the sequential reachability handler. The only remaining variant is the concurrent reachability handler, which has been the default implementation since its introduction. Additionally, remove the `-H:-RunReachabilityHandlersConcurrently` option which was introduced to simplify migration and has been deprecated since Version 24.0.0.
23+
* (GR-63268) Reflection and JNI queries do not require metadata entries to throw the expected JDK exception when querying a class that doesn't exist under `--exact-reachability-metadata` if the query cannot possibly be a valid class name
2324

2425
## GraalVM for JDK 24 (Internal Version 24.2.0)
2526
* (GR-59717) Added `DuringSetupAccess.registerObjectReachabilityHandler` to allow registering a callback that is executed when an object of a specified type is marked as reachable during heap scanning.

substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/JniCallInterceptor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,10 @@ private static JNIObjectHandle findClass(JNIEnvironment env, CCharPointer name)
137137
result = nullHandle();
138138
}
139139
if (shouldTrace()) {
140-
traceCall(env, "FindClass", nullHandle(), nullHandle(), callerClass, name.notEqual(nullHandle()), state, fromCString(name));
140+
String className = fromCString(name);
141+
if (className != null) {
142+
traceCall(env, "FindClass", nullHandle(), nullHandle(), callerClass, name.notEqual(nullHandle()), state, className);
143+
}
141144
}
142145
return result;
143146
}

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ClassNameSupport.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,13 @@ private static boolean isValidFullyQualifiedClassName(String name, int startInde
182182
return false;
183183
}
184184
lastPackageSeparatorIndex = i;
185-
} else if (!Character.isJavaIdentifierPart(current)) {
185+
} else if (current == '.' || current == ';' || current == '[' || current == '/') {
186+
/*
187+
* Some special characters are allowed in class files while not being permitted as
188+
* code identifiers (e.g. '+', '-', ',').
189+
*
190+
* @see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.2.2
191+
*/
186192
return false;
187193
}
188194
}

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationSet.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ public static List<Path> writeConfigurationToAllPaths(Function<ConfigurationFile
151151
for (Path path : configFilePathResolver.apply(reachabilityMetadataFile)) {
152152
writtenFiles.add(path);
153153
JsonWriter writer = new JsonPrettyWriter(path);
154-
writer.appendObjectStart();
155154
boolean first = true;
156155
for (ConfigurationFile configFile : ConfigurationFile.agentGeneratedFiles()) {
157156
JsonPrintable configuration = configSupplier.apply(configFile);
@@ -172,14 +171,19 @@ public static List<Path> writeConfigurationToAllPaths(Function<ConfigurationFile
172171
continue;
173172
}
174173
if (first) {
174+
writer.appendObjectStart();
175175
first = false;
176176
} else {
177177
writer.appendSeparator();
178178
}
179179
printConfigurationToCombinedFile(configSupplier.apply(configFile), configFile, writer);
180180
}
181181
}
182-
writer.appendObjectEnd();
182+
if (first) {
183+
writer.append("{}");
184+
} else {
185+
writer.appendObjectEnd();
186+
}
183187
writer.close();
184188
}
185189
return writtenFiles;

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,17 @@ void processEntry(EconomicMap<String, Object> entry, ConfigurationSet configurat
6666
// Special: FindClass and DefineClass take the class in question as a string argument
6767
if (function.equals("FindClass") || function.equals("DefineClass")) {
6868
String jniName = singleElement(args);
69-
ConfigurationTypeDescriptor type = NamedConfigurationTypeDescriptor.fromJNIName(jniName);
70-
LazyValue<String> reflectionName = lazyValue(ClassNameSupport.jniNameToReflectionName(jniName));
71-
if (!advisor.shouldIgnore(reflectionName, callerClassLazyValue, entry)) {
72-
if (function.equals("FindClass")) {
73-
if (!advisor.shouldIgnoreJniLookup(function, reflectionName, lazyNull(), lazyNull(), callerClassLazyValue, entry)) {
74-
configurationSet.getReflectionConfiguration().getOrCreateType(condition, type).setJniAccessible();
69+
if (ClassNameSupport.isValidJNIName(jniName)) {
70+
ConfigurationTypeDescriptor type = NamedConfigurationTypeDescriptor.fromJNIName(jniName);
71+
LazyValue<String> reflectionName = lazyValue(ClassNameSupport.jniNameToReflectionName(jniName));
72+
if (!advisor.shouldIgnore(reflectionName, callerClassLazyValue, entry)) {
73+
if (function.equals("FindClass")) {
74+
if (!advisor.shouldIgnoreJniLookup(function, reflectionName, lazyNull(), lazyNull(), callerClassLazyValue, entry)) {
75+
configurationSet.getReflectionConfiguration().getOrCreateType(condition, type).setJniAccessible();
76+
}
77+
} else if (!AccessAdvisor.PROXY_CLASS_NAME_PATTERN.matcher(jniName).matches()) { // DefineClass
78+
LogUtils.warning("Unsupported JNI function DefineClass used to load class " + jniName);
7579
}
76-
} else if (!AccessAdvisor.PROXY_CLASS_NAME_PATTERN.matcher(jniName).matches()) { // DefineClass
77-
LogUtils.warning("Unsupported JNI function DefineClass used to load class " + jniName);
7880
}
7981
}
8082
return;

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import org.graalvm.collections.EconomicMap;
3434

35+
import com.oracle.svm.configure.ClassNameSupport;
3536
import com.oracle.svm.configure.ConfigurationTypeDescriptor;
3637
import com.oracle.svm.configure.NamedConfigurationTypeDescriptor;
3738
import com.oracle.svm.configure.ProxyConfigurationTypeDescriptor;
@@ -101,7 +102,8 @@ public void processEntry(EconomicMap<String, Object> entry, ConfigurationSet con
101102
name = MetaUtil.internalNameToJava(MetaUtil.toInternalName(name), true, true);
102103
}
103104
if (!advisor.shouldIgnore(lazyValue(name), lazyValue(callerClass), entry) &&
104-
!(isLoadClass && advisor.shouldIgnoreLoadClass(lazyValue(name), lazyValue(callerClass), entry))) {
105+
!(isLoadClass && advisor.shouldIgnoreLoadClass(lazyValue(name), lazyValue(callerClass), entry)) &&
106+
ClassNameSupport.isValidReflectionName(name)) {
105107
configuration.getOrCreateType(condition, NamedConfigurationTypeDescriptor.fromReflectionName(name));
106108
}
107109
return;

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

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -246,18 +246,7 @@ private Object forName0(String className, ClassLoader classLoader) {
246246
result = PredefinedClassesSupport.getLoadedForNameOrNull(className, classLoader);
247247
}
248248
if (result == null && !ClassNameSupport.isValidReflectionName(className)) {
249-
if (result == null && ClassNameSupport.isValidJNIName(className)) {
250-
var jniAlias = knownClasses.get(ClassNameSupport.jniNameToReflectionName(className));
251-
if (jniAlias != null && jniAlias.getValue() != null) {
252-
result = NEGATIVE_QUERY;
253-
}
254-
}
255-
if (result == null && ClassNameSupport.isValidTypeName(className)) {
256-
var typeAlias = knownClasses.get(ClassNameSupport.typeNameToReflectionName(className));
257-
if (typeAlias != null && typeAlias.getValue() != null) {
258-
result = NEGATIVE_QUERY;
259-
}
260-
}
249+
result = NEGATIVE_QUERY;
261250
}
262251
return result == NEGATIVE_QUERY ? new ClassNotFoundException(className) : result;
263252
}

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

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,9 @@ public static Class<?> getClassObjectByName(CharSequence name) {
180180
for (var dictionary : layeredSingletons()) {
181181
JNIAccessibleClass clazz = dictionary.classesByName.get(name);
182182
if (clazz == null && !ClassNameSupport.isValidJNIName(name.toString())) {
183-
if (clazz == null && ClassNameSupport.isValidReflectionName(name.toString()) && dictionary.classesByName.containsKey(ClassNameSupport.reflectionNameToJNIName(name.toString()))) {
184-
clazz = NEGATIVE_CLASS_LOOKUP;
185-
}
186-
if (clazz == null && ClassNameSupport.isValidTypeName(name.toString()) && dictionary.classesByName.containsKey(ClassNameSupport.typeNameToJNIName(name.toString()))) {
187-
clazz = NEGATIVE_CLASS_LOOKUP;
188-
}
183+
clazz = NEGATIVE_CLASS_LOOKUP;
189184
}
190-
clazz = checkClass(clazz, name);
185+
clazz = checkClass(clazz, name.toString());
191186
if (clazz != null) {
192187
return clazz.getClassObject();
193188
}
@@ -196,9 +191,9 @@ public static Class<?> getClassObjectByName(CharSequence name) {
196191
return null;
197192
}
198193

199-
private static JNIAccessibleClass checkClass(JNIAccessibleClass clazz, CharSequence name) {
194+
private static JNIAccessibleClass checkClass(JNIAccessibleClass clazz, String name) {
200195
if (throwMissingRegistrationErrors() && clazz == null) {
201-
MissingJNIRegistrationUtils.forClass(name.toString());
196+
MissingJNIRegistrationUtils.forClass(name);
202197
} else if (clazz != null && clazz.isNegative()) {
203198
return null;
204199
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,11 @@ private void registerFields(boolean finalIsWritable, Field[] fields) {
257257
}
258258

259259
@Override
260-
public void registerClassLookup(ConfigurationCondition condition, String jniName) {
260+
public void registerClassLookup(ConfigurationCondition condition, String reflectionName) {
261261
try {
262-
register(condition, false, Class.forName(ClassNameSupport.jniNameToReflectionName(jniName)));
262+
register(condition, false, Class.forName(reflectionName));
263263
} catch (ClassNotFoundException e) {
264+
String jniName = ClassNameSupport.reflectionNameToJNIName(reflectionName);
264265
newNegativeClassLookups.add(jniName);
265266
}
266267
}

0 commit comments

Comments
 (0)