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 .hosted .RuntimeSerialization ;
44+ import org .graalvm .nativeimage .impl .ConfigurationCondition ;
45+ import org .graalvm .nativeimage .impl .RuntimeJNIAccessSupport ;
46+ import org .graalvm .nativeimage .impl .RuntimeProxyCreationSupport ;
47+ import org .graalvm .nativeimage .impl .RuntimeReflectionSupport ;
3248
49+ import com .oracle .graal .pointsto .ClassInclusionPolicy ;
3350import com .oracle .svm .core .SubstrateOptions ;
3451import com .oracle .svm .core .option .AccumulatingLocatableMultiOptionValue ;
3552import com .oracle .svm .core .option .LocatableMultiOptionValue ;
3653import com .oracle .svm .core .option .SubstrateOptionsParser ;
3754import com .oracle .svm .core .util .UserError ;
3855import com .oracle .svm .hosted .NativeImageClassLoaderSupport ;
3956import com .oracle .svm .hosted .driver .IncludeOptionsSupport ;
57+ import com .oracle .svm .hosted .reflect .ReflectionDataBuilder ;
58+ import com .oracle .svm .util .ReflectionUtil ;
4059
4160import jdk .graal .compiler .options .OptionKey ;
4261import jdk .graal .compiler .options .OptionValues ;
@@ -93,7 +112,8 @@ private static String preservePossibleOptions() {
93112 }
94113
95114 public static void parsePreserveOption (EconomicMap <OptionKey <?>, Object > hostedValues , NativeImageClassLoaderSupport classLoaderSupport ) {
96- AccumulatingLocatableMultiOptionValue .Strings preserve = SubstrateOptions .Preserve .getValue (new OptionValues (hostedValues ));
115+ OptionValues optionValues = new OptionValues (hostedValues );
116+ AccumulatingLocatableMultiOptionValue .Strings preserve = SubstrateOptions .Preserve .getValue (optionValues );
97117 Stream <LocatableMultiOptionValue .ValueWithOrigin <String >> valuesWithOrigins = preserve .getValuesWithOrigins ();
98118 valuesWithOrigins .forEach (valueWithOrigin -> {
99119 String optionArgument = SubstrateOptionsParser .commandArgument (SubstrateOptions .Preserve , valueWithOrigin .value (), true , false );
@@ -112,5 +132,95 @@ public static void parsePreserveOption(EconomicMap<OptionKey<?>, Object> hostedV
112132 }
113133 }
114134 });
135+ if (classLoaderSupport .isPreserveMode ()) {
136+ if (UseConservativeUnsafeAccess .hasBeenSet (optionValues )) {
137+ UserError .guarantee (UseConservativeUnsafeAccess .getValue (optionValues ), "%s can not be used together with %s. Please unset %s." ,
138+ SubstrateOptionsParser .commandArgument (UseConservativeUnsafeAccess , "-" ),
139+ SubstrateOptionsParser .commandArgument (Preserve , "<value>" ),
140+ SubstrateOptionsParser .commandArgument (UseConservativeUnsafeAccess , "-" ));
141+ }
142+ UseConservativeUnsafeAccess .update (hostedValues , true );
143+ }
144+ }
145+
146+ public static void registerPreservedClasses (NativeImageClassLoaderSupport classLoaderSupport ) {
147+ var classesOrPackagesToIgnore = ignoredClassesOrPackagesForPreserve ();
148+ var classesToPreserve = classLoaderSupport .getClassesToPreserve ()
149+ .filter (ClassInclusionPolicy ::isClassIncludedBase )
150+ .filter (c -> !(classesOrPackagesToIgnore .contains (c .getPackageName ()) || classesOrPackagesToIgnore .contains (c .getName ())))
151+ .sorted (Comparator .comparing (ReflectionUtil ::getClassHierarchyDepth ).reversed ())
152+ .toList ();
153+
154+ final RuntimeReflectionSupport reflection = ImageSingletons .lookup (RuntimeReflectionSupport .class );
155+ final RuntimeJNIAccessSupport jni = ImageSingletons .lookup (RuntimeJNIAccessSupport .class );
156+ final RuntimeProxyCreationSupport proxy = ImageSingletons .lookup (RuntimeProxyCreationSupport .class );
157+ final ConfigurationCondition always = ConfigurationCondition .alwaysTrue ();
158+
159+ /*
160+ * Sort descending by class hierarchy depth to avoid complexity related to field
161+ * registration.
162+ */
163+ classesToPreserve .forEach (c -> {
164+ reflection .register (always , false , c );
165+
166+ reflection .registerAllDeclaredFields (always , c );
167+ reflection .registerAllDeclaredMethodsQuery (always , false , c );
168+ reflection .registerAllDeclaredConstructorsQuery (always , false , c );
169+ reflection .registerAllConstructorsQuery (always , false , c );
170+ reflection .registerAllClassesQuery (always , c );
171+ reflection .registerAllDeclaredClassesQuery (always , c );
172+ reflection .registerAllNestMembersQuery (always , c );
173+ reflection .registerAllPermittedSubclassesQuery (always , c );
174+ reflection .registerAllRecordComponentsQuery (always , c );
175+ reflection .registerAllSignersQuery (always , c );
176+
177+ /* Register every single-interface proxy */
178+ // GR-62293 can't register proxies from jdk modules.
179+ if (c .getModule () == null && c .isInterface ()) {
180+ proxy .addProxyClass (always , c );
181+ }
182+
183+ jni .register (ConfigurationCondition .alwaysTrue (), c );
184+ try {
185+ for (Method declaredMethod : c .getDeclaredMethods ()) {
186+ jni .register (always , false , declaredMethod );
187+ }
188+ for (Constructor <?> declaredConstructor : c .getDeclaredConstructors ()) {
189+ jni .register (always , false , declaredConstructor );
190+ }
191+ for (Field declaredField : c .getDeclaredFields ()) {
192+ jni .register (always , false , declaredField );
193+ reflection .register (always , false , declaredField );
194+ }
195+ } catch (LinkageError e ) {
196+ /* If we can't link we can not register for JNI and reflection */
197+ }
198+
199+ // if we register as unsafe allocated earlier there are build-time
200+ // initialization errors
201+ reflection .register (always , !(c .isArray () || c .isInterface () || c .isPrimitive () || Modifier .isAbstract (c .getModifiers ())), c );
202+ });
203+
204+ /*
205+ * We now register super-type methods and fields in a separate pass--when all subtypes have
206+ * been fully registered. We do it the opposite order to avoid crawling the hierarchy
207+ * upwards multiple times when caching is implemented.
208+ */
209+ classesToPreserve .reversed ().forEach (c -> {
210+ reflection .registerAllFields (always , c );
211+ reflection .registerAllMethodsQuery (always , false , c );
212+ // RuntimeSerialization.register(c);
213+ });
214+
215+ for (String className : classLoaderSupport .getClassNamesToPreserve ()) {
216+ reflection .registerClassLookup (always , className );
217+ }
218+ }
219+
220+ private static Set <String > ignoredClassesOrPackagesForPreserve () {
221+ Set <String > ignoredClassesOrPackages = new HashSet <>(SubstrateOptions .IgnorePreserveForClasses .getValue ().valuesAsSet ());
222+ // GR-63360: Parsing of constant_ lambda forms fails
223+ ignoredClassesOrPackages .add ("java.lang.invoke.LambdaForm$Holder" );
224+ return Collections .unmodifiableSet (ignoredClassesOrPackages );
115225 }
116226}
0 commit comments