41
41
import java .nio .file .Path ;
42
42
import java .util .ArrayList ;
43
43
import java .util .Collections ;
44
- import java .util .Comparator ;
44
+ import java .util .HashMap ;
45
45
import java .util .HashSet ;
46
46
import java .util .Objects ;
47
47
import java .util .Set ;
@@ -60,71 +60,54 @@ public final class AnalyzeMethodsRequiringMetadataUsageFeature implements Intern
60
60
public static final String METHODTYPE_RESOURCE = "resource" ;
61
61
public static final String METHODTYPE_SERIALIZATION = "serialization" ;
62
62
public static final String METHODTYPE_PROXY = "proxy" ;
63
- private final Map <String , List <String >> reflectiveCalls ;
64
- private final Map <String , List <String >> resourceCalls ;
65
- private final Map <String , List <String >> serializationCalls ;
66
- private final Map <String , List <String >> proxyCalls ;
67
63
private final Set <String > jarPaths ;
64
+ private final Map <String , Map <String , Map <String , List <String >>>> callsByJar ;
68
65
private final Set <FoldEntry > foldEntries = ConcurrentHashMap .newKeySet ();
69
66
70
67
public AnalyzeMethodsRequiringMetadataUsageFeature () {
71
- this .reflectiveCalls = new TreeMap <>();
72
- this .resourceCalls = new TreeMap <>();
73
- this .serializationCalls = new TreeMap <>();
74
- this .proxyCalls = new TreeMap <>();
68
+ this .callsByJar = new ConcurrentHashMap <>();
75
69
this .jarPaths = Collections .unmodifiableSet (new HashSet <>(AnalyzeMethodsRequiringMetadataUsageFeature .Options .TrackMethodsRequiringMetadata .getValue ().values ()));
76
70
}
77
71
78
72
public static AnalyzeMethodsRequiringMetadataUsageFeature instance () {
79
73
return ImageSingletons .lookup (AnalyzeMethodsRequiringMetadataUsageFeature .class );
80
74
}
81
75
82
- public void addCall (String methodType , String call , String callLocation ) {
83
- switch (methodType ) {
84
- case METHODTYPE_REFLECTION -> this .reflectiveCalls .computeIfAbsent (call , k -> new ArrayList <>()).add (callLocation );
85
- case METHODTYPE_RESOURCE -> this .resourceCalls .computeIfAbsent (call , k -> new ArrayList <>()).add (callLocation );
86
- case METHODTYPE_SERIALIZATION -> this .serializationCalls .computeIfAbsent (call , k -> new ArrayList <>()).add (callLocation );
87
- case METHODTYPE_PROXY -> this .proxyCalls .computeIfAbsent (call , k -> new ArrayList <>()).add (callLocation );
88
- default -> throw new IllegalArgumentException ("Unknown method type: " + methodType );
89
- }
76
+ public void addCall (String jarPath , String methodType , String call , String callLocation ) {
77
+ this .callsByJar .computeIfAbsent (jarPath , k -> new HashMap <>());
78
+ this .callsByJar .get (jarPath ).computeIfAbsent (methodType , k -> new TreeMap <>());
79
+ this .callsByJar .get (jarPath ).get (methodType ).computeIfAbsent (call , k -> new ArrayList <>());
80
+ this .callsByJar .get (jarPath ).get (methodType ).get (call ).add (callLocation );
90
81
}
91
82
92
- public void printReport (String methodType ) {
93
- Map <String , List <String >> callMap = switch (methodType ) {
94
- case METHODTYPE_REFLECTION -> this .reflectiveCalls ;
95
- case METHODTYPE_RESOURCE -> this .resourceCalls ;
96
- case METHODTYPE_SERIALIZATION -> this .serializationCalls ;
97
- case METHODTYPE_PROXY -> this .proxyCalls ;
98
- default -> throw new IllegalArgumentException ("Unknown method type: " + methodType );
99
- };
100
-
101
- System .out .println (methodType .substring (0 , 1 ).toUpperCase () + methodType .substring (1 ) + " calls detected:" );
102
- for (String key : callMap .keySet ()) {
103
- System .out .println (" " + key + ":" );
104
- callMap .get (key ).sort (Comparator .comparing (String ::toString ));
105
- for (String callLocation : callMap .get (key )) {
106
- System .out .println (" at " + callLocation );
83
+ public void printReportForJar (String jarPath ) {
84
+ System .out .println ("Dynamic method usage detected in " + jarPath + ":" );
85
+ for (String methodType : callsByJar .get (jarPath ).keySet ()) {
86
+ System .out .println (" " + methodType .substring (0 , 1 ).toUpperCase () + methodType .substring (1 ) + " calls detected:" );
87
+ for (String call : callsByJar .get (jarPath ).get (methodType ).keySet ()) {
88
+ System .out .println (" " + call + ":" );
89
+ for (String callLocation : callsByJar .get (jarPath ).get (methodType ).get (call )) {
90
+ System .out .println (" at " + callLocation );
91
+ }
107
92
}
108
93
}
109
94
}
110
95
111
- public void dumpReport (String methodType ) {
112
- Map <String , List <String >> calls = switch (methodType ) {
113
- case METHODTYPE_REFLECTION -> this .reflectiveCalls ;
114
- case METHODTYPE_RESOURCE -> this .resourceCalls ;
115
- case METHODTYPE_SERIALIZATION -> this .serializationCalls ;
116
- case METHODTYPE_PROXY -> this .proxyCalls ;
117
- default -> throw new IllegalArgumentException ("Unknown method type: " + methodType );
118
- };
119
- String fileName = methodType + "-usage.json" ;
120
-
96
+ public void dumpReportForJar (String jarPath ) {
97
+ String fileName = extractLibraryName (jarPath ) + "-method-calls.json" ;
98
+ Map <String , Map <String , List <String >>> calls = callsByJar .get (jarPath );
121
99
Path targetPath = NativeImageGenerator .generatedFiles (HostedOptionValues .singleton ()).resolve (fileName );
122
100
try (var writer = new JsonWriter (targetPath );
123
101
var builder = writer .objectBuilder ()) {
124
- for (Map .Entry <String , List <String >> entry : calls .entrySet ()) {
125
- try (JsonBuilder .ArrayBuilder array = builder .append (entry .getKey ()).array ()) {
126
- for (String call : entry .getValue ()) {
127
- array .append (call );
102
+ for (Map .Entry <String , Map <String , List <String >>> callEntry : calls .entrySet ()) {
103
+ try (JsonBuilder .ObjectBuilder methodsByTypeBuilder = builder .append (callEntry .getKey ()).object ()) {
104
+ Map <String , List <String >> nestedMap = callEntry .getValue ();
105
+ for (Map .Entry <String , List <String >> entry : nestedMap .entrySet ()) {
106
+ try (JsonBuilder .ArrayBuilder array = methodsByTypeBuilder .append (entry .getKey ()).array ()) {
107
+ for (String call : entry .getValue ()) {
108
+ array .append (call );
109
+ }
110
+ }
128
111
}
129
112
}
130
113
}
@@ -136,25 +119,26 @@ public void dumpReport(String methodType) {
136
119
}
137
120
138
121
public void reportMethodUsage () {
139
- Map <String , Map <String , List <String >>> callMaps = Map .of (
140
- METHODTYPE_REFLECTION , this .reflectiveCalls ,
141
- METHODTYPE_RESOURCE , this .resourceCalls ,
142
- METHODTYPE_SERIALIZATION , this .serializationCalls ,
143
- METHODTYPE_PROXY , this .proxyCalls );
144
- for (Map .Entry <String , Map <String , List <String >>> entry : callMaps .entrySet ()) {
145
- String methodType = entry .getKey ();
146
- Map <String , List <String >> calls = entry .getValue ();
147
- if (!calls .isEmpty ()) {
148
- printReport (methodType );
149
- dumpReport (methodType );
150
- }
122
+ for (String jarPath : jarPaths ) {
123
+ printReportForJar (jarPath );
124
+ dumpReportForJar (jarPath );
151
125
}
152
126
}
153
127
154
128
public Set <String > getJarPaths () {
155
129
return jarPaths ;
156
130
}
157
131
132
+ public String extractLibraryName (String path ) {
133
+ String fileName = path .substring (path .lastIndexOf ("/" ) + 1 );
134
+
135
+ if (fileName .endsWith (".jar" )) {
136
+ fileName = fileName .substring (0 , fileName .length () - 4 );
137
+ }
138
+
139
+ return fileName ;
140
+ }
141
+
158
142
/*
159
143
* Support data structure used to keep track of calls which don't require metadata, but can't be
160
144
* folded.
0 commit comments