5454import org .graalvm .nativeimage .ProcessProperties ;
5555import org .graalvm .nativeimage .hosted .Feature ;
5656
57+ import com .oracle .svm .agent .conditionalconfig .ConditionalConfigurationWriter ;
58+ import com .oracle .svm .agent .configwithorigins .ConfigurationWithOriginsWriter ;
59+ import com .oracle .svm .agent .configwithorigins .MethodInfoRecordKeeper ;
5760import com .oracle .svm .agent .ignoredconfig .AgentMetaInfProcessor ;
58- import com .oracle .svm .agent .predicatedconfig .ConfigurationWithOriginsResultWriter ;
59- import com .oracle .svm .agent .predicatedconfig .MethodInfoRecordKeeper ;
6061import com .oracle .svm .agent .stackaccess .EagerlyLoadedJavaStackAccess ;
6162import com .oracle .svm .agent .stackaccess .InterceptedState ;
6263import com .oracle .svm .agent .stackaccess .OnDemandJavaStackAccess ;
6364import com .oracle .svm .agent .tracing .ConfigurationResultWriter ;
6465import com .oracle .svm .agent .tracing .TraceFileWriter ;
6566import com .oracle .svm .agent .tracing .core .Tracer ;
6667import com .oracle .svm .agent .tracing .core .TracingResultWriter ;
68+ import com .oracle .svm .configure .config .ConditionalConfigurationPredicate ;
69+ import com .oracle .svm .configure .config .ConfigurationFileCollection ;
6770import com .oracle .svm .configure .config .ConfigurationSet ;
71+ import com .oracle .svm .configure .filters .ComplexFilter ;
72+ import com .oracle .svm .configure .filters .ConfigurationFilter ;
6873import com .oracle .svm .configure .filters .FilterConfigurationParser ;
69- import com .oracle .svm .configure .filters .RuleNode ;
74+ import com .oracle .svm .configure .filters .HierarchyFilterNode ;
7075import com .oracle .svm .configure .trace .AccessAdvisor ;
7176import com .oracle .svm .configure .trace .TraceProcessor ;
7277import com .oracle .svm .core .SubstrateUtil ;
@@ -114,8 +119,8 @@ protected JNIHandleSet constructJavaHandles(JNIEnvironment env) {
114119 protected int onLoadCallback (JNIJavaVM vm , JvmtiEnv jvmti , JvmtiEventCallbacks callbacks , String options ) {
115120 String traceOutputFile = null ;
116121 String configOutputDir = null ;
117- ConfigurationSet mergeConfigs = new ConfigurationSet ();
118- ConfigurationSet omittedConfigs = new ConfigurationSet ();
122+ ConfigurationFileCollection mergeConfigs = new ConfigurationFileCollection ();
123+ ConfigurationFileCollection omittedConfigs = new ConfigurationFileCollection ();
119124 boolean builtinCallerFilter = true ;
120125 boolean builtinHeuristicFilter = true ;
121126 List <String > callerFilterFiles = new ArrayList <>();
@@ -125,6 +130,8 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
125130 boolean experimentalOmitClasspathConfig = false ;
126131 boolean build = false ;
127132 boolean configurationWithOrigins = false ;
133+ List <String > conditionalConfigUserPackageFilterFiles = new ArrayList <>();
134+ List <String > conditionalConfigClassNameFilterFiles = new ArrayList <>();
128135 int configWritePeriod = -1 ; // in seconds
129136 int configWritePeriodInitialDelay = 1 ; // in seconds
130137 boolean trackReflectionMetadata = true ;
@@ -196,6 +203,10 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
196203 build = Boolean .parseBoolean (getTokenValue (token ));
197204 } else if (token .equals ("experimental-configuration-with-origins" )) {
198205 configurationWithOrigins = true ;
206+ } else if (token .startsWith ("experimental-conditional-config-filter-file=" )) {
207+ conditionalConfigUserPackageFilterFiles .add (getTokenValue (token ));
208+ } else if (token .startsWith ("conditional-config-class-filter-file=" )) {
209+ conditionalConfigClassNameFilterFiles .add (getTokenValue (token ));
199210 } else if (token .equals ("track-reflection-metadata" )) {
200211 trackReflectionMetadata = true ;
201212 } else if (token .startsWith ("track-reflection-metadata=" )) {
@@ -210,6 +221,10 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
210221 inform ("no output/build options provided, tracking dynamic accesses and writing configuration to directory: " + configOutputDir );
211222 }
212223
224+ if (configurationWithOrigins && !conditionalConfigUserPackageFilterFiles .isEmpty ()) {
225+ return error (5 , "The agent can only be used in either the configuration with origins mode or the predefined classes mode." );
226+ }
227+
213228 if (configurationWithOrigins && !mergeConfigs .isEmpty ()) {
214229 configurationWithOrigins = false ;
215230 inform ("using configuration with origins with configuration merging is currently unsupported. Disabling configuration with origins mode." );
@@ -219,30 +234,34 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
219234 warn ("using experimental configuration with origins mode. Note that native-image cannot process these files, and this flag may change or be removed without a warning!" );
220235 }
221236
222- RuleNode callerFilter = null ;
237+ ComplexFilter callerFilter = null ;
238+ HierarchyFilterNode callerFilterHierarchyFilterNode = null ;
223239 if (!builtinCallerFilter ) {
224- callerFilter = RuleNode . createRoot ();
225- callerFilter . addOrGetChildren ( "**" , RuleNode . Inclusion . Include );
240+ callerFilterHierarchyFilterNode = HierarchyFilterNode . createInclusiveRoot ();
241+ callerFilter = new ComplexFilter ( callerFilterHierarchyFilterNode );
226242 }
243+
227244 if (!callerFilterFiles .isEmpty ()) {
228- if (callerFilter == null ) {
229- callerFilter = AccessAdvisor .copyBuiltinCallerFilterTree ();
245+ if (callerFilterHierarchyFilterNode == null ) {
246+ callerFilterHierarchyFilterNode = AccessAdvisor .copyBuiltinCallerFilterTree ();
247+ callerFilter = new ComplexFilter (callerFilterHierarchyFilterNode );
230248 }
231249 if (!parseFilterFiles (callerFilter , callerFilterFiles )) {
232250 return 1 ;
233251 }
234252 }
235253
236- RuleNode accessFilter = null ;
254+ ComplexFilter accessFilter = null ;
237255 if (!accessFilterFiles .isEmpty ()) {
238- accessFilter = AccessAdvisor .copyBuiltinAccessFilterTree ();
256+ accessFilter = new ComplexFilter ( AccessAdvisor .copyBuiltinAccessFilterTree () );
239257 if (!parseFilterFiles (accessFilter , accessFilterFiles )) {
240258 return 1 ;
241259 }
242260 }
243261
244- final MethodInfoRecordKeeper recordKeeper = new MethodInfoRecordKeeper (configurationWithOrigins );
245- final Supplier <InterceptedState > interceptedStateSupplier = configurationWithOrigins ? EagerlyLoadedJavaStackAccess .stackAccessSupplier ()
262+ boolean shouldTraceOriginInformation = configurationWithOrigins || !conditionalConfigUserPackageFilterFiles .isEmpty ();
263+ final MethodInfoRecordKeeper recordKeeper = new MethodInfoRecordKeeper (shouldTraceOriginInformation );
264+ final Supplier <InterceptedState > interceptedStateSupplier = shouldTraceOriginInformation ? EagerlyLoadedJavaStackAccess .stackAccessSupplier ()
246265 : OnDemandJavaStackAccess .stackAccessSupplier ();
247266
248267 if (configOutputDir != null ) {
@@ -273,21 +292,39 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
273292 ignoreConfigFromClasspath (jvmti , omittedConfigs );
274293 }
275294 AccessAdvisor advisor = createAccessAdvisor (builtinHeuristicFilter , callerFilter , accessFilter );
276- TraceProcessor omittedConfigProcessor = null ;
295+ TraceProcessor processor = new TraceProcessor (advisor );
296+ ConfigurationSet omittedConfiguration = new ConfigurationSet ();
277297 Predicate <String > shouldExcludeClassesWithHash = null ;
278298 if (!omittedConfigs .isEmpty ()) {
279299 Function <IOException , Exception > ignore = e -> {
280300 warn ("Failed to load omitted config: " + e );
281301 return null ;
282302 };
283- omittedConfigProcessor = new TraceProcessor (advisor , omittedConfigs .loadJniConfig (ignore ), omittedConfigs .loadReflectConfig (ignore ),
284- omittedConfigs .loadProxyConfig (ignore ), omittedConfigs .loadResourceConfig (ignore ), omittedConfigs .loadSerializationConfig (ignore ),
285- omittedConfigs .loadPredefinedClassesConfig (null , null , ignore ), null );
286- shouldExcludeClassesWithHash = omittedConfigProcessor .getPredefinedClassesConfiguration ()::containsClassWithHash ;
303+ omittedConfiguration = omittedConfigs .loadConfigurationSet (ignore , null , null );
304+ shouldExcludeClassesWithHash = omittedConfiguration .getPredefinedClassesConfiguration ()::containsClassWithHash ;
287305 }
288306
289307 if (configurationWithOrigins ) {
290- ConfigurationWithOriginsResultWriter writer = new ConfigurationWithOriginsResultWriter (advisor , recordKeeper );
308+ ConfigurationWithOriginsWriter writer = new ConfigurationWithOriginsWriter (processor , recordKeeper );
309+ tracer = writer ;
310+ tracingResultWriter = writer ;
311+ } else if (!conditionalConfigUserPackageFilterFiles .isEmpty ()) {
312+ ComplexFilter userCodeFilter = new ComplexFilter (HierarchyFilterNode .createRoot ());
313+ if (!parseFilterFiles (userCodeFilter , conditionalConfigUserPackageFilterFiles )) {
314+ return 2 ;
315+ }
316+ ComplexFilter classNameFilter ;
317+ if (!conditionalConfigClassNameFilterFiles .isEmpty ()) {
318+ classNameFilter = new ComplexFilter (HierarchyFilterNode .createRoot ());
319+ if (!parseFilterFiles (classNameFilter , conditionalConfigClassNameFilterFiles )) {
320+ return 3 ;
321+ }
322+ } else {
323+ classNameFilter = new ComplexFilter (HierarchyFilterNode .createInclusiveRoot ());
324+ }
325+
326+ ConditionalConfigurationPredicate predicate = new ConditionalConfigurationPredicate (classNameFilter );
327+ ConditionalConfigurationWriter writer = new ConditionalConfigurationWriter (processor , recordKeeper , userCodeFilter , predicate );
291328 tracer = writer ;
292329 tracingResultWriter = writer ;
293330 } else {
@@ -302,10 +339,9 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
302339 }
303340 return e ; // rethrow
304341 };
305- TraceProcessor processor = new TraceProcessor (advisor , mergeConfigs .loadJniConfig (handler ), mergeConfigs .loadReflectConfig (handler ),
306- mergeConfigs .loadProxyConfig (handler ), mergeConfigs .loadResourceConfig (handler ), mergeConfigs .loadSerializationConfig (handler ),
307- mergeConfigs .loadPredefinedClassesConfig (predefinedClassDestDirs , shouldExcludeClassesWithHash , handler ), omittedConfigProcessor );
308- ConfigurationResultWriter writer = new ConfigurationResultWriter (processor );
342+
343+ ConfigurationSet configuration = mergeConfigs .loadConfigurationSet (handler , predefinedClassDestDirs , shouldExcludeClassesWithHash );
344+ ConfigurationResultWriter writer = new ConfigurationResultWriter (processor , configuration , omittedConfiguration );
309345 tracer = writer ;
310346 tracingResultWriter = writer ;
311347 }
@@ -368,7 +404,7 @@ private static <T> T usage(T result, String message) {
368404 return result ;
369405 }
370406
371- private static AccessAdvisor createAccessAdvisor (boolean builtinHeuristicFilter , RuleNode callerFilter , RuleNode accessFilter ) {
407+ private static AccessAdvisor createAccessAdvisor (boolean builtinHeuristicFilter , ConfigurationFilter callerFilter , ConfigurationFilter accessFilter ) {
372408 AccessAdvisor advisor = new AccessAdvisor ();
373409 advisor .setHeuristicsEnabled (builtinHeuristicFilter );
374410 if (callerFilter != null ) {
@@ -388,15 +424,15 @@ private static int parseIntegerOrNegative(String number) {
388424 }
389425 }
390426
391- private static boolean parseFilterFiles (RuleNode filter , List <String > filterFiles ) {
427+ private static boolean parseFilterFiles (ComplexFilter filter , List <String > filterFiles ) {
392428 for (String path : filterFiles ) {
393429 try {
394430 new FilterConfigurationParser (filter ).parseAndRegister (Paths .get (path ).toUri ());
395431 } catch (Exception e ) {
396432 return error (false , "cannot parse filter file " + path + ": " + e );
397433 }
398434 }
399- filter .removeRedundantNodes ();
435+ filter .getHierarchyFilterNode (). removeRedundantNodes ();
400436 return true ;
401437 }
402438
@@ -422,7 +458,7 @@ private void setupExecutorServiceForPeriodicConfigurationCapture(int writePeriod
422458 initialDelay , writePeriod , TimeUnit .SECONDS );
423459 }
424460
425- private static void ignoreConfigFromClasspath (JvmtiEnv jvmti , ConfigurationSet ignoredConfigSet ) {
461+ private static void ignoreConfigFromClasspath (JvmtiEnv jvmti , ConfigurationFileCollection ignoredConfigCollection ) {
426462 String classpath = Support .getSystemProperty (jvmti , "java.class.path" );
427463 String sep = Support .getSystemProperty (jvmti , "path.separator" );
428464 if (sep == null ) {
@@ -436,7 +472,7 @@ private static void ignoreConfigFromClasspath(JvmtiEnv jvmti, ConfigurationSet i
436472 }
437473 }
438474
439- AgentMetaInfProcessor processor = new AgentMetaInfProcessor (ignoredConfigSet );
475+ AgentMetaInfProcessor processor = new AgentMetaInfProcessor (ignoredConfigCollection );
440476 for (String cpEntry : classpath .split (sep )) {
441477 try {
442478 NativeImageMetaInfWalker .walkMetaInfForCPEntry (Paths .get (cpEntry ), processor );
0 commit comments