Skip to content

[GR-63268] Do not require negative queries for impossible class names #11240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This changelog summarizes major changes to GraalVM Native Image.
* (GR-64584) Experimental option `-H:+RelativeCodePointers` to significantly reduce relocation entries in position-independent executables and shared libraries.
* (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.
* (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.
* (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

## GraalVM for JDK 24 (Internal Version 24.2.0)
* (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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,10 @@ private static JNIObjectHandle findClass(JNIEnvironment env, CCharPointer name)
result = nullHandle();
}
if (shouldTrace()) {
traceCall(env, "FindClass", nullHandle(), nullHandle(), callerClass, name.notEqual(nullHandle()), state, fromCString(name));
String className = fromCString(name);
if (className != null) {
traceCall(env, "FindClass", nullHandle(), nullHandle(), callerClass, name.notEqual(nullHandle()), state, className);
}
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,13 @@ private static boolean isValidFullyQualifiedClassName(String name, int startInde
return false;
}
lastPackageSeparatorIndex = i;
} else if (!Character.isJavaIdentifierPart(current)) {
} else if (current == '.' || current == ';' || current == '[' || current == '/') {
/*
* Some special characters are allowed in class files while not being permitted as
* code identifiers (e.g. '+', '-', ',').
*
* @see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.2.2
*/
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ public static List<Path> writeConfigurationToAllPaths(Function<ConfigurationFile
for (Path path : configFilePathResolver.apply(reachabilityMetadataFile)) {
writtenFiles.add(path);
JsonWriter writer = new JsonPrettyWriter(path);
writer.appendObjectStart();
boolean first = true;
for (ConfigurationFile configFile : ConfigurationFile.agentGeneratedFiles()) {
JsonPrintable configuration = configSupplier.apply(configFile);
Expand All @@ -172,14 +171,19 @@ public static List<Path> writeConfigurationToAllPaths(Function<ConfigurationFile
continue;
}
if (first) {
writer.appendObjectStart();
first = false;
} else {
writer.appendSeparator();
}
printConfigurationToCombinedFile(configSupplier.apply(configFile), configFile, writer);
}
}
writer.appendObjectEnd();
if (first) {
writer.append("{}");
} else {
writer.appendObjectEnd();
}
writer.close();
}
return writtenFiles;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,17 @@ void processEntry(EconomicMap<String, Object> entry, ConfigurationSet configurat
// Special: FindClass and DefineClass take the class in question as a string argument
if (function.equals("FindClass") || function.equals("DefineClass")) {
String jniName = singleElement(args);
ConfigurationTypeDescriptor type = NamedConfigurationTypeDescriptor.fromJNIName(jniName);
LazyValue<String> reflectionName = lazyValue(ClassNameSupport.jniNameToReflectionName(jniName));
if (!advisor.shouldIgnore(reflectionName, callerClassLazyValue, entry)) {
if (function.equals("FindClass")) {
if (!advisor.shouldIgnoreJniLookup(function, reflectionName, lazyNull(), lazyNull(), callerClassLazyValue, entry)) {
configurationSet.getReflectionConfiguration().getOrCreateType(condition, type).setJniAccessible();
if (ClassNameSupport.isValidJNIName(jniName)) {
ConfigurationTypeDescriptor type = NamedConfigurationTypeDescriptor.fromJNIName(jniName);
LazyValue<String> reflectionName = lazyValue(ClassNameSupport.jniNameToReflectionName(jniName));
if (!advisor.shouldIgnore(reflectionName, callerClassLazyValue, entry)) {
if (function.equals("FindClass")) {
if (!advisor.shouldIgnoreJniLookup(function, reflectionName, lazyNull(), lazyNull(), callerClassLazyValue, entry)) {
configurationSet.getReflectionConfiguration().getOrCreateType(condition, type).setJniAccessible();
}
} else if (!AccessAdvisor.PROXY_CLASS_NAME_PATTERN.matcher(jniName).matches()) { // DefineClass
LogUtils.warning("Unsupported JNI function DefineClass used to load class " + jniName);
}
} else if (!AccessAdvisor.PROXY_CLASS_NAME_PATTERN.matcher(jniName).matches()) { // DefineClass
LogUtils.warning("Unsupported JNI function DefineClass used to load class " + jniName);
}
}
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

import org.graalvm.collections.EconomicMap;

import com.oracle.svm.configure.ClassNameSupport;
import com.oracle.svm.configure.ConfigurationTypeDescriptor;
import com.oracle.svm.configure.NamedConfigurationTypeDescriptor;
import com.oracle.svm.configure.ProxyConfigurationTypeDescriptor;
Expand Down Expand Up @@ -101,7 +102,8 @@ public void processEntry(EconomicMap<String, Object> entry, ConfigurationSet con
name = MetaUtil.internalNameToJava(MetaUtil.toInternalName(name), true, true);
}
if (!advisor.shouldIgnore(lazyValue(name), lazyValue(callerClass), entry) &&
!(isLoadClass && advisor.shouldIgnoreLoadClass(lazyValue(name), lazyValue(callerClass), entry))) {
!(isLoadClass && advisor.shouldIgnoreLoadClass(lazyValue(name), lazyValue(callerClass), entry)) &&
ClassNameSupport.isValidReflectionName(name)) {
configuration.getOrCreateType(condition, NamedConfigurationTypeDescriptor.fromReflectionName(name));
}
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,18 +246,7 @@ private Object forName0(String className, ClassLoader classLoader) {
result = PredefinedClassesSupport.getLoadedForNameOrNull(className, classLoader);
}
if (result == null && !ClassNameSupport.isValidReflectionName(className)) {
if (result == null && ClassNameSupport.isValidJNIName(className)) {
var jniAlias = knownClasses.get(ClassNameSupport.jniNameToReflectionName(className));
if (jniAlias != null && jniAlias.getValue() != null) {
result = NEGATIVE_QUERY;
}
}
if (result == null && ClassNameSupport.isValidTypeName(className)) {
var typeAlias = knownClasses.get(ClassNameSupport.typeNameToReflectionName(className));
if (typeAlias != null && typeAlias.getValue() != null) {
result = NEGATIVE_QUERY;
}
}
result = NEGATIVE_QUERY;
}
return result == NEGATIVE_QUERY ? new ClassNotFoundException(className) : result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,9 @@ public static Class<?> getClassObjectByName(CharSequence name) {
for (var dictionary : layeredSingletons()) {
JNIAccessibleClass clazz = dictionary.classesByName.get(name);
if (clazz == null && !ClassNameSupport.isValidJNIName(name.toString())) {
if (clazz == null && ClassNameSupport.isValidReflectionName(name.toString()) && dictionary.classesByName.containsKey(ClassNameSupport.reflectionNameToJNIName(name.toString()))) {
clazz = NEGATIVE_CLASS_LOOKUP;
}
if (clazz == null && ClassNameSupport.isValidTypeName(name.toString()) && dictionary.classesByName.containsKey(ClassNameSupport.typeNameToJNIName(name.toString()))) {
clazz = NEGATIVE_CLASS_LOOKUP;
}
clazz = NEGATIVE_CLASS_LOOKUP;
}
clazz = checkClass(clazz, name);
clazz = checkClass(clazz, name.toString());
if (clazz != null) {
return clazz.getClassObject();
}
Expand All @@ -196,9 +191,9 @@ public static Class<?> getClassObjectByName(CharSequence name) {
return null;
}

private static JNIAccessibleClass checkClass(JNIAccessibleClass clazz, CharSequence name) {
private static JNIAccessibleClass checkClass(JNIAccessibleClass clazz, String name) {
if (throwMissingRegistrationErrors() && clazz == null) {
MissingJNIRegistrationUtils.forClass(name.toString());
MissingJNIRegistrationUtils.forClass(name);
} else if (clazz != null && clazz.isNegative()) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,11 @@ private void registerFields(boolean finalIsWritable, Field[] fields) {
}

@Override
public void registerClassLookup(ConfigurationCondition condition, String jniName) {
public void registerClassLookup(ConfigurationCondition condition, String reflectionName) {
try {
register(condition, false, Class.forName(ClassNameSupport.jniNameToReflectionName(jniName)));
register(condition, false, Class.forName(reflectionName));
} catch (ClassNotFoundException e) {
String jniName = ClassNameSupport.reflectionNameToJNIName(reflectionName);
newNegativeClassLookups.add(jniName);
}
}
Expand Down
Loading