< 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             }

 289                     jos.write(fd.fileData);
 290                 }
 291             }
 292         }
 293     }
 294 
 295     private static final String PREVIEW_FEATURE_ANNOTATION_OLD =
 296             "Ljdk/internal/PreviewFeature;";
 297     private static final String PREVIEW_FEATURE_ANNOTATION_NEW =
 298             "Ljdk/internal/javac/PreviewFeature;";
 299     private static final String PREVIEW_FEATURE_ANNOTATION_INTERNAL =
 300             "Ljdk/internal/PreviewFeature+Annotation;";
 301     private static final String RESTRICTED_ANNOTATION =
 302             "Ljdk/internal/javac/Restricted;";
 303     private static final String RESTRICTED_ANNOTATION_INTERNAL =
 304             "Ljdk/internal/javac/Restricted+Annotation;";
 305     private static final String VALUE_BASED_ANNOTATION =
 306             "Ljdk/internal/ValueBased;";
 307     private static final String VALUE_BASED_ANNOTATION_INTERNAL =
 308             "Ljdk/internal/ValueBased+Annotation;";




 309     private static final String REQUIRES_IDENTITY_ANNOTATION =
 310             "Ljdk/internal/RequiresIdentity;";
 311     private static final String REQUIRES_IDENTITY_ANNOTATION_INTERNAL =
 312             "Ljdk/internal/RequiresIdentity+Annotation;";
 313     public static final Set<String> HARDCODED_ANNOTATIONS = new HashSet<>(
 314             List.of("Ljdk/Profile+Annotation;",
 315                     "Lsun/Proprietary+Annotation;",
 316                     PREVIEW_FEATURE_ANNOTATION_OLD,
 317                     PREVIEW_FEATURE_ANNOTATION_NEW,
 318                     VALUE_BASED_ANNOTATION,

 319                     RESTRICTED_ANNOTATION,
 320                     REQUIRES_IDENTITY_ANNOTATION));
 321 
 322     private void stripNonExistentAnnotations(LoadDescriptions data) {
 323         Set<String> allClasses = data.classes.name2Class.keySet();
 324         data.modules.values().forEach(mod -> {
 325             stripNonExistentAnnotations(allClasses, mod.header);
 326         });
 327         data.classes.classes.forEach(clazz -> {
 328             stripNonExistentAnnotations(allClasses, clazz.header);
 329             stripNonExistentAnnotations(allClasses, clazz.fields);
 330             stripNonExistentAnnotations(allClasses, clazz.methods);
 331         });
 332     }
 333 
 334     private void stripNonExistentAnnotations(Set<String> allClasses, Iterable<? extends FeatureDescription> descs) {
 335         descs.forEach(d -> stripNonExistentAnnotations(allClasses, d));
 336     }
 337 
 338     private void stripNonExistentAnnotations(Set<String> allClasses, FeatureDescription d) {

 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);

1017             //the non-public PreviewFeature annotation will not be available in ct.sym,
1018             //replace with purely synthetic javac-internal annotation:
1019             annotationType = PREVIEW_FEATURE_ANNOTATION_INTERNAL;
1020         }
1021 
1022         if (PREVIEW_FEATURE_ANNOTATION_OLD.equals(annotationType)) {
1023             //the non-public PreviewFeature annotation will not be available in ct.sym,
1024             //replace with purely synthetic javac-internal annotation:
1025             annotationType = PREVIEW_FEATURE_ANNOTATION_INTERNAL;
1026             values = new HashMap<>(values);
1027             Boolean essentialAPI = (Boolean) values.remove("essentialAPI");
1028             values.put("reflective", essentialAPI != null && !essentialAPI);
1029         }
1030 
1031         if (VALUE_BASED_ANNOTATION.equals(annotationType)) {
1032             //the non-public ValueBased annotation will not be available in ct.sym,
1033             //replace with purely synthetic javac-internal annotation:
1034             annotationType = VALUE_BASED_ANNOTATION_INTERNAL;
1035         }
1036 






1037         if (REQUIRES_IDENTITY_ANNOTATION.equals(annotationType)) {
1038             //the non-public RequiresIdentity annotation will not be available in ct.sym,
1039             //replace with purely synthetic javac-internal annotation:
1040             annotationType = REQUIRES_IDENTITY_ANNOTATION_INTERNAL;
1041         }
1042 
1043         if (RESTRICTED_ANNOTATION.equals(annotationType)) {
1044             //the non-public Restricted annotation will not be available in ct.sym,
1045             //replace with purely synthetic javac-internal annotation:
1046             annotationType = RESTRICTED_ANNOTATION_INTERNAL;
1047         }
1048 
1049         return Annotation.of(ClassDesc.ofDescriptor(annotationType),
1050                 createElementPairs(values));
1051     }
1052 
1053     private List<AnnotationElement> createElementPairs(Map<String, Object> annotationAttributes) {
1054         return annotationAttributes.entrySet().stream()
1055                 .map(e -> AnnotationElement.of(e.getKey(), createAttributeValue(e.getValue())))
1056                 .collect(Collectors.toList());

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             }

 309                     jos.write(fd.fileData);
 310                 }
 311             }
 312         }
 313     }
 314 
 315     private static final String PREVIEW_FEATURE_ANNOTATION_OLD =
 316             "Ljdk/internal/PreviewFeature;";
 317     private static final String PREVIEW_FEATURE_ANNOTATION_NEW =
 318             "Ljdk/internal/javac/PreviewFeature;";
 319     private static final String PREVIEW_FEATURE_ANNOTATION_INTERNAL =
 320             "Ljdk/internal/PreviewFeature+Annotation;";
 321     private static final String RESTRICTED_ANNOTATION =
 322             "Ljdk/internal/javac/Restricted;";
 323     private static final String RESTRICTED_ANNOTATION_INTERNAL =
 324             "Ljdk/internal/javac/Restricted+Annotation;";
 325     private static final String VALUE_BASED_ANNOTATION =
 326             "Ljdk/internal/ValueBased;";
 327     private static final String VALUE_BASED_ANNOTATION_INTERNAL =
 328             "Ljdk/internal/ValueBased+Annotation;";
 329     private static final String MIGRATED_VALUE_CLASS_ANNOTATION =
 330             "Ljdk/internal/MigratedValueClass;";
 331     private static final String MIGRATED_VALUE_CLASS_ANNOTATION_INTERNAL =
 332             "Ljdk/internal/MigratedValueClass+Annotation;";
 333     private static final String REQUIRES_IDENTITY_ANNOTATION =
 334             "Ljdk/internal/RequiresIdentity;";
 335     private static final String REQUIRES_IDENTITY_ANNOTATION_INTERNAL =
 336             "Ljdk/internal/RequiresIdentity+Annotation;";
 337     public static final Set<String> HARDCODED_ANNOTATIONS = new HashSet<>(
 338             List.of("Ljdk/Profile+Annotation;",
 339                     "Lsun/Proprietary+Annotation;",
 340                     PREVIEW_FEATURE_ANNOTATION_OLD,
 341                     PREVIEW_FEATURE_ANNOTATION_NEW,
 342                     VALUE_BASED_ANNOTATION,
 343                     MIGRATED_VALUE_CLASS_ANNOTATION,
 344                     RESTRICTED_ANNOTATION,
 345                     REQUIRES_IDENTITY_ANNOTATION));
 346 
 347     private void stripNonExistentAnnotations(LoadDescriptions data) {
 348         Set<String> allClasses = data.classes.name2Class.keySet();
 349         data.modules.values().forEach(mod -> {
 350             stripNonExistentAnnotations(allClasses, mod.header);
 351         });
 352         data.classes.classes.forEach(clazz -> {
 353             stripNonExistentAnnotations(allClasses, clazz.header);
 354             stripNonExistentAnnotations(allClasses, clazz.fields);
 355             stripNonExistentAnnotations(allClasses, clazz.methods);
 356         });
 357     }
 358 
 359     private void stripNonExistentAnnotations(Set<String> allClasses, Iterable<? extends FeatureDescription> descs) {
 360         descs.forEach(d -> stripNonExistentAnnotations(allClasses, d));
 361     }
 362 
 363     private void stripNonExistentAnnotations(Set<String> allClasses, FeatureDescription d) {

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

1045             //the non-public PreviewFeature annotation will not be available in ct.sym,
1046             //replace with purely synthetic javac-internal annotation:
1047             annotationType = PREVIEW_FEATURE_ANNOTATION_INTERNAL;
1048         }
1049 
1050         if (PREVIEW_FEATURE_ANNOTATION_OLD.equals(annotationType)) {
1051             //the non-public PreviewFeature annotation will not be available in ct.sym,
1052             //replace with purely synthetic javac-internal annotation:
1053             annotationType = PREVIEW_FEATURE_ANNOTATION_INTERNAL;
1054             values = new HashMap<>(values);
1055             Boolean essentialAPI = (Boolean) values.remove("essentialAPI");
1056             values.put("reflective", essentialAPI != null && !essentialAPI);
1057         }
1058 
1059         if (VALUE_BASED_ANNOTATION.equals(annotationType)) {
1060             //the non-public ValueBased annotation will not be available in ct.sym,
1061             //replace with purely synthetic javac-internal annotation:
1062             annotationType = VALUE_BASED_ANNOTATION_INTERNAL;
1063         }
1064 
1065         if (MIGRATED_VALUE_CLASS_ANNOTATION.equals(annotationType)) {
1066             //the non-public MigratedValueClass annotation will not be available in ct.sym,
1067             //replace with purely synthetic javac-internal annotation:
1068             annotationType = MIGRATED_VALUE_CLASS_ANNOTATION_INTERNAL;
1069         }
1070 
1071         if (REQUIRES_IDENTITY_ANNOTATION.equals(annotationType)) {
1072             //the non-public RequiresIdentity annotation will not be available in ct.sym,
1073             //replace with purely synthetic javac-internal annotation:
1074             annotationType = REQUIRES_IDENTITY_ANNOTATION_INTERNAL;
1075         }
1076 
1077         if (RESTRICTED_ANNOTATION.equals(annotationType)) {
1078             //the non-public Restricted annotation will not be available in ct.sym,
1079             //replace with purely synthetic javac-internal annotation:
1080             annotationType = RESTRICTED_ANNOTATION_INTERNAL;
1081         }
1082 
1083         return Annotation.of(ClassDesc.ofDescriptor(annotationType),
1084                 createElementPairs(values));
1085     }
1086 
1087     private List<AnnotationElement> createElementPairs(Map<String, Object> annotationAttributes) {
1088         return annotationAttributes.entrySet().stream()
1089                 .map(e -> AnnotationElement.of(e.getKey(), createAttributeValue(e.getValue())))
1090                 .collect(Collectors.toList());

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

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

2033             for (var attr : f.attributes()) {
2034                 readAttribute(fieldDesc, attr);
2035             }
2036             addField(clazzDesc, fieldDesc, version, null);
2037         }
2038     }
2039 
2040     private ModuleDescription inspectModuleInfoClassFile(byte[] data,
2041             Map<String, ModuleDescription> modules,
2042             String version) {
2043         ClassModel cm = ClassFile.of().parse(data);
2044 
2045         if (!cm.flags().has(AccessFlag.MODULE)) {
2046             return null;
2047         }
2048 
2049         ModuleHeaderDescription headerDesc = new ModuleHeaderDescription();
2050 
2051         headerDesc.versions = version;
2052         headerDesc.flags = cm.flags().flagsMask();
2053         headerDesc.preview = cm.minorVersion() == ClassFile.PREVIEW_MINOR_VERSION;
2054 
2055         for (var attr : cm.attributes()) {
2056             if (!readAttribute(headerDesc, attr))
2057                 return null;
2058         }
2059 
2060         String name = headerDesc.name;
2061 
2062         ModuleDescription moduleDesc = modules.get(name);
2063 
2064         if (moduleDesc == null) {
2065             moduleDesc = new ModuleDescription();
2066             moduleDesc.name = name;
2067             modules.put(moduleDesc.name, moduleDesc);
2068         }
2069 
2070         addModuleHeader(moduleDesc, headerDesc, version);
2071 
2072         return moduleDesc;
2073     }

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

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