diff --git a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest index 5c15c4d96208..24cb6cb5bc57 100644 --- a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest +++ b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest @@ -39,6 +39,13 @@ cons public init(java.lang.Throwable) supr java.lang.Throwable hfds serialVersionUID +CLSS public java.lang.LinkageError +cons public init() +cons public init(java.lang.String) +cons public init(java.lang.String,java.lang.Throwable) +supr java.lang.Error +hfds serialVersionUID + CLSS public java.lang.Exception cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean) cons public init() @@ -230,7 +237,7 @@ meth public java.lang.Class getDeclaringClass() meth public java.lang.Class getElementType() meth public java.lang.String getElementName() meth public java.lang.String getSignature() -supr java.lang.Error +supr java.lang.LinkageError hfds declaringClass,elementName,elementType,serialVersionUID,signature CLSS public final org.graalvm.nativeimage.MissingReflectionRegistrationError @@ -239,7 +246,7 @@ meth public java.lang.Class getDeclaringClass() meth public java.lang.Class getElementType() meth public java.lang.Class[] getParameterTypes() meth public java.lang.String getElementName() -supr java.lang.Error +supr java.lang.LinkageError hfds declaringClass,elementName,elementType,parameterTypes,serialVersionUID CLSS public abstract interface org.graalvm.nativeimage.ObjectHandle diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingJNIRegistrationError.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingJNIRegistrationError.java index 0504f27ddc75..cfb15b5d082c 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingJNIRegistrationError.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingJNIRegistrationError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -74,7 +74,7 @@ * * @since 24.1 */ -public final class MissingJNIRegistrationError extends Error { +public final class MissingJNIRegistrationError extends LinkageError { @Serial private static final long serialVersionUID = -8940056537864516986L; private final Class elementType; diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingReflectionRegistrationError.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingReflectionRegistrationError.java index 9eb745bf8af9..a2a4ee58ed67 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingReflectionRegistrationError.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingReflectionRegistrationError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -91,7 +91,7 @@ * * @since 23.0 */ -public final class MissingReflectionRegistrationError extends Error { +public final class MissingReflectionRegistrationError extends LinkageError { @Serial private static final long serialVersionUID = 2764341882856270640L; private final Class elementType; diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index a25d8d923e54..ee75e9680822 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -24,6 +24,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-59869) Implemented initial optimization of Java Vector API (JEP 338) operations in native images. See the compiler changelog for more details. * (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 * (GR-47881) Remove the total number of loaded types, fields, and methods from the build output, deprecated these metrics in the build output schema, and removed already deprecated build output metrics. +* (GR-64619) Missing registration errors are now subclasses of `LinkageError` ## 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. diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/MissingRegistrationUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/MissingRegistrationUtils.java index 510cca5f604d..a21482449e04 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/MissingRegistrationUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/MissingRegistrationUtils.java @@ -28,8 +28,10 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; import com.oracle.svm.core.util.ExitStatus; +import com.oracle.svm.core.util.VMError; public final class MissingRegistrationUtils { @@ -46,7 +48,7 @@ public static SubstrateOptions.ReportingMode missingRegistrationReportingMode() private static final AtomicReference> seenOutputs = new AtomicReference<>(null); public static void report(Error exception, StackTraceElement responsibleClass) { - if (responsibleClass != null && !MissingRegistrationSupport.singleton().reportMissingRegistrationErrors(responsibleClass)) { + if (missingRegistrationErrorsSuspended.get() || (responsibleClass != null && !MissingRegistrationSupport.singleton().reportMissingRegistrationErrors(responsibleClass))) { return; } switch (missingRegistrationReportingMode()) { @@ -104,6 +106,25 @@ public static void report(Error exception, StackTraceElement responsibleClass) { } } + private static final ThreadLocal missingRegistrationErrorsSuspended = ThreadLocal.withInitial(() -> false); + + /** + * Code executing inside this function will temporarily revert to throwing JDK exceptions like + * ({@code ClassNotFoundException} when encountering a situation that would normally cause a + * missing registration error. This is currently required during resource bundle lookups, where + * encountering an unregistered class can mean that the corresponding locale isn't included in + * the image, and is not a reason to abort the lookup completely. + */ + public static T runIgnoringMissingRegistrations(Supplier callback) { + VMError.guarantee(!missingRegistrationErrorsSuspended.get()); + try { + missingRegistrationErrorsSuspended.set(true); + return callback.get(); + } finally { + missingRegistrationErrorsSuspended.set(false); + } + } + private static void printLine(StringBuilder sb, Object object) { sb.append(" ").append(object).append(System.lineSeparator()); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/invoke/Target_java_lang_invoke_MemberName.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/invoke/Target_java_lang_invoke_MemberName.java index 752aee45839f..ad1288c1185d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/invoke/Target_java_lang_invoke_MemberName.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/invoke/Target_java_lang_invoke_MemberName.java @@ -27,11 +27,16 @@ import java.lang.invoke.MethodType; import java.lang.reflect.Member; +import org.graalvm.nativeimage.MissingReflectionRegistrationError; + import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Inject; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.methodhandles.Target_java_lang_invoke_MethodHandleNatives; +import com.oracle.svm.core.util.BasedOnJDKFile; +import com.oracle.svm.core.util.VMError; @TargetClass(className = "java.lang.invoke.MemberName") public final class Target_java_lang_invoke_MemberName { @@ -81,7 +86,57 @@ public final class Target_java_lang_invoke_MemberName { @SuppressWarnings("static-method") @Substitute - private boolean vminfoIsConsistent() { + boolean vminfoIsConsistent() { return true; /* The substitution class doesn't use the same internals as the JDK */ } + + @Alias + @Override + protected native Target_java_lang_invoke_MemberName clone(); + + @Alias + native void ensureTypeVisible(Class refc); + + @Alias + public native boolean isResolved(); + + @Alias + native boolean referenceKindIsConsistent(); + + @Alias + native void initResolved(boolean isResolved); +} + +@TargetClass(className = "java.lang.invoke.MemberName", innerClass = "Factory") +final class Target_java_lang_invoke_MemberName_Factory { + @Substitute + @SuppressWarnings("static-method") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/b685ea54081fcf54a6567dddb49b63435a6e1ea4/src/java.base/share/classes/java/lang/invoke/MemberName.java#L937-L973") + private Target_java_lang_invoke_MemberName resolve(byte refKind, Target_java_lang_invoke_MemberName ref, Class lookupClass, int allowedModes, + boolean speculativeResolve) { + Target_java_lang_invoke_MemberName m = ref.clone(); + assert (refKind == m.getReferenceKind()); + try { + m = Target_java_lang_invoke_MethodHandleNatives.resolve(m, lookupClass, allowedModes, speculativeResolve); + if (m == null) { + VMError.guarantee(speculativeResolve, "non-speculative resolution should return member name or throw"); + return null; + } + m.ensureTypeVisible(m.getDeclaringClass()); + m.resolution = null; + } catch (ClassNotFoundException | LinkageError ex) { + if (ex instanceof MissingReflectionRegistrationError e) { + /* Bypass the LinkageError catch below */ + throw e; + } + VMError.guarantee(m != null, "speculative resolution should not throw"); + assert (!m.isResolved()); + m.resolution = ex; + return m; + } + assert (m.referenceKindIsConsistent()); + m.initResolved(true); + assert (m.vminfoIsConsistent()); + return m; + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/substitutions/Target_java_util_ResourceBundle.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/substitutions/Target_java_util_ResourceBundle.java index 9bd8f8870a1f..401790ecab1d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/substitutions/Target_java_util_ResourceBundle.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/substitutions/Target_java_util_ResourceBundle.java @@ -28,9 +28,11 @@ import java.util.ResourceBundle; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.function.Supplier; import org.graalvm.nativeimage.ImageSingletons; +import com.oracle.svm.core.MissingRegistrationUtils; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; @@ -115,7 +117,12 @@ private static ResourceBundle getBundleImpl(String baseName, if (!ImageSingletons.lookup(LocalizationSupport.class).isRegisteredBundleLookup(baseName, locale, control)) { MissingResourceRegistrationUtils.missingResourceBundle(baseName); } - return getBundleImpl(callerModule, callerModule, baseName, locale, control); + return MissingRegistrationUtils.runIgnoringMissingRegistrations(new Supplier() { + @Override + public ResourceBundle get() { + return getBundleImpl(callerModule, callerModule, baseName, locale, control); + } + }); } // find resource bundles from unnamed module of given class loader @@ -129,7 +136,12 @@ private static ResourceBundle getBundleImpl(String baseName, if (!ImageSingletons.lookup(LocalizationSupport.class).isRegisteredBundleLookup(baseName, locale, control)) { MissingResourceRegistrationUtils.missingResourceBundle(baseName); } - return getBundleImpl(callerModule, unnamedModule, baseName, locale, control); + return MissingRegistrationUtils.runIgnoringMissingRegistrations(new Supplier() { + @Override + public ResourceBundle get() { + return getBundleImpl(callerModule, unnamedModule, baseName, locale, control); + } + }); } @Alias diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java index 773f3acd1218..5142f84a9951 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java @@ -65,7 +65,7 @@ */ @SuppressWarnings("unused") @TargetClass(className = "java.lang.invoke.MethodHandleNatives") -final class Target_java_lang_invoke_MethodHandleNatives { +public final class Target_java_lang_invoke_MethodHandleNatives { /* * MemberName native constructor. We need to resolve the actual type and flags of the member and @@ -209,7 +209,7 @@ private static Object getMemberVMInfo(Target_java_lang_invoke_MemberName self) { static native String refKindName(byte refKind); @Substitute - static Target_java_lang_invoke_MemberName resolve(Target_java_lang_invoke_MemberName self, Class caller, int lookupMode, boolean speculativeResolve) + public static Target_java_lang_invoke_MemberName resolve(Target_java_lang_invoke_MemberName self, Class caller, int lookupMode, boolean speculativeResolve) throws LinkageError, ClassNotFoundException { Class declaringClass = self.getDeclaringClass(); Target_java_lang_invoke_MemberName resolved = Util_java_lang_invoke_MethodHandleNatives.resolve(self, caller, speculativeResolve); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/MissingSerializationRegistrationError.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/MissingSerializationRegistrationError.java index 0fa28f1231f5..d636779d3656 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/MissingSerializationRegistrationError.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/MissingSerializationRegistrationError.java @@ -34,7 +34,7 @@ * The purpose of this exception is to easily discover unregistered elements and to assure that all * serialization or deserialization operations have expected behavior. */ -public final class MissingSerializationRegistrationError extends Error { +public final class MissingSerializationRegistrationError extends LinkageError { @Serial private static final long serialVersionUID = 2764341882856270641L; private final Class culprit; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java index dd3aac70ba64..b2d9c4c2b546 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java @@ -396,7 +396,7 @@ private void handleAliasClass(Class annotatedClass, Class originalClass, T } private void handleMethodInAliasClass(Executable annotatedMethod, Class originalClass) { - if (skipExcludedPlatform(annotatedMethod)) { + if (skipExcludedPlatform(annotatedMethod) || annotatedMethod.isSynthetic()) { return; } @@ -482,7 +482,7 @@ private boolean skipExcludedPlatform(AnnotatedElement annotatedMethod) { } private void handleFieldInAliasClass(Field annotatedField, Class originalClass) { - if (skipExcludedPlatform(annotatedField)) { + if (skipExcludedPlatform(annotatedField) || annotatedField.isSynthetic()) { return; }