Skip to content

Commit a2279e6

Browse files
authored
Include module name in output of JDK api extractor tool (#135639)
1 parent 1eeab39 commit a2279e6

File tree

2 files changed

+55
-34
lines changed

2 files changed

+55
-34
lines changed

libs/entitlement/tools/jdk-api-extractor/README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,23 @@ That is:
66
The output of this tool is meant to be diffed against the output for another JDK
77
version to identify changes that need to be reviewed for entitlements.
88

9+
The following `TAB`-separated columns are written:
10+
1. module name
11+
2. fully qualified class name (ASM style, with `/` separators)
12+
3. method name
13+
4. method descriptor (ASM signature)
14+
5. visibility (`PUBLIC` / `PROTECTED`)
15+
6. `STATIC` modifier or empty
16+
7. `FINAL` modifier or empty
17+
918
Usage example:
1019
```bash
1120
./gradlew :libs:entitlement:tools:jdk-api-extractor:run -Druntime.java=24 --args="api-jdk24.tsv"
1221
./gradlew :libs:entitlement:tools:jdk-api-extractor:run -Druntime.java=25 --args="api-jdk25.tsv"
13-
diff libs/entitlement/tools/jdk-api-extractor/api-jdk24.tsv libs/entitlement/tools/jdk-api-extractor/api-jdk25.tsv
22+
# diff the public apis
23+
diff -u libs/entitlement/tools/jdk-api-extractor/api-jdk24.tsv libs/entitlement/tools/jdk-api-extractor/api-jdk25.tsv > libs/entitlement/tools/jdk-api-extractor/api.diff
24+
# extract additions in the new JDK, these require the most careful review
25+
cat libs/entitlement/tools/jdk-api-extractor/api.diff | grep '^+[^+]' | sed 's/^+//' > api-jdk25-additions.tsv
1426
```
1527

1628
### Optional arguments:

libs/entitlement/tools/jdk-api-extractor/src/main/java/org/elasticsearch/entitlement/tools/jdkapi/JdkApiExtractor.java

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.stream.Collectors;
3434
import java.util.stream.Stream;
3535

36+
import static java.util.Collections.emptySet;
3637
import static org.objectweb.asm.Opcodes.ACC_DEPRECATED;
3738
import static org.objectweb.asm.Opcodes.ACC_FINAL;
3839
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
@@ -64,9 +65,9 @@ public static void main(String[] args) throws IOException {
6465
boolean deprecationsOnly = optionalArgs(args).anyMatch(DEPRECATIONS_ONLY::equals);
6566

6667
final Map<String, String> moduleNameByClass = new HashMap<>();
67-
final Map<String, Set<AccessibleMethod>> accessibleImplementationsByClass = new TreeMap<>();
68-
final Map<String, Set<AccessibleMethod>> accessibleForOverridesByClass = new TreeMap<>();
69-
final Map<String, Set<AccessibleMethod>> deprecationsByClass = new TreeMap<>();
68+
final Map<ModuleClass, Set<AccessibleMethod>> accessibleImplementationsByClass = new TreeMap<>(ModuleClass.COMPARATOR);
69+
final Map<ModuleClass, Set<AccessibleMethod>> accessibleForOverridesByClass = new TreeMap<>(ModuleClass.COMPARATOR);
70+
final Map<ModuleClass, Set<AccessibleMethod>> deprecationsByClass = new TreeMap<>(ModuleClass.COMPARATOR);
7071

7172
final Map<String, Set<String>> exportsByModule = Utils.findModuleExports();
7273
// 1st: map class names to module names (including later excluded modules) for lookup in 2nd step
@@ -93,7 +94,8 @@ public static void main(String[] args) throws IOException {
9394
Utils.walkJdkModules(modulePredicate, exportsByModule, (moduleName, moduleClasses, moduleExports) -> {
9495
for (var classFile : moduleClasses) {
9596
// skip if class was already visited earlier due to a dependency on it
96-
if (accessibleImplementationsByClass.containsKey(internalClassName(classFile, moduleName))) {
97+
String className = internalClassName(classFile, moduleName);
98+
if (accessibleImplementationsByClass.containsKey(new ModuleClass(moduleName, className))) {
9799
continue;
98100
}
99101
try {
@@ -106,15 +108,14 @@ public static void main(String[] args) throws IOException {
106108
});
107109

108110
// finally, skip some implementations we're not interested in
109-
Predicate<Map.Entry<String, Set<AccessibleMethod>>> predicate = entry -> {
110-
if (entry.getKey().startsWith("com/sun/") && entry.getKey().contains("/internal/")) {
111+
Predicate<Map.Entry<ModuleClass, Set<AccessibleMethod>>> predicate = entry -> {
112+
if (entry.getKey().clazz.startsWith("com/sun/") && entry.getKey().clazz.contains("/internal/")) {
111113
// skip com.sun.*.internal classes as they are not part of the supported JDK API
112114
// even if methods override some publicly visible API
113115
return false;
114116
}
115117
// skip classes that are not part of included modules, but checked due to dependencies
116-
String moduleName = moduleNameByClass.get(entry.getKey());
117-
return modulePredicate.test(moduleName);
118+
return modulePredicate.test(entry.getKey().module);
118119
};
119120
writeFile(Path.of(args[0]), deprecationsOnly ? deprecationsByClass : accessibleImplementationsByClass, predicate);
120121
}
@@ -159,8 +160,8 @@ private static boolean isWritableOutputPath(String pathStr) {
159160
@SuppressForbidden(reason = "cli tool printing to standard err/out")
160161
private static void writeFile(
161162
Path path,
162-
Map<String, Set<AccessibleMethod>> methods,
163-
Predicate<Map.Entry<String, Set<AccessibleMethod>>> predicate
163+
Map<ModuleClass, Set<AccessibleMethod>> methods,
164+
Predicate<Map.Entry<ModuleClass, Set<AccessibleMethod>>> predicate
164165
) throws IOException {
165166
System.out.println("Writing result for " + Runtime.version() + " to " + path.toAbsolutePath());
166167
Files.write(
@@ -170,6 +171,11 @@ private static void writeFile(
170171
);
171172
}
172173

174+
record ModuleClass(String module, String clazz) {
175+
private static final Comparator<ModuleClass> COMPARATOR = Comparator.comparing(ModuleClass::module)
176+
.thenComparing(ModuleClass::clazz);
177+
}
178+
173179
record AccessibleMethod(String method, String descriptor, boolean isPublic, boolean isFinal, boolean isStatic) {
174180

175181
private static final String SEPARATOR = "\t";
@@ -178,10 +184,11 @@ record AccessibleMethod(String method, String descriptor, boolean isPublic, bool
178184
.thenComparing(AccessibleMethod::descriptor)
179185
.thenComparing(AccessibleMethod::isStatic);
180186

181-
CharSequence toLine(String clazz) {
187+
CharSequence toLine(ModuleClass moduleClass) {
182188
return String.join(
183189
SEPARATOR,
184-
clazz,
190+
moduleClass.module,
191+
moduleClass.clazz,
185192
method,
186193
descriptor,
187194
isPublic ? "PUBLIC" : "PROTECTED",
@@ -190,23 +197,23 @@ CharSequence toLine(String clazz) {
190197
);
191198
}
192199

193-
static Stream<CharSequence> toLines(Map.Entry<String, Set<AccessibleMethod>> entry) {
200+
static Stream<CharSequence> toLines(Map.Entry<ModuleClass, Set<AccessibleMethod>> entry) {
194201
return entry.getValue().stream().map(m -> m.toLine(entry.getKey()));
195202
}
196203
}
197204

198205
static class AccessibleClassVisitor extends ClassVisitor {
199206
private final Map<String, String> moduleNameByClass;
200207
private final Map<String, Set<String>> exportsByModule;
201-
private final Map<String, Set<AccessibleMethod>> accessibleImplementationsByClass;
202-
private final Map<String, Set<AccessibleMethod>> accessibleForOverridesByClass;
203-
private final Map<String, Set<AccessibleMethod>> deprecationsByClass;
208+
private final Map<ModuleClass, Set<AccessibleMethod>> accessibleImplementationsByClass;
209+
private final Map<ModuleClass, Set<AccessibleMethod>> accessibleForOverridesByClass;
210+
private final Map<ModuleClass, Set<AccessibleMethod>> deprecationsByClass;
204211

205212
private Set<AccessibleMethod> accessibleImplementations;
206213
private Set<AccessibleMethod> accessibleForOverrides;
207214
private Set<AccessibleMethod> deprecations;
208215

209-
private String className;
216+
private ModuleClass moduleClass;
210217
private boolean isPublicClass;
211218
private boolean isFinalClass;
212219
private boolean isDeprecatedClass;
@@ -215,9 +222,9 @@ static class AccessibleClassVisitor extends ClassVisitor {
215222
AccessibleClassVisitor(
216223
Map<String, String> moduleNameByClass,
217224
Map<String, Set<String>> exportsByModule,
218-
Map<String, Set<AccessibleMethod>> accessibleImplementationsByClass,
219-
Map<String, Set<AccessibleMethod>> accessibleForOverridesByClass,
220-
Map<String, Set<AccessibleMethod>> deprecationsByClass
225+
Map<ModuleClass, Set<AccessibleMethod>> accessibleImplementationsByClass,
226+
Map<ModuleClass, Set<AccessibleMethod>> accessibleForOverridesByClass,
227+
Map<ModuleClass, Set<AccessibleMethod>> deprecationsByClass
221228
) {
222229
super(ASM9);
223230
this.moduleNameByClass = moduleNameByClass;
@@ -235,23 +242,25 @@ private static Set<AccessibleMethod> newSortedSet() {
235242
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
236243
final Set<AccessibleMethod> currentAccessibleForOverrides = newSortedSet();
237244
if (superName != null) {
238-
if (accessibleImplementationsByClass.containsKey(superName) == false) {
245+
var superModuleClass = getModuleClass(superName);
246+
if (accessibleImplementationsByClass.containsKey(superModuleClass) == false) {
239247
visitSuperClass(superName);
240248
}
241-
currentAccessibleForOverrides.addAll(accessibleForOverridesByClass.getOrDefault(superName, Collections.emptySet()));
249+
currentAccessibleForOverrides.addAll(accessibleForOverridesByClass.getOrDefault(superModuleClass, emptySet()));
242250
}
243251
if (interfaces != null && interfaces.length > 0) {
244252
for (var interfaceName : interfaces) {
245-
if (accessibleImplementationsByClass.containsKey(interfaceName) == false) {
253+
var interfaceModuleClass = getModuleClass(interfaceName);
254+
if (accessibleImplementationsByClass.containsKey(interfaceModuleClass) == false) {
246255
visitInterface(interfaceName);
247256
}
248-
currentAccessibleForOverrides.addAll(accessibleForOverridesByClass.getOrDefault(interfaceName, Collections.emptySet()));
257+
currentAccessibleForOverrides.addAll(accessibleForOverridesByClass.getOrDefault(interfaceModuleClass, emptySet()));
249258
}
250259
}
251260
// only initialize local state AFTER visiting all dependencies above!
252261
super.visit(version, access, name, signature, superName, interfaces);
253-
this.isExported = getModuleExports(getModuleName(name)).contains(getPackageName(name));
254-
this.className = name;
262+
this.moduleClass = getModuleClass(name);
263+
this.isExported = getModuleExports(moduleClass.module).contains(getPackageName(name));
255264
this.isPublicClass = (access & ACC_PUBLIC) != 0;
256265
this.isFinalClass = (access & ACC_FINAL) != 0;
257266
this.isDeprecatedClass = (access & ACC_DEPRECATED) != 0;
@@ -260,12 +269,12 @@ public void visit(int version, int access, String name, String signature, String
260269
this.deprecations = newSortedSet();
261270
}
262271

263-
private String getModuleName(String name) {
272+
private ModuleClass getModuleClass(String name) {
264273
String module = moduleNameByClass.get(name);
265274
if (module == null) {
266275
throw new IllegalStateException("Unknown module for class: " + name);
267276
}
268-
return module;
277+
return new ModuleClass(module, name);
269278
}
270279

271280
private Set<String> getModuleExports(String module) {
@@ -279,15 +288,15 @@ private Set<String> getModuleExports(String module) {
279288
@Override
280289
public void visitEnd() {
281290
super.visitEnd();
282-
if (accessibleImplementationsByClass.put(className, unmodifiableSet(accessibleImplementations)) != null
283-
|| accessibleForOverridesByClass.put(className, unmodifiableSet(accessibleForOverrides)) != null
284-
|| deprecationsByClass.put(className, unmodifiableSet(deprecations)) != null) {
285-
throw new IllegalStateException("Class " + className + " was already visited!");
291+
if (accessibleImplementationsByClass.put(moduleClass, unmodifiableSet(accessibleImplementations)) != null
292+
|| accessibleForOverridesByClass.put(moduleClass, unmodifiableSet(accessibleForOverrides)) != null
293+
|| deprecationsByClass.put(moduleClass, unmodifiableSet(deprecations)) != null) {
294+
throw new IllegalStateException("Class " + moduleClass.clazz + " was already visited!");
286295
}
287296
}
288297

289298
private static Set<AccessibleMethod> unmodifiableSet(Set<AccessibleMethod> set) {
290-
return set.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(set);
299+
return set.isEmpty() ? emptySet() : Collections.unmodifiableSet(set);
291300
}
292301

293302
@SuppressForbidden(reason = "cli tool printing to standard err/out")

0 commit comments

Comments
 (0)