2424 */
2525package com .oracle .svm .hosted .image ;
2626
27+ import static com .oracle .graal .pointsto .api .PointstoOptions .UseConservativeUnsafeAccess ;
28+ import static com .oracle .svm .core .SubstrateOptions .Preserve ;
29+
30+ import java .lang .reflect .Constructor ;
31+ import java .lang .reflect .Field ;
32+ import java .lang .reflect .Method ;
33+ import java .lang .reflect .Modifier ;
2734import java .util .Arrays ;
35+ import java .util .Collections ;
36+ import java .util .Comparator ;
37+ import java .util .HashSet ;
2838import java .util .Set ;
2939import java .util .stream .Stream ;
3040
3141import org .graalvm .collections .EconomicMap ;
42+ import org .graalvm .nativeimage .ImageSingletons ;
43+ import org .graalvm .nativeimage .impl .ConfigurationCondition ;
44+ import org .graalvm .nativeimage .impl .RuntimeJNIAccessSupport ;
45+ import org .graalvm .nativeimage .impl .RuntimeProxyCreationSupport ;
46+ import org .graalvm .nativeimage .impl .RuntimeReflectionSupport ;
3247
48+ import com .oracle .graal .pointsto .ClassInclusionPolicy ;
3349import com .oracle .svm .core .SubstrateOptions ;
3450import com .oracle .svm .core .option .AccumulatingLocatableMultiOptionValue ;
3551import com .oracle .svm .core .option .LocatableMultiOptionValue ;
3652import com .oracle .svm .core .option .SubstrateOptionsParser ;
3753import com .oracle .svm .core .util .UserError ;
3854import com .oracle .svm .hosted .NativeImageClassLoaderSupport ;
3955import com .oracle .svm .hosted .driver .IncludeOptionsSupport ;
56+ import com .oracle .svm .util .ReflectionUtil ;
4057
4158import jdk .graal .compiler .options .OptionKey ;
4259import jdk .graal .compiler .options .OptionValues ;
@@ -93,7 +110,8 @@ private static String preservePossibleOptions() {
93110 }
94111
95112 public static void parsePreserveOption (EconomicMap <OptionKey <?>, Object > hostedValues , NativeImageClassLoaderSupport classLoaderSupport ) {
96- AccumulatingLocatableMultiOptionValue .Strings preserve = SubstrateOptions .Preserve .getValue (new OptionValues (hostedValues ));
113+ OptionValues optionValues = new OptionValues (hostedValues );
114+ AccumulatingLocatableMultiOptionValue .Strings preserve = SubstrateOptions .Preserve .getValue (optionValues );
97115 Stream <LocatableMultiOptionValue .ValueWithOrigin <String >> valuesWithOrigins = preserve .getValuesWithOrigins ();
98116 valuesWithOrigins .forEach (valueWithOrigin -> {
99117 String optionArgument = SubstrateOptionsParser .commandArgument (SubstrateOptions .Preserve , valueWithOrigin .value (), true , false );
@@ -112,5 +130,95 @@ public static void parsePreserveOption(EconomicMap<OptionKey<?>, Object> hostedV
112130 }
113131 }
114132 });
133+ if (classLoaderSupport .isPreserveMode ()) {
134+ if (UseConservativeUnsafeAccess .hasBeenSet (optionValues )) {
135+ UserError .guarantee (UseConservativeUnsafeAccess .getValue (optionValues ), "%s can not be used together with %s. Please unset %s." ,
136+ SubstrateOptionsParser .commandArgument (UseConservativeUnsafeAccess , "-" ),
137+ SubstrateOptionsParser .commandArgument (Preserve , "<value>" ),
138+ SubstrateOptionsParser .commandArgument (UseConservativeUnsafeAccess , "-" ));
139+ }
140+ UseConservativeUnsafeAccess .update (hostedValues , true );
141+ }
142+ }
143+
144+ public static void registerPreservedClasses (NativeImageClassLoaderSupport classLoaderSupport ) {
145+ var classesOrPackagesToIgnore = ignoredClassesOrPackagesForPreserve ();
146+ var classesToPreserve = classLoaderSupport .getClassesToPreserve ()
147+ .filter (ClassInclusionPolicy ::isClassIncludedBase )
148+ .filter (c -> !(classesOrPackagesToIgnore .contains (c .getPackageName ()) || classesOrPackagesToIgnore .contains (c .getName ())))
149+ .sorted (Comparator .comparing (ReflectionUtil ::getClassHierarchyDepth ).reversed ())
150+ .toList ();
151+
152+ final RuntimeReflectionSupport reflection = ImageSingletons .lookup (RuntimeReflectionSupport .class );
153+ final RuntimeJNIAccessSupport jni = ImageSingletons .lookup (RuntimeJNIAccessSupport .class );
154+ final RuntimeProxyCreationSupport proxy = ImageSingletons .lookup (RuntimeProxyCreationSupport .class );
155+ final ConfigurationCondition always = ConfigurationCondition .alwaysTrue ();
156+
157+ /*
158+ * Sort descending by class hierarchy depth to avoid complexity related to field
159+ * registration.
160+ */
161+ classesToPreserve .forEach (c -> {
162+ reflection .register (always , false , c );
163+
164+ reflection .registerAllDeclaredFields (always , c );
165+ reflection .registerAllDeclaredMethodsQuery (always , false , c );
166+ reflection .registerAllDeclaredConstructorsQuery (always , false , c );
167+ reflection .registerAllConstructorsQuery (always , false , c );
168+ reflection .registerAllClassesQuery (always , c );
169+ reflection .registerAllDeclaredClassesQuery (always , c );
170+ reflection .registerAllNestMembersQuery (always , c );
171+ reflection .registerAllPermittedSubclassesQuery (always , c );
172+ reflection .registerAllRecordComponentsQuery (always , c );
173+ reflection .registerAllSignersQuery (always , c );
174+
175+ /* Register every single-interface proxy */
176+ // GR-62293 can't register proxies from jdk modules.
177+ if (c .getModule () == null && c .isInterface ()) {
178+ proxy .addProxyClass (always , c );
179+ }
180+
181+ jni .register (ConfigurationCondition .alwaysTrue (), c );
182+ try {
183+ for (Method declaredMethod : c .getDeclaredMethods ()) {
184+ jni .register (always , false , declaredMethod );
185+ }
186+ for (Constructor <?> declaredConstructor : c .getDeclaredConstructors ()) {
187+ jni .register (always , false , declaredConstructor );
188+ }
189+ for (Field declaredField : c .getDeclaredFields ()) {
190+ jni .register (always , false , declaredField );
191+ reflection .register (always , false , declaredField );
192+ }
193+ } catch (LinkageError e ) {
194+ /* If we can't link we can not register for JNI and reflection */
195+ }
196+
197+ // if we register as unsafe allocated earlier there are build-time
198+ // initialization errors
199+ reflection .register (always , !(c .isArray () || c .isInterface () || c .isPrimitive () || Modifier .isAbstract (c .getModifiers ())), c );
200+ });
201+
202+ /*
203+ * We now register super-type methods and fields in a separate pass--when all subtypes have
204+ * been fully registered. We do it the opposite order to avoid crawling the hierarchy
205+ * upwards multiple times when caching is implemented.
206+ */
207+ classesToPreserve .reversed ().forEach (c -> {
208+ reflection .registerAllFields (always , c );
209+ reflection .registerAllMethodsQuery (always , false , c );
210+ // RuntimeSerialization.register(c);
211+ });
212+
213+ for (String className : classLoaderSupport .getClassNamesToPreserve ()) {
214+ reflection .registerClassLookup (always , className );
215+ }
216+ }
217+
218+ private static Set <String > ignoredClassesOrPackagesForPreserve () {
219+ Set <String > ignoredClassesOrPackages = new HashSet <>(SubstrateOptions .IgnorePreserveForClasses .getValue ().valuesAsSet ());
220+ // GR-63360: Parsing of constant_ lambda forms fails
221+ ignoredClassesOrPackages .add ("java.lang.invoke.LambdaForm$Holder" );
222+ return Collections .unmodifiableSet (ignoredClassesOrPackages );
115223 }
116224}
0 commit comments