5050import java .util .function .Supplier ;
5151import java .util .regex .Pattern ;
5252
53+ import com .oracle .svm .agent .conditionalconfig .ConditionalConfigurationPartialRunWriter ;
54+ import com .oracle .svm .agent .configwithorigins .ConfigurationWithOriginsTracer ;
5355import org .graalvm .nativeimage .Platform ;
5456import org .graalvm .nativeimage .ProcessProperties ;
5557import org .graalvm .nativeimage .hosted .Feature ;
6567import com .oracle .svm .agent .tracing .TraceFileWriter ;
6668import com .oracle .svm .agent .tracing .core .Tracer ;
6769import com .oracle .svm .agent .tracing .core .TracingResultWriter ;
68- import com .oracle .svm .configure .config .ConditionalConfigurationPredicate ;
70+ import com .oracle .svm .configure .config .conditional . ConditionalConfigurationPredicate ;
6971import com .oracle .svm .configure .config .ConfigurationFileCollection ;
7072import com .oracle .svm .configure .config .ConfigurationSet ;
7173import com .oracle .svm .configure .filters .ComplexFilter ;
@@ -105,6 +107,18 @@ private static String getTokenValue(String token) {
105107 return token .substring (token .indexOf ('=' ) + 1 );
106108 }
107109
110+ private static boolean getBooleanTokenValue (String token ) {
111+ int equalsIndex = token .indexOf ('=' );
112+ if (equalsIndex == -1 ) {
113+ return true ;
114+ }
115+ return Boolean .parseBoolean (token .substring (equalsIndex + 1 ));
116+ }
117+
118+ private static boolean isBooleanOption (String token , String option ) {
119+ return token .equals (option ) || token .startsWith (option + "=" );
120+ }
121+
108122 @ Override
109123 protected int getRequiredJvmtiVersion () {
110124 return JvmtiInterface .JVMTI_VERSION_1_2 ;
@@ -133,6 +147,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
133147 boolean configurationWithOrigins = false ;
134148 List <String > conditionalConfigUserPackageFilterFiles = new ArrayList <>();
135149 List <String > conditionalConfigClassNameFilterFiles = new ArrayList <>();
150+ boolean conditionalConfigPartialRun = false ;
136151 int configWritePeriod = -1 ; // in seconds
137152 int configWritePeriodInitialDelay = 1 ; // in seconds
138153 boolean trackReflectionMetadata = true ;
@@ -156,42 +171,31 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
156171 String omittedConfigDir = getTokenValue (token );
157172 omittedConfigDir = transformPath (omittedConfigDir );
158173 omittedConfigs .addDirectory (Paths .get (omittedConfigDir ));
159- } else if (token .equals ("experimental-omit-config-from-classpath" )) {
160- experimentalOmitClasspathConfig = true ;
161- } else if (token .startsWith ("experimental-omit-config-from-classpath=" )) {
162- experimentalOmitClasspathConfig = Boolean .parseBoolean (getTokenValue (token ));
174+ } else if (isBooleanOption (token , "experimental-omit-config-from-classpath" )) {
175+ experimentalOmitClasspathConfig = getBooleanTokenValue (token );
163176 } else if (token .startsWith ("restrict-all-dir" ) || token .equals ("restrict" ) || token .startsWith ("restrict=" )) {
164177 warn ("restrict mode is no longer supported, ignoring option: " + token );
165178 } else if (token .equals ("no-builtin-caller-filter" )) {
166179 builtinCallerFilter = false ;
167- } else if (token . startsWith ( "builtin-caller-filter= " )) {
168- builtinCallerFilter = Boolean . parseBoolean ( getTokenValue ( token ) );
180+ } else if (isBooleanOption ( token , "builtin-caller-filter" )) {
181+ builtinCallerFilter = getBooleanTokenValue ( token );
169182 } else if (token .equals ("no-builtin-heuristic-filter" )) {
170183 builtinHeuristicFilter = false ;
171- } else if (token .startsWith ("builtin-heuristic-filter=" )) {
172- builtinHeuristicFilter = Boolean .parseBoolean (getTokenValue (token ));
173- } else if (token .equals ("no-filter" )) { // legacy
174- builtinCallerFilter = false ;
175- builtinHeuristicFilter = false ;
176- } else if (token .startsWith ("no-filter=" )) { // legacy
177- builtinCallerFilter = !Boolean .parseBoolean (getTokenValue (token ));
184+ } else if (isBooleanOption (token , "builtin-heuristic-filter" )) {
185+ builtinHeuristicFilter = getBooleanTokenValue (token );
186+ } else if (isBooleanOption (token , "no-filter" )) { // legacy
187+ builtinCallerFilter = !getBooleanTokenValue (token );
178188 builtinHeuristicFilter = builtinCallerFilter ;
179189 } else if (token .startsWith ("caller-filter-file=" )) {
180190 callerFilterFiles .add (getTokenValue (token ));
181191 } else if (token .startsWith ("access-filter-file=" )) {
182192 accessFilterFiles .add (getTokenValue (token ));
183- } else if (token .equals ("experimental-class-loader-support" )) {
184- experimentalClassLoaderSupport = true ;
185- } else if (token .startsWith ("experimental-class-loader-support=" )) {
186- experimentalClassLoaderSupport = Boolean .parseBoolean (getTokenValue (token ));
187- } else if (token .equals ("experimental-class-define-support" )) {
188- experimentalClassDefineSupport = true ;
189- } else if (token .startsWith ("experimental-class-define-support=" )) {
190- experimentalClassDefineSupport = Boolean .parseBoolean (getTokenValue (token ));
191- } else if (token .equals ("experimental-unsafe-allocation-support" )) {
192- experimentalUnsafeAllocationSupport = Boolean .parseBoolean (getTokenValue (token ));
193- } else if (token .startsWith ("experimental-unsafe-allocation-support=" )) {
194- experimentalUnsafeAllocationSupport = Boolean .parseBoolean (getTokenValue (token ));
193+ } else if (isBooleanOption (token , "experimental-class-loader-support" )) {
194+ experimentalClassLoaderSupport = getBooleanTokenValue (token );
195+ } else if (isBooleanOption (token , "experimental-class-define-support" )) {
196+ experimentalClassDefineSupport = getBooleanTokenValue (token );
197+ } else if (isBooleanOption (token , "experimental-unsafe-allocation-support" )) {
198+ experimentalUnsafeAllocationSupport = getBooleanTokenValue (token );
195199 } else if (token .startsWith ("config-write-period-secs=" )) {
196200 configWritePeriod = parseIntegerOrNegative (getTokenValue (token ));
197201 if (configWritePeriod <= 0 ) {
@@ -202,20 +206,18 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
202206 if (configWritePeriodInitialDelay < 0 ) {
203207 return usage (1 , "config-write-initial-delay-secs must be an integer greater or equal to 0" );
204208 }
205- } else if (token .equals ("build" )) {
206- build = true ;
207- } else if (token .startsWith ("build=" )) {
208- build = Boolean .parseBoolean (getTokenValue (token ));
209- } else if (token .equals ("experimental-configuration-with-origins" )) {
210- configurationWithOrigins = true ;
209+ } else if (isBooleanOption (token , "build" )) {
210+ build = getBooleanTokenValue (token );
211+ } else if (isBooleanOption (token , "experimental-configuration-with-origins" )) {
212+ configurationWithOrigins = getBooleanTokenValue (token );
211213 } else if (token .startsWith ("experimental-conditional-config-filter-file=" )) {
212214 conditionalConfigUserPackageFilterFiles .add (getTokenValue (token ));
213215 } else if (token .startsWith ("conditional-config-class-filter-file=" )) {
214216 conditionalConfigClassNameFilterFiles .add (getTokenValue (token ));
215- } else if (token . equals ( "track-reflection-metadata " )) {
216- trackReflectionMetadata = true ;
217- } else if (token . startsWith ( "track-reflection-metadata= " )) {
218- trackReflectionMetadata = Boolean . parseBoolean ( getTokenValue ( token ) );
217+ } else if (isBooleanOption ( token , "experimental-conditional-config-part " )) {
218+ conditionalConfigPartialRun = getBooleanTokenValue ( token ) ;
219+ } else if (isBooleanOption ( token , "track-reflection-metadata" )) {
220+ trackReflectionMetadata = getBooleanTokenValue ( token );
219221 } else {
220222 return usage (1 , "unknown option: '" + token + "'." );
221223 }
@@ -264,7 +266,12 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
264266 }
265267 }
266268
267- boolean shouldTraceOriginInformation = configurationWithOrigins || !conditionalConfigUserPackageFilterFiles .isEmpty ();
269+ if (!conditionalConfigUserPackageFilterFiles .isEmpty () && conditionalConfigPartialRun ) {
270+ return error (6 , "The agent can generate conditional configuration either for the current run or in the partial mode but not both at the same time." );
271+ }
272+
273+ boolean isConditionalConfigurationRun = !conditionalConfigUserPackageFilterFiles .isEmpty () || conditionalConfigPartialRun ;
274+ boolean shouldTraceOriginInformation = configurationWithOrigins || isConditionalConfigurationRun ;
268275 final MethodInfoRecordKeeper recordKeeper = new MethodInfoRecordKeeper (shouldTraceOriginInformation );
269276 final Supplier <InterceptedState > interceptedStateSupplier = shouldTraceOriginInformation ? EagerlyLoadedJavaStackAccess .stackAccessSupplier ()
270277 : OnDemandJavaStackAccess .stackAccessSupplier ();
@@ -309,29 +316,34 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
309316 shouldExcludeClassesWithHash = omittedConfiguration .getPredefinedClassesConfiguration ()::containsClassWithHash ;
310317 }
311318
312- if (configurationWithOrigins ) {
313- ConfigurationWithOriginsWriter writer = new ConfigurationWithOriginsWriter (processor , recordKeeper );
314- tracer = writer ;
315- tracingResultWriter = writer ;
316- } else if (!conditionalConfigUserPackageFilterFiles .isEmpty ()) {
317- ComplexFilter userCodeFilter = new ComplexFilter (HierarchyFilterNode .createRoot ());
318- if (!parseFilterFiles (userCodeFilter , conditionalConfigUserPackageFilterFiles )) {
319- return 2 ;
320- }
321- ComplexFilter classNameFilter ;
322- if (!conditionalConfigClassNameFilterFiles .isEmpty ()) {
323- classNameFilter = new ComplexFilter (HierarchyFilterNode .createRoot ());
324- if (!parseFilterFiles (classNameFilter , conditionalConfigClassNameFilterFiles )) {
325- return 3 ;
319+ if (shouldTraceOriginInformation ) {
320+ ConfigurationWithOriginsTracer configWithOriginsTracer = new ConfigurationWithOriginsTracer (processor , recordKeeper );
321+ tracer = configWithOriginsTracer ;
322+
323+ if (isConditionalConfigurationRun ) {
324+ if (conditionalConfigPartialRun ) {
325+ tracingResultWriter = new ConditionalConfigurationPartialRunWriter (configWithOriginsTracer );
326+ } else {
327+ ComplexFilter userCodeFilter = new ComplexFilter (HierarchyFilterNode .createRoot ());
328+ if (!parseFilterFiles (userCodeFilter , conditionalConfigUserPackageFilterFiles )) {
329+ return 2 ;
330+ }
331+ ComplexFilter classNameFilter ;
332+ if (!conditionalConfigClassNameFilterFiles .isEmpty ()) {
333+ classNameFilter = new ComplexFilter (HierarchyFilterNode .createRoot ());
334+ if (!parseFilterFiles (classNameFilter , conditionalConfigClassNameFilterFiles )) {
335+ return 3 ;
336+ }
337+ } else {
338+ classNameFilter = new ComplexFilter (HierarchyFilterNode .createInclusiveRoot ());
339+ }
340+
341+ ConditionalConfigurationPredicate predicate = new ConditionalConfigurationPredicate (classNameFilter );
342+ tracingResultWriter = new ConditionalConfigurationWriter (configWithOriginsTracer , userCodeFilter , predicate );
326343 }
327344 } else {
328- classNameFilter = new ComplexFilter ( HierarchyFilterNode . createInclusiveRoot () );
345+ tracingResultWriter = new ConfigurationWithOriginsWriter ( configWithOriginsTracer );
329346 }
330-
331- ConditionalConfigurationPredicate predicate = new ConditionalConfigurationPredicate (classNameFilter );
332- ConditionalConfigurationWriter writer = new ConditionalConfigurationWriter (processor , recordKeeper , userCodeFilter , predicate );
333- tracer = writer ;
334- tracingResultWriter = writer ;
335347 } else {
336348 Path [] predefinedClassDestDirs = {Files .createDirectories (configOutputDirPath .resolve (ConfigurationFile .PREDEFINED_CLASSES_AGENT_EXTRACTED_SUBDIR ))};
337349 Function <IOException , Exception > handler = e -> {
0 commit comments