< prev index next > make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java
Print this page
* Note: the versions are expected to be a single character.
*
*/
public class CreateSymbols {
+ /**
+ * <p>Support for a "preview version" of classfiles when running with preview
+ * mode. This is modeled as a new version (@) and since preview mode is only
+ * supported for the current version, a single identifier token is sufficient.
+ *
+ * <p>For example, inside ct.sym, 27 will be modeled as 'R', and the preview
+ * for 27 will be '@'. Classfiles unchanged between 27 and 27-preview will
+ * not be duplicated (in the same way classfiles that are common between 26
+ * and 27 are shared).
+ */
+ private static final String PREVIEW_VERSION = "@";
+
//<editor-fold defaultstate="collapsed" desc="ct.sym construction">
/**Create sig files for ct.sym reading the classes description from the directory that contains
* {@code ctDescriptionFile}, using the file as a recipe to create the sigfiles.
*/
public void createSymbols(String ctDescriptionFileExtra, String ctDescriptionFile, String ctSymLocation,
.collect(Collectors.toSet());
loadVersionClassesFromDirectory(data.classes, data.modules, moduleClassPath,
includedModules, currentVersion, previousVersion);
+ loadVersionClassesFromDirectory(data.classes, data.modules, moduleClassPath,
+ includedModules, PREVIEW_VERSION, currentVersion);
+
stripNonExistentAnnotations(data);
splitHeaders(data.classes);
Map<String, Map<Character, String>> package2Version2Module = new HashMap<>();
Map<String, Set<FileData>> directory2FileData = new TreeMap<>();
+ String currentVersionFin = currentVersion;
+
for (ModuleDescription md : data.modules.values()) {
for (ModuleHeaderDescription mhd : md.header) {
writeModulesForVersions(directory2FileData,
md,
mhd,
mhd.versions,
version -> {
String versionString = Character.toString(version);
+ if (PREVIEW_VERSION.equals(versionString)) {
+ versionString = currentVersionFin;
+ }
int versionNumber = Integer.parseInt(versionString, Character.MAX_RADIX);
versionString = Integer.toString(versionNumber);
if (versionNumber == currentVersionParsed && !preReleaseTag.isEmpty()) {
versionString = versionString + "-" + preReleaseTag;
}
"Ljdk/internal/javac/Restricted+Annotation;";
private static final String VALUE_BASED_ANNOTATION =
"Ljdk/internal/ValueBased;";
private static final String VALUE_BASED_ANNOTATION_INTERNAL =
"Ljdk/internal/ValueBased+Annotation;";
+ private static final String MIGRATED_VALUE_CLASS_ANNOTATION =
+ "Ljdk/internal/MigratedValueClass;";
+ private static final String MIGRATED_VALUE_CLASS_ANNOTATION_INTERNAL =
+ "Ljdk/internal/MigratedValueClass+Annotation;";
private static final String REQUIRES_IDENTITY_ANNOTATION =
"Ljdk/internal/RequiresIdentity;";
private static final String REQUIRES_IDENTITY_ANNOTATION_INTERNAL =
"Ljdk/internal/RequiresIdentity+Annotation;";
public static final Set<String> HARDCODED_ANNOTATIONS = new HashSet<>(
List.of("Ljdk/Profile+Annotation;",
"Lsun/Proprietary+Annotation;",
PREVIEW_FEATURE_ANNOTATION_OLD,
PREVIEW_FEATURE_ANNOTATION_NEW,
VALUE_BASED_ANNOTATION,
+ MIGRATED_VALUE_CLASS_ANNOTATION,
RESTRICTED_ANNOTATION,
REQUIRES_IDENTITY_ANNOTATION));
private void stripNonExistentAnnotations(LoadDescriptions data) {
Set<String> allClasses = data.classes.name2Class.keySet();
ClassDescription classDescription,
ClassHeaderDescription header,
String module,
String version) throws IOException {
var classFile = ClassFile.of().build(ClassDesc.ofInternalName(classDescription.name), clb -> {
+ if (header.preview) {
+ clb.withVersion(ClassFile.latestMajorVersion(), ClassFile.PREVIEW_MINOR_VERSION);
+ }
if (header.extendsAttr != null)
clb.withSuperclass(ClassDesc.ofInternalName(header.extendsAttr));
clb.withInterfaceSymbols(header.implementsAttr.stream().map(ClassDesc::ofInternalName).collect(Collectors.toList()))
.withFlags(header.flags);
for (FieldDescription fieldDesc : classDescription.fields) {
//the non-public ValueBased annotation will not be available in ct.sym,
//replace with purely synthetic javac-internal annotation:
annotationType = VALUE_BASED_ANNOTATION_INTERNAL;
}
+ if (MIGRATED_VALUE_CLASS_ANNOTATION.equals(annotationType)) {
+ //the non-public MigratedValueClass annotation will not be available in ct.sym,
+ //replace with purely synthetic javac-internal annotation:
+ annotationType = MIGRATED_VALUE_CLASS_ANNOTATION_INTERNAL;
+ }
+
if (REQUIRES_IDENTITY_ANNOTATION.equals(annotationType)) {
//the non-public RequiresIdentity annotation will not be available in ct.sym,
//replace with purely synthetic javac-internal annotation:
annotationType = REQUIRES_IDENTITY_ANNOTATION_INTERNAL;
}
ExcludeIncludeList currentEIList = new ExcludeIncludeList(includes,
privateIncludes,
Collections.emptySet());
try {
Map<Path, ModuleHeaderDescription> modulePath2Header = new HashMap<>();
! List<Path> pendingExportedDirectories = new ArrayList<>();
try (DirectoryStream<Path> ds = Files.newDirectoryStream(modulesDirectory)) {
for (Path p : ds) {
if (!includedModules.contains(p.getFileName().toString())) {
continue;
}
! Path moduleInfo = p.resolve("module-info.class");
if (Files.isReadable(moduleInfo)) {
ModuleDescription md = inspectModuleInfoClassFile(Files.readAllBytes(moduleInfo),
currentVersionModules, version);
if (md == null) {
ExcludeIncludeList currentEIList = new ExcludeIncludeList(includes,
privateIncludes,
Collections.emptySet());
try {
+ record ExportedDir(Path modulePath, Path exportedDir) {}
Map<Path, ModuleHeaderDescription> modulePath2Header = new HashMap<>();
! List<ExportedDir> pendingExportedDirectories = new ArrayList<>();
try (DirectoryStream<Path> ds = Files.newDirectoryStream(modulesDirectory)) {
for (Path p : ds) {
if (!includedModules.contains(p.getFileName().toString())) {
continue;
}
! Path moduleInfo = resolvePossiblyPreviewClassFile(version, p, p.resolve("module-info.class"));
if (Files.isReadable(moduleInfo)) {
ModuleDescription md = inspectModuleInfoClassFile(Files.readAllBytes(moduleInfo),
currentVersionModules, version);
if (md == null) {
.map(e -> e.packageName + '/')
.collect(Collectors.toSet());
for (String dir : currentModuleExports) {
includes.add(dir);
! pendingExportedDirectories.add(p.resolve(dir));
}
} else {
throw new IllegalArgumentException("Included module: " +
p.getFileName() +
" does not have a module-info.class");
.map(e -> e.packageName + '/')
.collect(Collectors.toSet());
for (String dir : currentModuleExports) {
includes.add(dir);
! pendingExportedDirectories.add(new ExportedDir(p, p.resolve(dir)));
}
} else {
throw new IllegalArgumentException("Included module: " +
p.getFileName() +
" does not have a module-info.class");
}
}
List<String> pendingExtraClasses = new ArrayList<>();
! for (Path exported : pendingExportedDirectories) {
! try (DirectoryStream<Path> ds = Files.newDirectoryStream(exported)) {
for (Path p2 : ds) {
if (!Files.isRegularFile(p2) || !p2.getFileName().toString().endsWith(".class")) {
continue;
}
loadFromDirectoryHandleClassFile(p2, currentVersionClasses,
currentEIList, version,
pendingExtraClasses);
}
}
}
}
List<String> pendingExtraClasses = new ArrayList<>();
! for (ExportedDir exported : pendingExportedDirectories) {
! try (DirectoryStream<Path> ds = Files.newDirectoryStream(exported.exportedDir())) {
for (Path p2 : ds) {
if (!Files.isRegularFile(p2) || !p2.getFileName().toString().endsWith(".class")) {
continue;
}
+ p2 = resolvePossiblyPreviewClassFile(version, exported.modulePath(), p2);
+
loadFromDirectoryHandleClassFile(p2, currentVersionClasses,
currentEIList, version,
pendingExtraClasses);
}
}
for (Entry<Path, ModuleHeaderDescription> e : modulePath2Header.entrySet()) {
Path currentPath = e.getKey().resolve(current + ".class");
if (Files.isReadable(currentPath)) {
+ currentPath = resolvePossiblyPreviewClassFile(version, e.getKey(), currentPath);
String pack = current.substring(0, current.lastIndexOf('/'));
e.getValue().extraModulePackages.add(pack);
loadFromDirectoryHandleClassFile(currentPath, currentVersionClasses,
todo.addAll(superTypes);
});
}
}
+ private Path resolvePossiblyPreviewClassFile(String version, Path moduleClassDir, Path classfile) {
+ if (!PREVIEW_VERSION.equals(version)) {
+ return classfile;
+ }
+
+ Path relativePath = moduleClassDir.relativize(classfile);
+ Path previewCandidate = moduleClassDir.resolve("META-INF").resolve("preview").resolve(relativePath);
+
+ if (Files.exists(previewCandidate)) {
+ return previewCandidate;
+ }
+
+ return classfile;
+ }
+
private void finishClassLoading(ClassList classes, Map<String, ModuleDescription> modules, Map<String, ModuleDescription> currentVersionModules, ClassList currentVersionClasses, ExcludeIncludeList currentEIList, String version,
String baseline) {
ModuleDescription unsupported =
currentVersionModules.get("jdk.unsupported");
extraTask.accept(cm);
ClassHeaderDescription headerDesc = new ClassHeaderDescription();
headerDesc.flags = cm.flags().flagsMask();
+ headerDesc.preview = cm.minorVersion() == ClassFile.PREVIEW_MINOR_VERSION;
if (cm.superclass().isPresent()) {
headerDesc.extendsAttr = cm.superclass().get().asInternalName();
}
headerDesc.implementsAttr = cm.interfaces().stream().map(ClassEntry::asInternalName).collect(Collectors.toList());
ModuleHeaderDescription headerDesc = new ModuleHeaderDescription();
headerDesc.versions = version;
headerDesc.flags = cm.flags().flagsMask();
+ headerDesc.preview = cm.minorVersion() == ClassFile.PREVIEW_MINOR_VERSION;
for (var attr : cm.attributes()) {
if (!readAttribute(headerDesc, attr))
return null;
}
case ModuleMainClassAttribute a -> ((ModuleHeaderDescription) feature).moduleMainClass = a.mainClass().asInternalName();
case RuntimeInvisibleTypeAnnotationsAttribute a ->
feature.classTypeAnnotations = typeAnnotations2Descriptions(a.annotations());
case RuntimeVisibleTypeAnnotationsAttribute a ->
feature.runtimeTypeAnnotations = typeAnnotations2Descriptions(a.annotations());
! default -> throw new IllegalArgumentException("Unhandled attribute: " + attr.attributeName()); // Do nothing
}
return true;
}
case ModuleMainClassAttribute a -> ((ModuleHeaderDescription) feature).moduleMainClass = a.mainClass().asInternalName();
case RuntimeInvisibleTypeAnnotationsAttribute a ->
feature.classTypeAnnotations = typeAnnotations2Descriptions(a.annotations());
case RuntimeVisibleTypeAnnotationsAttribute a ->
feature.runtimeTypeAnnotations = typeAnnotations2Descriptions(a.annotations());
! default -> {
+ if (attr.attributeName().equalsString("LoadableDescriptors")) {
+ //OK, do nothing
+ } else {
+ throw new IllegalArgumentException("Unhandled attribute: " + attr.attributeName());
+ }
+ }
}
return true;
}
}
}
static abstract class HeaderDescription extends FeatureDescription {
List<InnerClassInfo> innerClasses;
+ boolean preview;
@Override
public int hashCode() {
int hash = super.hashCode();
hash = 19 * hash + Objects.hashCode(this.innerClasses);
+ hash = 19 * hash + Objects.hashCode(this.preview);
return hash;
}
@Override
public boolean equals(Object obj) {
}
final HeaderDescription other = (HeaderDescription) obj;
if (!listEquals(this.innerClasses, other.innerClasses)) {
return false;
}
+ if (this.preview != other.preview) {
+ return false;
+ }
return true;
}
protected void writeInnerClasses(Appendable output,
String baselineVersion,
reader.moveNext();
}
}
+ @Override
+ protected void writeAttributes(Appendable output) throws IOException {
+ super.writeAttributes(output);
+ if (preview) {
+ output.append(" preview true");
+ }
+ }
+
+ @Override
+ protected void readAttributes(LineBasedReader reader) {
+ super.readAttributes(reader);
+ String inPreview = reader.attributes.get("preview");
+ if ("true".equals(inPreview)) {
+ preview = true;
+ }
+ }
}
static class MethodDescription extends FeatureDescription {
static int METHODS_FLAGS_NORMALIZATION = ~0;
String name;
< prev index next >