< prev index next >

make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java

Print this page

 166  * that is specified should use literal value "<none>", to have all the APIs of the platform written to
 167  * the .sym.txt files. If there is an existing platform with full .sym.txt files in the repository,
 168  * that platform should be used as the first platform to avoid unnecessary changes to the .sym.txt
 169  * files. The <diff-against-platformN> for platform N should be determined as follows: if N < F, then
 170  * <diff-against-platformN> should be N + 1. If F < N, then <diff-against-platformN> should be N - 1.
 171  * If N is a custom/specialized sub-version of another platform N', then <diff-against-platformN> should be N'.
 172  *
 173  * To generate the .sym.txt files for OpenJDK 7 and 8:
 174  *     <jdk-7>/bin/java build.tools.symbolgenerator.Probe OpenJDK7.classes
 175  *     <jdk-8>/bin/java build.tools.symbolgenerator.Probe OpenJDK8.classes
 176  *     java build.tools.symbolgenerator.CreateSymbols build-description src/jdk.compiler/share/data/symbols
 177  *          $TOPDIR src/jdk.compiler/share/data/symbols/include.list
 178  *                                                    8 OpenJDK8.classes '<none>'
 179  *                                                    7 OpenJDK7.classes 8
 180  *
 181  * Note: the versions are expected to be a single character.
 182  *
 183  */
 184 public class CreateSymbols {
 185 












 186     //<editor-fold defaultstate="collapsed" desc="ct.sym construction">
 187     /**Create sig files for ct.sym reading the classes description from the directory that contains
 188      * {@code ctDescriptionFile}, using the file as a recipe to create the sigfiles.
 189      */
 190     public void createSymbols(String ctDescriptionFileExtra, String ctDescriptionFile, String ctSymLocation,
 191                               long timestamp, String currentVersion, String preReleaseTag, String moduleClasses,
 192                               String includedModulesFile) throws IOException {
 193         LoadDescriptions data = load(ctDescriptionFileExtra != null ? Paths.get(ctDescriptionFileExtra)
 194                                                                     : null,
 195                                      Paths.get(ctDescriptionFile));
 196 
 197         int currentVersionParsed = Integer.parseInt(currentVersion);
 198 
 199         currentVersion = Integer.toString(currentVersionParsed, Character.MAX_RADIX);
 200         currentVersion = currentVersion.toUpperCase(Locale.ROOT);
 201 
 202         String previousVersion = Integer.toString(currentVersionParsed - 1, Character.MAX_RADIX);
 203 
 204         previousVersion = previousVersion.toUpperCase(Locale.ROOT);
 205 
 206         //load current version classes:
 207         Path moduleClassPath = Paths.get(moduleClasses);
 208         Set<String> includedModules = Files.lines(Paths.get(includedModulesFile))
 209                                            .flatMap(l -> Arrays.stream(l.split(" ")))
 210                                            .collect(Collectors.toSet());
 211 
 212         loadVersionClassesFromDirectory(data.classes, data.modules, moduleClassPath,
 213                                         includedModules, currentVersion, previousVersion);
 214 



 215         stripNonExistentAnnotations(data);
 216         splitHeaders(data.classes);
 217 
 218         Map<String, Map<Character, String>> package2Version2Module = new HashMap<>();
 219         Map<String, Set<FileData>> directory2FileData = new TreeMap<>();
 220 


 221         for (ModuleDescription md : data.modules.values()) {
 222             for (ModuleHeaderDescription mhd : md.header) {
 223                 writeModulesForVersions(directory2FileData,
 224                                         md,
 225                                         mhd,
 226                                         mhd.versions,
 227                                         version -> {
 228                                             String versionString = Character.toString(version);



 229                                             int versionNumber = Integer.parseInt(versionString, Character.MAX_RADIX);
 230                                             versionString = Integer.toString(versionNumber);
 231                                             if (versionNumber == currentVersionParsed && !preReleaseTag.isEmpty()) {
 232                                                 versionString = versionString + "-" + preReleaseTag;
 233                                             }
 234                                             return versionString;
 235                                         });
 236                 List<String> packages = new ArrayList<>();
 237                 mhd.exports.stream()
 238                            .map(ExportsDescription::packageName)
 239                            .forEach(packages::add);
 240                 if (mhd.extraModulePackages != null) {
 241                     packages.addAll(mhd.extraModulePackages);
 242                 }
 243                 packages.stream().forEach(pkg -> {
 244                     for (char v : mhd.versions.toCharArray()) {
 245                         package2Version2Module.computeIfAbsent(pkg, dummy -> new HashMap<>()).put(v, md.name);
 246                     }
 247                 });
 248             }

 792     void writeModule(Map<String, Set<FileData>> directory2FileData,
 793                     ModuleDescription moduleDescription,
 794                     ModuleHeaderDescription header,
 795                     char version,
 796                     Function<Character, String> version2ModuleVersion) throws IOException {
 797         var classFile = ClassFile.of().build(ClassDesc.of("module-info"), clb -> {
 798             clb.withFlags(header.flags);
 799             addAttributes(moduleDescription, header, clb, version2ModuleVersion.apply(version));
 800         });
 801 
 802         String versionString = Character.toString(version);
 803         doWrite(directory2FileData, versionString, moduleDescription.name, "module-info" + EXTENSION, classFile);
 804     }
 805 
 806     void writeClass(Map<String, Set<FileData>> directory2FileData,
 807                     ClassDescription classDescription,
 808                     ClassHeaderDescription header,
 809                     String module,
 810                     String version) throws IOException {
 811         var classFile = ClassFile.of().build(ClassDesc.ofInternalName(classDescription.name), clb -> {



 812             if (header.extendsAttr != null)
 813                 clb.withSuperclass(ClassDesc.ofInternalName(header.extendsAttr));
 814             clb.withInterfaceSymbols(header.implementsAttr.stream().map(ClassDesc::ofInternalName).collect(Collectors.toList()))
 815                     .withFlags(header.flags);
 816             for (FieldDescription fieldDesc : classDescription.fields) {
 817                 if (disjoint(fieldDesc.versions, version))
 818                     continue;
 819                 clb.withField(fieldDesc.name, ClassDesc.ofDescriptor(fieldDesc.descriptor), fb -> {
 820                     addAttributes(fieldDesc, fb);
 821                     fb.withFlags(fieldDesc.flags);
 822                 });
 823             }
 824             for (MethodDescription methDesc : classDescription.methods) {
 825                 if (disjoint(methDesc.versions, version))
 826                     continue;
 827                 clb.withMethod(methDesc.name, MethodTypeDesc.ofDescriptor(methDesc.descriptor), methDesc.flags, mb -> addAttributes(methDesc, mb));
 828             }
 829             addAttributes(header, clb);
 830         });
 831         doWrite(directory2FileData, version, module, classDescription.name + EXTENSION, classFile);

1288         return module.exports.stream().filter(ed -> ed.packageName().equals(pack)).findAny().isPresent() ||
1289                module.extraModulePackages.contains(pack);
1290     }
1291 
1292     private void loadVersionClassesFromDirectory(ClassList classes,
1293                                     Map<String, ModuleDescription> modules,
1294                                     Path modulesDirectory,
1295                                     Set<String> includedModules,
1296                                     String version,
1297                                     String baseline) {
1298         Map<String, ModuleDescription> currentVersionModules =
1299                 new HashMap<>();
1300         ClassList currentVersionClasses = new ClassList();
1301         Set<String> privateIncludes = new HashSet<>();
1302         Set<String> includes = new HashSet<>();
1303         ExcludeIncludeList currentEIList = new ExcludeIncludeList(includes,
1304                 privateIncludes,
1305                 Collections.emptySet());
1306 
1307         try {

1308             Map<Path, ModuleHeaderDescription> modulePath2Header = new HashMap<>();
1309             List<Path> pendingExportedDirectories = new ArrayList<>();
1310 
1311             try (DirectoryStream<Path> ds = Files.newDirectoryStream(modulesDirectory)) {
1312                 for (Path p : ds) {
1313                     if (!includedModules.contains(p.getFileName().toString())) {
1314                         continue;
1315                     }
1316 
1317                     Path moduleInfo = p.resolve("module-info.class");
1318 
1319                     if (Files.isReadable(moduleInfo)) {
1320                         ModuleDescription md = inspectModuleInfoClassFile(Files.readAllBytes(moduleInfo),
1321                                     currentVersionModules, version);
1322                         if (md == null) {
1323                             continue;
1324                         }
1325 
1326                         modulePath2Header.put(p, md.header.get(0));
1327 
1328                         Set<String> currentModuleExports =
1329                                 md.header.get(0).exports.stream()
1330                                                         .filter(e -> !e.isQualified())
1331                                                         .map(e -> e.packageName + '/')
1332                                                         .collect(Collectors.toSet());
1333 
1334                         for (String dir : currentModuleExports) {
1335                             includes.add(dir);
1336                             pendingExportedDirectories.add(p.resolve(dir));
1337                         }
1338                     } else {
1339                         throw new IllegalArgumentException("Included module: " +
1340                                                            p.getFileName() +
1341                                                            " does not have a module-info.class");
1342                     }
1343                 }
1344             }
1345 
1346             List<String> pendingExtraClasses = new ArrayList<>();
1347 
1348             for (Path exported : pendingExportedDirectories) {
1349                 try (DirectoryStream<Path> ds = Files.newDirectoryStream(exported)) {
1350                     for (Path p2 : ds) {
1351                         if (!Files.isRegularFile(p2) || !p2.getFileName().toString().endsWith(".class")) {
1352                             continue;
1353                         }
1354 


1355                         loadFromDirectoryHandleClassFile(p2, currentVersionClasses,
1356                                                          currentEIList, version,
1357                                                          pendingExtraClasses);
1358                     }
1359                 }
1360             }
1361 
1362             while (!pendingExtraClasses.isEmpty()) {
1363                 String current = pendingExtraClasses.remove(pendingExtraClasses.size() - 1);
1364 
1365                 if (currentVersionClasses.find(current, true) != null) {
1366                     continue;
1367                 }
1368 
1369                 for (Entry<Path, ModuleHeaderDescription> e : modulePath2Header.entrySet()) {
1370                     Path currentPath = e.getKey().resolve(current + ".class");
1371 
1372                     if (Files.isReadable(currentPath)) {

1373                         String pack = current.substring(0, current.lastIndexOf('/'));
1374 
1375                         e.getValue().extraModulePackages.add(pack);
1376 
1377                         loadFromDirectoryHandleClassFile(currentPath, currentVersionClasses,
1378                                                          currentEIList, version,
1379                                                          pendingExtraClasses);
1380                     }
1381                 }
1382             }
1383         } catch (IOException ex) {
1384             throw new IllegalArgumentException(ex);
1385         }
1386 
1387         finishClassLoading(classes, modules, currentVersionModules, currentVersionClasses, currentEIList, version, baseline);
1388     }
1389 
1390     private void loadFromDirectoryHandleClassFile(Path path, ClassList currentVersionClasses,
1391                                                   ExcludeIncludeList currentEIList, String version,
1392                                                   List<String> todo) throws IOException {
1393         try (InputStream in = Files.newInputStream(path)) {
1394             inspectClassFile(in, currentVersionClasses,
1395                              currentEIList, version,
1396                              cf -> {
1397                                  Set<String> superTypes = otherRelevantTypesWithOwners(cf);
1398 
1399                                  currentEIList.privateIncludeList.addAll(superTypes);
1400                                  todo.addAll(superTypes);
1401                              });
1402         }
1403     }
1404 















1405     private void finishClassLoading(ClassList classes, Map<String, ModuleDescription> modules, Map<String, ModuleDescription> currentVersionModules, ClassList currentVersionClasses, ExcludeIncludeList currentEIList, String version,
1406                                     String baseline) {
1407         ModuleDescription unsupported =
1408                 currentVersionModules.get("jdk.unsupported");
1409 
1410         if (unsupported != null) {
1411             for (ClassDescription cd : currentVersionClasses.classes) {
1412                 if (unsupported.header
1413                                .get(0)
1414                                .exports
1415                                .stream()
1416                                .map(ed -> ed.packageName)
1417                                .anyMatch(pack -> pack.equals(cd.packge().replace('.', '/')))) {
1418                     ClassHeaderDescription ch = cd.header.get(0);
1419                     if (ch.classAnnotations == null) {
1420                         ch.classAnnotations = new ArrayList<>();
1421                     }
1422                     AnnotationDescription ad;
1423                     ad = new AnnotationDescription(PROPERITARY_ANNOTATION,
1424                                                    Collections.emptyMap());

1913         inspectClassFile(in, classes, excludesIncludes, version, cf -> {});
1914     }
1915 
1916     private void inspectClassFile(InputStream in, ClassList classes, ExcludeIncludeList excludesIncludes, String version,
1917                                   Consumer<ClassModel> extraTask) throws IOException {
1918         ClassModel cm = ClassFile.of().parse(in.readAllBytes());
1919 
1920         if (cm.isModuleInfo()) {
1921             return ;
1922         }
1923 
1924         if (!excludesIncludes.accepts(cm.thisClass().asInternalName(), true)) {
1925             return ;
1926         }
1927 
1928         extraTask.accept(cm);
1929 
1930         ClassHeaderDescription headerDesc = new ClassHeaderDescription();
1931 
1932         headerDesc.flags = cm.flags().flagsMask();

1933 
1934         if (cm.superclass().isPresent()) {
1935             headerDesc.extendsAttr = cm.superclass().get().asInternalName();
1936         }
1937         headerDesc.implementsAttr = cm.interfaces().stream().map(ClassEntry::asInternalName).collect(Collectors.toList());
1938         for (var attr : cm.attributes()) {
1939             if (!readAttribute(headerDesc, attr))
1940                 return ;
1941         }
1942 
1943         ClassDescription clazzDesc = null;
1944 
1945         for (ClassDescription cd : classes) {
1946             if (cd.name.equals(cm.thisClass().asInternalName())) {
1947                 clazzDesc = cd;
1948                 break;
1949             }
1950         }
1951 
1952         if (clazzDesc == null) {

1979             for (var attr : f.attributes()) {
1980                 readAttribute(fieldDesc, attr);
1981             }
1982             addField(clazzDesc, fieldDesc, version, null);
1983         }
1984     }
1985 
1986     private ModuleDescription inspectModuleInfoClassFile(byte[] data,
1987             Map<String, ModuleDescription> modules,
1988             String version) {
1989         ClassModel cm = ClassFile.of().parse(data);
1990 
1991         if (!cm.flags().has(AccessFlag.MODULE)) {
1992             return null;
1993         }
1994 
1995         ModuleHeaderDescription headerDesc = new ModuleHeaderDescription();
1996 
1997         headerDesc.versions = version;
1998         headerDesc.flags = cm.flags().flagsMask();

1999 
2000         for (var attr : cm.attributes()) {
2001             if (!readAttribute(headerDesc, attr))
2002                 return null;
2003         }
2004 
2005         String name = headerDesc.name;
2006 
2007         ModuleDescription moduleDesc = modules.get(name);
2008 
2009         if (moduleDesc == null) {
2010             moduleDesc = new ModuleDescription();
2011             moduleDesc.name = name;
2012             modules.put(moduleDesc.name, moduleDesc);
2013         }
2014 
2015         addModuleHeader(moduleDesc, headerDesc, version);
2016 
2017         return moduleDesc;
2018     }

2246                     var rcd = new RecordComponentDescription();
2247                     rcd.name = rci.name().stringValue();
2248                     rcd.descriptor = rci.descriptor().stringValue();
2249                     rci.attributes().forEach(child -> readAttribute(rcd, child));
2250                     return rcd;
2251                 }).collect(Collectors.toList());
2252             }
2253             case MethodParametersAttribute a -> ((MethodDescription) feature).methodParameters = a.parameters().stream()
2254                     .map(mpi -> new MethodDescription.MethodParam(mpi.flagsMask(), mpi.name().map(Utf8Entry::stringValue).orElse(null)))
2255                     .collect(Collectors.toList());
2256             case PermittedSubclassesAttribute a -> {
2257                 var chd = (ClassHeaderDescription) feature;
2258                 chd.isSealed = true;
2259                 chd.permittedSubclasses = a.permittedSubclasses().stream().map(ClassEntry::asInternalName).collect(Collectors.toList());
2260             }
2261             case ModuleMainClassAttribute a -> ((ModuleHeaderDescription) feature).moduleMainClass = a.mainClass().asInternalName();
2262             case RuntimeInvisibleTypeAnnotationsAttribute a ->
2263                 feature.classTypeAnnotations = typeAnnotations2Descriptions(a.annotations());
2264             case RuntimeVisibleTypeAnnotationsAttribute a ->
2265                 feature.runtimeTypeAnnotations = typeAnnotations2Descriptions(a.annotations());
2266             default -> throw new IllegalArgumentException("Unhandled attribute: " + attr.attributeName()); // Do nothing






2267         }
2268 
2269         return true;
2270     }
2271 
2272     public static String INJECTED_VERSION = null;
2273 
2274     private static String getVersion(Optional<Utf8Entry> version) {
2275         if (INJECTED_VERSION != null) {
2276             return INJECTED_VERSION;
2277         }
2278         return version.map(Utf8Entry::stringValue).orElse(null);
2279     }
2280 
2281     Object convertConstantValue(ConstantValueEntry info, String descriptor) {
2282         if (descriptor.length() == 1 && info instanceof IntegerEntry ie) {
2283             var i = ie.intValue();
2284             return switch (descriptor.charAt(0)) {
2285                 case 'I', 'B', 'S' -> i;
2286                 case 'C' -> (char) i;

3291             if (recordComponents != null) {
3292                 for (RecordComponentDescription rcd : recordComponents) {
3293                     rcd.write(output, "", "");
3294                 }
3295             }
3296         }
3297 
3298         protected void readRecordComponents(LineBasedReader reader) throws IOException {
3299             recordComponents = new ArrayList<>();
3300 
3301             while ("recordcomponent".equals(reader.lineKey)) {
3302                 RecordComponentDescription rcd = new RecordComponentDescription();
3303                 rcd.read(reader);
3304                 recordComponents.add(rcd);
3305             }
3306         }
3307     }
3308 
3309     static abstract class HeaderDescription extends FeatureDescription {
3310         List<InnerClassInfo> innerClasses;

3311 
3312         @Override
3313         public int hashCode() {
3314             int hash = super.hashCode();
3315             hash = 19 * hash + Objects.hashCode(this.innerClasses);

3316             return hash;
3317         }
3318 
3319         @Override
3320         public boolean equals(Object obj) {
3321             if (obj == null) {
3322                 return false;
3323             }
3324             if (!super.equals(obj)) {
3325                 return false;
3326             }
3327             final HeaderDescription other = (HeaderDescription) obj;
3328             if (!listEquals(this.innerClasses, other.innerClasses)) {
3329                 return false;
3330             }



3331             return true;
3332         }
3333 
3334         protected void writeInnerClasses(Appendable output,
3335                                          String baselineVersion,
3336                                          String version) throws IOException {
3337             if (innerClasses != null && !innerClasses.isEmpty()) {
3338                 for (InnerClassInfo ici : innerClasses) {
3339                     output.append("innerclass");
3340                     output.append(" innerClass " + ici.innerClass);
3341                     output.append(" outerClass " + ici.outerClass);
3342                     output.append(" innerClassName " + ici.innerClassName);
3343                     output.append(" flags " + Integer.toHexString(ici.innerClassFlags));
3344                     output.append("\n");
3345                 }
3346             }
3347         }
3348 
3349         protected void readInnerClasses(LineBasedReader reader) throws IOException {
3350             innerClasses = new ArrayList<>();
3351 
3352             while ("innerclass".equals(reader.lineKey)) {
3353                 InnerClassInfo info = new InnerClassInfo();
3354 
3355                 info.innerClass = reader.attributes.get("innerClass");
3356                 info.outerClass = reader.attributes.get("outerClass");
3357                 info.innerClassName = reader.attributes.get("innerClassName");
3358 
3359                 String inFlags = reader.attributes.get("flags");
3360                 if (inFlags != null && !inFlags.isEmpty())
3361                     info.innerClassFlags = Integer.parseInt(inFlags, 16);
3362 
3363                 innerClasses.add(info);
3364 
3365                 reader.moveNext();
3366             }
3367         }
3368 
















3369     }
3370 
3371     static class MethodDescription extends FeatureDescription {
3372         static int METHODS_FLAGS_NORMALIZATION = ~0;
3373         String name;
3374         String descriptor;
3375         List<String> thrownTypes;
3376         Object annotationDefaultValue;
3377         List<List<AnnotationDescription>> classParameterAnnotations;
3378         List<List<AnnotationDescription>> runtimeParameterAnnotations;
3379         List<MethodParam> methodParameters;
3380 
3381         public MethodDescription() {
3382             flagsNormalization = METHODS_FLAGS_NORMALIZATION;
3383         }
3384 
3385         @Override
3386         public int hashCode() {
3387             int hash = super.hashCode();
3388             hash = 59 * hash + Objects.hashCode(this.name);

 166  * that is specified should use literal value "<none>", to have all the APIs of the platform written to
 167  * the .sym.txt files. If there is an existing platform with full .sym.txt files in the repository,
 168  * that platform should be used as the first platform to avoid unnecessary changes to the .sym.txt
 169  * files. The <diff-against-platformN> for platform N should be determined as follows: if N < F, then
 170  * <diff-against-platformN> should be N + 1. If F < N, then <diff-against-platformN> should be N - 1.
 171  * If N is a custom/specialized sub-version of another platform N', then <diff-against-platformN> should be N'.
 172  *
 173  * To generate the .sym.txt files for OpenJDK 7 and 8:
 174  *     <jdk-7>/bin/java build.tools.symbolgenerator.Probe OpenJDK7.classes
 175  *     <jdk-8>/bin/java build.tools.symbolgenerator.Probe OpenJDK8.classes
 176  *     java build.tools.symbolgenerator.CreateSymbols build-description src/jdk.compiler/share/data/symbols
 177  *          $TOPDIR src/jdk.compiler/share/data/symbols/include.list
 178  *                                                    8 OpenJDK8.classes '<none>'
 179  *                                                    7 OpenJDK7.classes 8
 180  *
 181  * Note: the versions are expected to be a single character.
 182  *
 183  */
 184 public class CreateSymbols {
 185 
 186     /**
 187      * <p>Support for a "preview version" of classfiles when running with preview
 188      * mode. This is modeled as a new version (@) and since preview mode is only
 189      * supported for the current version, a single identifier token is sufficient.
 190      *
 191      * <p>For example, inside ct.sym, 27 will be modeled as 'R', and the preview
 192      * for 27 will be '@'. Classfiles unchanged between 27 and 27-preview will
 193      * not be duplicated (in the same way classfiles that are common between 26
 194      * and 27 are shared).
 195      */
 196     private static final String PREVIEW_VERSION = "@";
 197 
 198     //<editor-fold defaultstate="collapsed" desc="ct.sym construction">
 199     /**Create sig files for ct.sym reading the classes description from the directory that contains
 200      * {@code ctDescriptionFile}, using the file as a recipe to create the sigfiles.
 201      */
 202     public void createSymbols(String ctDescriptionFileExtra, String ctDescriptionFile, String ctSymLocation,
 203                               long timestamp, String currentVersion, String preReleaseTag, String moduleClasses,
 204                               String includedModulesFile) throws IOException {
 205         LoadDescriptions data = load(ctDescriptionFileExtra != null ? Paths.get(ctDescriptionFileExtra)
 206                                                                     : null,
 207                                      Paths.get(ctDescriptionFile));
 208 
 209         int currentVersionParsed = Integer.parseInt(currentVersion);
 210 
 211         currentVersion = Integer.toString(currentVersionParsed, Character.MAX_RADIX);
 212         currentVersion = currentVersion.toUpperCase(Locale.ROOT);
 213 
 214         String previousVersion = Integer.toString(currentVersionParsed - 1, Character.MAX_RADIX);
 215 
 216         previousVersion = previousVersion.toUpperCase(Locale.ROOT);
 217 
 218         //load current version classes:
 219         Path moduleClassPath = Paths.get(moduleClasses);
 220         Set<String> includedModules = Files.lines(Paths.get(includedModulesFile))
 221                                            .flatMap(l -> Arrays.stream(l.split(" ")))
 222                                            .collect(Collectors.toSet());
 223 
 224         loadVersionClassesFromDirectory(data.classes, data.modules, moduleClassPath,
 225                                         includedModules, currentVersion, previousVersion);
 226 
 227         loadVersionClassesFromDirectory(data.classes, data.modules, moduleClassPath,
 228                 includedModules, PREVIEW_VERSION, currentVersion);
 229 
 230         stripNonExistentAnnotations(data);
 231         splitHeaders(data.classes);
 232 
 233         Map<String, Map<Character, String>> package2Version2Module = new HashMap<>();
 234         Map<String, Set<FileData>> directory2FileData = new TreeMap<>();
 235 
 236         String currentVersionFin = currentVersion;
 237 
 238         for (ModuleDescription md : data.modules.values()) {
 239             for (ModuleHeaderDescription mhd : md.header) {
 240                 writeModulesForVersions(directory2FileData,
 241                                         md,
 242                                         mhd,
 243                                         mhd.versions,
 244                                         version -> {
 245                                             String versionString = Character.toString(version);
 246                                             if (PREVIEW_VERSION.equals(versionString)) {
 247                                                 versionString = currentVersionFin;
 248                                             }
 249                                             int versionNumber = Integer.parseInt(versionString, Character.MAX_RADIX);
 250                                             versionString = Integer.toString(versionNumber);
 251                                             if (versionNumber == currentVersionParsed && !preReleaseTag.isEmpty()) {
 252                                                 versionString = versionString + "-" + preReleaseTag;
 253                                             }
 254                                             return versionString;
 255                                         });
 256                 List<String> packages = new ArrayList<>();
 257                 mhd.exports.stream()
 258                            .map(ExportsDescription::packageName)
 259                            .forEach(packages::add);
 260                 if (mhd.extraModulePackages != null) {
 261                     packages.addAll(mhd.extraModulePackages);
 262                 }
 263                 packages.stream().forEach(pkg -> {
 264                     for (char v : mhd.versions.toCharArray()) {
 265                         package2Version2Module.computeIfAbsent(pkg, dummy -> new HashMap<>()).put(v, md.name);
 266                     }
 267                 });
 268             }

 812     void writeModule(Map<String, Set<FileData>> directory2FileData,
 813                     ModuleDescription moduleDescription,
 814                     ModuleHeaderDescription header,
 815                     char version,
 816                     Function<Character, String> version2ModuleVersion) throws IOException {
 817         var classFile = ClassFile.of().build(ClassDesc.of("module-info"), clb -> {
 818             clb.withFlags(header.flags);
 819             addAttributes(moduleDescription, header, clb, version2ModuleVersion.apply(version));
 820         });
 821 
 822         String versionString = Character.toString(version);
 823         doWrite(directory2FileData, versionString, moduleDescription.name, "module-info" + EXTENSION, classFile);
 824     }
 825 
 826     void writeClass(Map<String, Set<FileData>> directory2FileData,
 827                     ClassDescription classDescription,
 828                     ClassHeaderDescription header,
 829                     String module,
 830                     String version) throws IOException {
 831         var classFile = ClassFile.of().build(ClassDesc.ofInternalName(classDescription.name), clb -> {
 832             if (header.preview) {
 833                 clb.withVersion(ClassFile.latestMajorVersion(), ClassFile.PREVIEW_MINOR_VERSION);
 834             }
 835             if (header.extendsAttr != null)
 836                 clb.withSuperclass(ClassDesc.ofInternalName(header.extendsAttr));
 837             clb.withInterfaceSymbols(header.implementsAttr.stream().map(ClassDesc::ofInternalName).collect(Collectors.toList()))
 838                     .withFlags(header.flags);
 839             for (FieldDescription fieldDesc : classDescription.fields) {
 840                 if (disjoint(fieldDesc.versions, version))
 841                     continue;
 842                 clb.withField(fieldDesc.name, ClassDesc.ofDescriptor(fieldDesc.descriptor), fb -> {
 843                     addAttributes(fieldDesc, fb);
 844                     fb.withFlags(fieldDesc.flags);
 845                 });
 846             }
 847             for (MethodDescription methDesc : classDescription.methods) {
 848                 if (disjoint(methDesc.versions, version))
 849                     continue;
 850                 clb.withMethod(methDesc.name, MethodTypeDesc.ofDescriptor(methDesc.descriptor), methDesc.flags, mb -> addAttributes(methDesc, mb));
 851             }
 852             addAttributes(header, clb);
 853         });
 854         doWrite(directory2FileData, version, module, classDescription.name + EXTENSION, classFile);

1311         return module.exports.stream().filter(ed -> ed.packageName().equals(pack)).findAny().isPresent() ||
1312                module.extraModulePackages.contains(pack);
1313     }
1314 
1315     private void loadVersionClassesFromDirectory(ClassList classes,
1316                                     Map<String, ModuleDescription> modules,
1317                                     Path modulesDirectory,
1318                                     Set<String> includedModules,
1319                                     String version,
1320                                     String baseline) {
1321         Map<String, ModuleDescription> currentVersionModules =
1322                 new HashMap<>();
1323         ClassList currentVersionClasses = new ClassList();
1324         Set<String> privateIncludes = new HashSet<>();
1325         Set<String> includes = new HashSet<>();
1326         ExcludeIncludeList currentEIList = new ExcludeIncludeList(includes,
1327                 privateIncludes,
1328                 Collections.emptySet());
1329 
1330         try {
1331             record ExportedDir(Path modulePath, Path exportedDir) {}
1332             Map<Path, ModuleHeaderDescription> modulePath2Header = new HashMap<>();
1333             List<ExportedDir> pendingExportedDirectories = new ArrayList<>();
1334 
1335             try (DirectoryStream<Path> ds = Files.newDirectoryStream(modulesDirectory)) {
1336                 for (Path p : ds) {
1337                     if (!includedModules.contains(p.getFileName().toString())) {
1338                         continue;
1339                     }
1340 
1341                     Path moduleInfo = resolvePossiblyPreviewClassFile(version, p, p.resolve("module-info.class"));
1342 
1343                     if (Files.isReadable(moduleInfo)) {
1344                         ModuleDescription md = inspectModuleInfoClassFile(Files.readAllBytes(moduleInfo),
1345                                     currentVersionModules, version);
1346                         if (md == null) {
1347                             continue;
1348                         }
1349 
1350                         modulePath2Header.put(p, md.header.get(0));
1351 
1352                         Set<String> currentModuleExports =
1353                                 md.header.get(0).exports.stream()
1354                                                         .filter(e -> !e.isQualified())
1355                                                         .map(e -> e.packageName + '/')
1356                                                         .collect(Collectors.toSet());
1357 
1358                         for (String dir : currentModuleExports) {
1359                             includes.add(dir);
1360                             pendingExportedDirectories.add(new ExportedDir(p, p.resolve(dir)));
1361                         }
1362                     } else {
1363                         throw new IllegalArgumentException("Included module: " +
1364                                                            p.getFileName() +
1365                                                            " does not have a module-info.class");
1366                     }
1367                 }
1368             }
1369 
1370             List<String> pendingExtraClasses = new ArrayList<>();
1371 
1372             for (ExportedDir exported : pendingExportedDirectories) {
1373                 try (DirectoryStream<Path> ds = Files.newDirectoryStream(exported.exportedDir())) {
1374                     for (Path p2 : ds) {
1375                         if (!Files.isRegularFile(p2) || !p2.getFileName().toString().endsWith(".class")) {
1376                             continue;
1377                         }
1378 
1379                         p2 = resolvePossiblyPreviewClassFile(version, exported.modulePath(), p2);
1380 
1381                         loadFromDirectoryHandleClassFile(p2, currentVersionClasses,
1382                                                          currentEIList, version,
1383                                                          pendingExtraClasses);
1384                     }
1385                 }
1386             }
1387 
1388             while (!pendingExtraClasses.isEmpty()) {
1389                 String current = pendingExtraClasses.remove(pendingExtraClasses.size() - 1);
1390 
1391                 if (currentVersionClasses.find(current, true) != null) {
1392                     continue;
1393                 }
1394 
1395                 for (Entry<Path, ModuleHeaderDescription> e : modulePath2Header.entrySet()) {
1396                     Path currentPath = e.getKey().resolve(current + ".class");
1397 
1398                     if (Files.isReadable(currentPath)) {
1399                         currentPath = resolvePossiblyPreviewClassFile(version, e.getKey(), currentPath);
1400                         String pack = current.substring(0, current.lastIndexOf('/'));
1401 
1402                         e.getValue().extraModulePackages.add(pack);
1403 
1404                         loadFromDirectoryHandleClassFile(currentPath, currentVersionClasses,
1405                                                          currentEIList, version,
1406                                                          pendingExtraClasses);
1407                     }
1408                 }
1409             }
1410         } catch (IOException ex) {
1411             throw new IllegalArgumentException(ex);
1412         }
1413 
1414         finishClassLoading(classes, modules, currentVersionModules, currentVersionClasses, currentEIList, version, baseline);
1415     }
1416 
1417     private void loadFromDirectoryHandleClassFile(Path path, ClassList currentVersionClasses,
1418                                                   ExcludeIncludeList currentEIList, String version,
1419                                                   List<String> todo) throws IOException {
1420         try (InputStream in = Files.newInputStream(path)) {
1421             inspectClassFile(in, currentVersionClasses,
1422                              currentEIList, version,
1423                              cf -> {
1424                                  Set<String> superTypes = otherRelevantTypesWithOwners(cf);
1425 
1426                                  currentEIList.privateIncludeList.addAll(superTypes);
1427                                  todo.addAll(superTypes);
1428                              });
1429         }
1430     }
1431 
1432     private Path resolvePossiblyPreviewClassFile(String version, Path moduleClassDir, Path classfile) {
1433         if (!PREVIEW_VERSION.equals(version)) {
1434             return classfile;
1435         }
1436 
1437         Path relativePath = moduleClassDir.relativize(classfile);
1438         Path previewCandidate = moduleClassDir.resolve("META-INF").resolve("preview").resolve(relativePath);
1439 
1440         if (Files.exists(previewCandidate)) {
1441             return previewCandidate;
1442         }
1443 
1444         return classfile;
1445     }
1446 
1447     private void finishClassLoading(ClassList classes, Map<String, ModuleDescription> modules, Map<String, ModuleDescription> currentVersionModules, ClassList currentVersionClasses, ExcludeIncludeList currentEIList, String version,
1448                                     String baseline) {
1449         ModuleDescription unsupported =
1450                 currentVersionModules.get("jdk.unsupported");
1451 
1452         if (unsupported != null) {
1453             for (ClassDescription cd : currentVersionClasses.classes) {
1454                 if (unsupported.header
1455                                .get(0)
1456                                .exports
1457                                .stream()
1458                                .map(ed -> ed.packageName)
1459                                .anyMatch(pack -> pack.equals(cd.packge().replace('.', '/')))) {
1460                     ClassHeaderDescription ch = cd.header.get(0);
1461                     if (ch.classAnnotations == null) {
1462                         ch.classAnnotations = new ArrayList<>();
1463                     }
1464                     AnnotationDescription ad;
1465                     ad = new AnnotationDescription(PROPERITARY_ANNOTATION,
1466                                                    Collections.emptyMap());

1955         inspectClassFile(in, classes, excludesIncludes, version, cf -> {});
1956     }
1957 
1958     private void inspectClassFile(InputStream in, ClassList classes, ExcludeIncludeList excludesIncludes, String version,
1959                                   Consumer<ClassModel> extraTask) throws IOException {
1960         ClassModel cm = ClassFile.of().parse(in.readAllBytes());
1961 
1962         if (cm.isModuleInfo()) {
1963             return ;
1964         }
1965 
1966         if (!excludesIncludes.accepts(cm.thisClass().asInternalName(), true)) {
1967             return ;
1968         }
1969 
1970         extraTask.accept(cm);
1971 
1972         ClassHeaderDescription headerDesc = new ClassHeaderDescription();
1973 
1974         headerDesc.flags = cm.flags().flagsMask();
1975         headerDesc.preview = cm.minorVersion() == ClassFile.PREVIEW_MINOR_VERSION;
1976 
1977         if (cm.superclass().isPresent()) {
1978             headerDesc.extendsAttr = cm.superclass().get().asInternalName();
1979         }
1980         headerDesc.implementsAttr = cm.interfaces().stream().map(ClassEntry::asInternalName).collect(Collectors.toList());
1981         for (var attr : cm.attributes()) {
1982             if (!readAttribute(headerDesc, attr))
1983                 return ;
1984         }
1985 
1986         ClassDescription clazzDesc = null;
1987 
1988         for (ClassDescription cd : classes) {
1989             if (cd.name.equals(cm.thisClass().asInternalName())) {
1990                 clazzDesc = cd;
1991                 break;
1992             }
1993         }
1994 
1995         if (clazzDesc == null) {

2022             for (var attr : f.attributes()) {
2023                 readAttribute(fieldDesc, attr);
2024             }
2025             addField(clazzDesc, fieldDesc, version, null);
2026         }
2027     }
2028 
2029     private ModuleDescription inspectModuleInfoClassFile(byte[] data,
2030             Map<String, ModuleDescription> modules,
2031             String version) {
2032         ClassModel cm = ClassFile.of().parse(data);
2033 
2034         if (!cm.flags().has(AccessFlag.MODULE)) {
2035             return null;
2036         }
2037 
2038         ModuleHeaderDescription headerDesc = new ModuleHeaderDescription();
2039 
2040         headerDesc.versions = version;
2041         headerDesc.flags = cm.flags().flagsMask();
2042         headerDesc.preview = cm.minorVersion() == ClassFile.PREVIEW_MINOR_VERSION;
2043 
2044         for (var attr : cm.attributes()) {
2045             if (!readAttribute(headerDesc, attr))
2046                 return null;
2047         }
2048 
2049         String name = headerDesc.name;
2050 
2051         ModuleDescription moduleDesc = modules.get(name);
2052 
2053         if (moduleDesc == null) {
2054             moduleDesc = new ModuleDescription();
2055             moduleDesc.name = name;
2056             modules.put(moduleDesc.name, moduleDesc);
2057         }
2058 
2059         addModuleHeader(moduleDesc, headerDesc, version);
2060 
2061         return moduleDesc;
2062     }

2290                     var rcd = new RecordComponentDescription();
2291                     rcd.name = rci.name().stringValue();
2292                     rcd.descriptor = rci.descriptor().stringValue();
2293                     rci.attributes().forEach(child -> readAttribute(rcd, child));
2294                     return rcd;
2295                 }).collect(Collectors.toList());
2296             }
2297             case MethodParametersAttribute a -> ((MethodDescription) feature).methodParameters = a.parameters().stream()
2298                     .map(mpi -> new MethodDescription.MethodParam(mpi.flagsMask(), mpi.name().map(Utf8Entry::stringValue).orElse(null)))
2299                     .collect(Collectors.toList());
2300             case PermittedSubclassesAttribute a -> {
2301                 var chd = (ClassHeaderDescription) feature;
2302                 chd.isSealed = true;
2303                 chd.permittedSubclasses = a.permittedSubclasses().stream().map(ClassEntry::asInternalName).collect(Collectors.toList());
2304             }
2305             case ModuleMainClassAttribute a -> ((ModuleHeaderDescription) feature).moduleMainClass = a.mainClass().asInternalName();
2306             case RuntimeInvisibleTypeAnnotationsAttribute a ->
2307                 feature.classTypeAnnotations = typeAnnotations2Descriptions(a.annotations());
2308             case RuntimeVisibleTypeAnnotationsAttribute a ->
2309                 feature.runtimeTypeAnnotations = typeAnnotations2Descriptions(a.annotations());
2310             default -> {
2311                 if (attr.attributeName().equalsString("LoadableDescriptors")) {
2312                     //OK, do nothing
2313                 } else {
2314                     throw new IllegalArgumentException("Unhandled attribute: " + attr.attributeName());
2315                 }
2316             }
2317         }
2318 
2319         return true;
2320     }
2321 
2322     public static String INJECTED_VERSION = null;
2323 
2324     private static String getVersion(Optional<Utf8Entry> version) {
2325         if (INJECTED_VERSION != null) {
2326             return INJECTED_VERSION;
2327         }
2328         return version.map(Utf8Entry::stringValue).orElse(null);
2329     }
2330 
2331     Object convertConstantValue(ConstantValueEntry info, String descriptor) {
2332         if (descriptor.length() == 1 && info instanceof IntegerEntry ie) {
2333             var i = ie.intValue();
2334             return switch (descriptor.charAt(0)) {
2335                 case 'I', 'B', 'S' -> i;
2336                 case 'C' -> (char) i;

3341             if (recordComponents != null) {
3342                 for (RecordComponentDescription rcd : recordComponents) {
3343                     rcd.write(output, "", "");
3344                 }
3345             }
3346         }
3347 
3348         protected void readRecordComponents(LineBasedReader reader) throws IOException {
3349             recordComponents = new ArrayList<>();
3350 
3351             while ("recordcomponent".equals(reader.lineKey)) {
3352                 RecordComponentDescription rcd = new RecordComponentDescription();
3353                 rcd.read(reader);
3354                 recordComponents.add(rcd);
3355             }
3356         }
3357     }
3358 
3359     static abstract class HeaderDescription extends FeatureDescription {
3360         List<InnerClassInfo> innerClasses;
3361         boolean preview;
3362 
3363         @Override
3364         public int hashCode() {
3365             int hash = super.hashCode();
3366             hash = 19 * hash + Objects.hashCode(this.innerClasses);
3367             hash = 19 * hash + Objects.hashCode(this.preview);
3368             return hash;
3369         }
3370 
3371         @Override
3372         public boolean equals(Object obj) {
3373             if (obj == null) {
3374                 return false;
3375             }
3376             if (!super.equals(obj)) {
3377                 return false;
3378             }
3379             final HeaderDescription other = (HeaderDescription) obj;
3380             if (!listEquals(this.innerClasses, other.innerClasses)) {
3381                 return false;
3382             }
3383             if (this.preview != other.preview) {
3384                 return false;
3385             }
3386             return true;
3387         }
3388 
3389         protected void writeInnerClasses(Appendable output,
3390                                          String baselineVersion,
3391                                          String version) throws IOException {
3392             if (innerClasses != null && !innerClasses.isEmpty()) {
3393                 for (InnerClassInfo ici : innerClasses) {
3394                     output.append("innerclass");
3395                     output.append(" innerClass " + ici.innerClass);
3396                     output.append(" outerClass " + ici.outerClass);
3397                     output.append(" innerClassName " + ici.innerClassName);
3398                     output.append(" flags " + Integer.toHexString(ici.innerClassFlags));
3399                     output.append("\n");
3400                 }
3401             }
3402         }
3403 
3404         protected void readInnerClasses(LineBasedReader reader) throws IOException {
3405             innerClasses = new ArrayList<>();
3406 
3407             while ("innerclass".equals(reader.lineKey)) {
3408                 InnerClassInfo info = new InnerClassInfo();
3409 
3410                 info.innerClass = reader.attributes.get("innerClass");
3411                 info.outerClass = reader.attributes.get("outerClass");
3412                 info.innerClassName = reader.attributes.get("innerClassName");
3413 
3414                 String inFlags = reader.attributes.get("flags");
3415                 if (inFlags != null && !inFlags.isEmpty())
3416                     info.innerClassFlags = Integer.parseInt(inFlags, 16);
3417 
3418                 innerClasses.add(info);
3419 
3420                 reader.moveNext();
3421             }
3422         }
3423 
3424         @Override
3425         protected void writeAttributes(Appendable output) throws IOException {
3426             super.writeAttributes(output);
3427             if (preview) {
3428                 output.append(" preview true");
3429             }
3430         }
3431 
3432         @Override
3433         protected void readAttributes(LineBasedReader reader) {
3434             super.readAttributes(reader);
3435             String inPreview = reader.attributes.get("preview");
3436             if ("true".equals(inPreview)) {
3437                 preview = true;
3438             }
3439         }
3440     }
3441 
3442     static class MethodDescription extends FeatureDescription {
3443         static int METHODS_FLAGS_NORMALIZATION = ~0;
3444         String name;
3445         String descriptor;
3446         List<String> thrownTypes;
3447         Object annotationDefaultValue;
3448         List<List<AnnotationDescription>> classParameterAnnotations;
3449         List<List<AnnotationDescription>> runtimeParameterAnnotations;
3450         List<MethodParam> methodParameters;
3451 
3452         public MethodDescription() {
3453             flagsNormalization = METHODS_FLAGS_NORMALIZATION;
3454         }
3455 
3456         @Override
3457         public int hashCode() {
3458             int hash = super.hashCode();
3459             hash = 59 * hash + Objects.hashCode(this.name);
< prev index next >