diff --git a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java index 42cad61852d..99fb65d7130 100644 --- a/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java +++ b/make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java @@ -122,6 +122,7 @@ import com.sun.tools.classfile.Field; import com.sun.tools.classfile.InnerClasses_attribute; import com.sun.tools.classfile.InnerClasses_attribute.Info; import com.sun.tools.classfile.Method; +import com.sun.tools.classfile.MethodParameters_attribute; import com.sun.tools.classfile.ModuleResolution_attribute; import com.sun.tools.classfile.ModuleTarget_attribute; import com.sun.tools.classfile.Module_attribute; @@ -131,6 +132,8 @@ import com.sun.tools.classfile.Module_attribute.ProvidesEntry; import com.sun.tools.classfile.Module_attribute.RequiresEntry; import com.sun.tools.classfile.NestHost_attribute; import com.sun.tools.classfile.NestMembers_attribute; +import com.sun.tools.classfile.Record_attribute; +import com.sun.tools.classfile.Record_attribute.ComponentInfo; import com.sun.tools.classfile.RuntimeAnnotations_attribute; import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute; import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute; @@ -959,6 +962,22 @@ public class CreateSymbols { attributes.put(Attribute.NestMembers, new NestMembers_attribute(attributeString, nestMembers)); } + if (header.isRecord) { + assert header.recordComponents != null; + int attributeString = addString(constantPool, Attribute.Record); + ComponentInfo[] recordComponents = new ComponentInfo[header.recordComponents.size()]; + int i = 0; + for (RecordComponentDescription rcd : header.recordComponents) { + int name = addString(constantPool, rcd.name); + Descriptor desc = new Descriptor(addString(constantPool, rcd.descriptor)); + Map nestedAttrs = new HashMap<>(); + addGenericAttributes(rcd, constantPool, nestedAttrs); + Attributes attrs = new Attributes(nestedAttrs); + recordComponents[i++] = new ComponentInfo(name, desc, attrs); + } + attributes.put(Attribute.Record, + new Record_attribute(attributeString, recordComponents)); + } addInnerClassesAttribute(header, constantPool, attributes); } @@ -1017,6 +1036,18 @@ public class CreateSymbols { new RuntimeVisibleParameterAnnotations_attribute(attributeString, annotations)); } + if (desc.methodParameters != null && !desc.methodParameters.isEmpty()) { + int attributeString = + addString(constantPool, Attribute.MethodParameters); + MethodParameters_attribute.Entry[] entries = + desc.methodParameters + .stream() + .map(p -> new MethodParameters_attribute.Entry(addString(constantPool, p.name), + p.flags)) + .toArray(s -> new MethodParameters_attribute.Entry[s]); + attributes.put(Attribute.MethodParameters, + new MethodParameters_attribute(attributeString, entries)); + } } private void addAttributes(FieldDescription desc, List constantPool, Map attributes) { @@ -1595,7 +1626,9 @@ public class CreateSymbols { StringWriter data = new StringWriter(); ModuleDescription module = modules.get(e.getKey()); - module.write(data, desc.basePlatform, desc.version); + if (module != null) { //module == null should only be in tests. + module.write(data, desc.basePlatform, desc.version); + } for (ClassDescription clazz : e.getValue()) { clazz.write(data, desc.basePlatform, desc.version); @@ -2153,6 +2186,37 @@ public class CreateSymbols { .collect(Collectors.toList()); break; } + case Attribute.Record: { + assert feature instanceof ClassHeaderDescription; + Record_attribute record = (Record_attribute) attr; + List components = new ArrayList<>(); + for (ComponentInfo info : record.component_info_arr) { + RecordComponentDescription rcd = new RecordComponentDescription(); + rcd.name = info.getName(cf.constant_pool); + rcd.descriptor = info.descriptor.getValue(cf.constant_pool); + for (Attribute nestedAttr : info.attributes) { + readAttribute(cf, rcd, nestedAttr); + } + components.add(rcd); + } + ClassHeaderDescription chd = (ClassHeaderDescription) feature; + chd.isRecord = true; + chd.recordComponents = components; + break; + } + case Attribute.MethodParameters: { + assert feature instanceof MethodDescription; + MethodParameters_attribute params = (MethodParameters_attribute) attr; + MethodDescription method = (MethodDescription) feature; + method.methodParameters = new ArrayList<>(); + for (MethodParameters_attribute.Entry e : params.method_parameter_table) { + String name = cf.constant_pool.getUTF8Value(e.name_index); + MethodDescription.MethodParam param = + new MethodDescription.MethodParam(e.flags, name); + method.methodParameters.add(param); + } + break; + } default: throw new IllegalStateException("Unhandled attribute: " + attrName); @@ -2999,6 +3063,8 @@ public class CreateSymbols { List implementsAttr; String nestHost; List nestMembers; + boolean isRecord; + List recordComponents; @Override public int hashCode() { @@ -3007,6 +3073,8 @@ public class CreateSymbols { hash = 17 * hash + Objects.hashCode(this.implementsAttr); hash = 17 * hash + Objects.hashCode(this.nestHost); hash = 17 * hash + Objects.hashCode(this.nestMembers); + hash = 17 * hash + Objects.hashCode(this.isRecord); + hash = 17 * hash + Objects.hashCode(this.recordComponents); return hash; } @@ -3031,6 +3099,12 @@ public class CreateSymbols { if (!listEquals(this.nestMembers, other.nestMembers)) { return false; } + if (this.isRecord != other.isRecord) { + return false; + } + if (!listEquals(this.recordComponents, other.recordComponents)) { + return false; + } return true; } @@ -3048,8 +3122,12 @@ public class CreateSymbols { output.append(" nestHost " + nestHost); if (nestMembers != null && !nestMembers.isEmpty()) output.append(" nestMembers " + serializeList(nestMembers)); + if (isRecord) { + output.append(" record true"); + } writeAttributes(output); output.append("\n"); + writeRecordComponents(output, baselineVersion, version); writeInnerClasses(output, baselineVersion, version); } @@ -3065,14 +3143,37 @@ public class CreateSymbols { nestHost = reader.attributes.get("nestHost"); String nestMembersList = reader.attributes.get("nestMembers"); nestMembers = deserializeList(nestMembersList); + isRecord = reader.attributes.containsKey("record"); readAttributes(reader); reader.moveNext(); + if (isRecord) { + readRecordComponents(reader); + } readInnerClasses(reader); return true; } + protected void writeRecordComponents(Appendable output, + String baselineVersion, + String version) throws IOException { + if (recordComponents != null) { + for (RecordComponentDescription rcd : recordComponents) { + rcd.write(output, "", ""); + } + } + } + + protected void readRecordComponents(LineBasedReader reader) throws IOException { + recordComponents = new ArrayList<>(); + + while ("recordcomponent".equals(reader.lineKey)) { + RecordComponentDescription rcd = new RecordComponentDescription(); + rcd.read(reader); + recordComponents.add(rcd); + } + } } static abstract class HeaderDescription extends FeatureDescription { @@ -3145,6 +3246,7 @@ public class CreateSymbols { Object annotationDefaultValue; List> classParameterAnnotations; List> runtimeParameterAnnotations; + List methodParameters; public MethodDescription() { flagsNormalization = METHODS_FLAGS_NORMALIZATION; @@ -3221,6 +3323,15 @@ public class CreateSymbols { output.append(";"); } } + if (methodParameters != null && !methodParameters.isEmpty()) { + Function param2String = + p -> Integer.toHexString(p.flags) + ":" + p.name; + List paramsAsStrings = + methodParameters.stream() + .map(param2String) + .collect(Collectors.toList()); + output.append(" methodParameters " + serializeList(paramsAsStrings)); + } output.append("\n"); } @@ -3268,17 +3379,41 @@ public class CreateSymbols { runtimeParameterAnnotations = annos; } + String inMethodParameters = reader.attributes.get("methodParameters"); + if (inMethodParameters != null) { + Function string2Param = + p -> { + int sep = p.indexOf(':'); + return new MethodParam(Integer.parseInt(p.substring(0, sep)), + p.substring(sep + 1)); + }; + methodParameters = + deserializeList(inMethodParameters).stream() + .map(string2Param) + .collect(Collectors.toList()); + } + reader.moveNext(); return true; } + public static class MethodParam { + public final int flags; + public final String name; + + public MethodParam(int flags, String name) { + this.flags = flags; + this.name = name; + } + } } static class FieldDescription extends FeatureDescription { String name; String descriptor; Object constantValue; + String keyName = "field"; @Override public int hashCode() { @@ -3315,13 +3450,13 @@ public class CreateSymbols { if (shouldIgnore(baselineVersion, version)) return ; if (!versions.contains(version)) { - output.append("-field"); + output.append("-" + keyName); output.append(" name " + quote(name, false)); output.append(" descriptor " + quote(descriptor, false)); output.append("\n"); return ; } - output.append("field"); + output.append(keyName); output.append(" name " + name); output.append(" descriptor " + descriptor); if (constantValue != null) { @@ -3333,7 +3468,7 @@ public class CreateSymbols { @Override public boolean read(LineBasedReader reader) throws IOException { - if (!"field".equals(reader.lineKey)) + if (!keyName.equals(reader.lineKey)) return false; name = reader.attributes.get("name"); @@ -3366,6 +3501,19 @@ public class CreateSymbols { } + static final class RecordComponentDescription extends FieldDescription { + + public RecordComponentDescription() { + this.keyName = "recordcomponent"; + } + + @Override + protected boolean shouldIgnore(String baselineVersion, String version) { + return false; + } + + } + static final class AnnotationDescription { String annotationType; Map values; diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java index a124bff84c7..940ff2bae38 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java @@ -1516,7 +1516,7 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem } RecordComponent rc = null; if (addIfMissing) { - recordComponents = recordComponents.append(rc = new RecordComponent(var, annotations)); + recordComponents = recordComponents.append(rc = new RecordComponent(var.sym, annotations)); } return rc; } @@ -1527,6 +1527,10 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem return recordComponents; } + public void setRecordComponents(List recordComponents) { + this.recordComponents = recordComponents; + } + @DefinedBy(Api.LANGUAGE_MODEL) public NestingKind getNestingKind() { apiComplete(); @@ -1790,10 +1794,17 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem /** * Construct a record component, given its flags, name, type and owner. */ - public RecordComponent(JCVariableDecl fieldDecl, List annotations) { - super(PUBLIC, fieldDecl.sym.name, fieldDecl.sym.type, fieldDecl.sym.owner); + public RecordComponent(Name name, Type type, Symbol owner) { + super(PUBLIC, name, type, owner); + pos = -1; + originalAnnos = List.nil(); + isVarargs = false; + } + + public RecordComponent(VarSymbol field, List annotations) { + super(PUBLIC, field.name, field.type, field.owner); this.originalAnnos = annotations; - this.pos = fieldDecl.pos; + this.pos = field.pos; /* it is better to store the original information for this one, instead of relying * on the info in the type of the symbol. This is because on the presence of APs * the symbol will be blown out and we won't be able to know if the original diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 6f22fc97870..bb3fd28b930 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -1205,7 +1205,16 @@ public class ClassReader { if (sym.kind == TYP) { sym.flags_field |= RECORD; } - bp = bp + attrLen; + int componentCount = nextChar(); + ListBuffer components = new ListBuffer<>(); + for (int i = 0; i < componentCount; i++) { + Name name = poolReader.getName(nextChar()); + Type type = poolReader.getType(nextChar()); + RecordComponent c = new RecordComponent(name, type, sym); + readAttrs(c, AttributeKind.MEMBER); + components.add(c); + } + ((ClassSymbol) sym).setRecordComponents(components.toList()); } }, new AttributeReader(names.PermittedSubclasses, V59, CLASS_ATTRIBUTE) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java index 5d3010a11a6..f0e47f05d49 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/PrintingProcessor.java @@ -229,7 +229,7 @@ public class PrintingProcessor extends AbstractProcessor { writer.print("("); writer.print(e.getRecordComponents() .stream() - .map(recordDes -> recordDes.asType().toString() + " " + recordDes.getSimpleName()) + .map(recordDes -> annotationsToString(recordDes) + recordDes.asType().toString() + " " + recordDes.getSimpleName()) .collect(Collectors.joining(", "))); writer.print(")"); } @@ -448,7 +448,7 @@ public class PrintingProcessor extends AbstractProcessor { private void printModifiers(Element e) { ElementKind kind = e.getKind(); - if (kind == PARAMETER) { + if (kind == PARAMETER || kind == RECORD_COMPONENT) { // Print annotation inline writer.print(annotationsToString(e)); } else { @@ -456,7 +456,7 @@ public class PrintingProcessor extends AbstractProcessor { indent(); } - if (kind == ENUM_CONSTANT) + if (kind == ENUM_CONSTANT || kind == RECORD_COMPONENT) return; Set modifiers = new LinkedHashSet<>(); diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java index badbf90e453..a24fb837f43 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java @@ -362,16 +362,14 @@ public class ClassWriter { write(a, out); } - // Note: due to the use of shared resources, this method is not reentrant. public void write(Attribute attr, ClassOutputStream out) { out.writeShort(attr.attribute_name_index); - sharedOut.reset(); - attr.accept(this, sharedOut); - out.writeInt(sharedOut.size()); - sharedOut.writeTo(out); + ClassOutputStream nestedOut = new ClassOutputStream(); + attr.accept(this, nestedOut); + out.writeInt(nestedOut.size()); + nestedOut.writeTo(out); } - protected ClassOutputStream sharedOut = new ClassOutputStream(); protected AnnotationWriter annotationWriter = new AnnotationWriter(); @Override @@ -756,8 +754,8 @@ public class ClassWriter { return null; } - protected void writeAccessFlags(AccessFlags flags, ClassOutputStream p) { - sharedOut.writeShort(flags.flags); + protected void writeAccessFlags(AccessFlags flags, ClassOutputStream out) { + out.writeShort(flags.flags); } protected StackMapTableWriter stackMapWriter; diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/MethodParameters_attribute.java b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/MethodParameters_attribute.java index 86a15386258..942f8f00c04 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/MethodParameters_attribute.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/MethodParameters_attribute.java @@ -76,6 +76,11 @@ public class MethodParameters_attribute extends Attribute { flags = cr.readUnsignedShort(); } + public Entry(int name_index, int flags) { + this.name_index = name_index; + this.flags = flags; + } + public static int length() { return 6; } diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Record_attribute.java b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Record_attribute.java index 58a3ff967ee..8d67e6c3016 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Record_attribute.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Record_attribute.java @@ -44,6 +44,12 @@ public class Record_attribute extends Attribute { } } + public Record_attribute(int name_index, ComponentInfo[] component_info_arr) { + super(name_index, 2); + this.component_count = component_info_arr.length; + this.component_info_arr = component_info_arr; + } + @Override public R accept(Visitor visitor, D data) { return visitor.visitRecord(this, data); @@ -59,6 +65,12 @@ public class Record_attribute extends Attribute { attributes = new Attributes(cr); } + public ComponentInfo(int name_index, Descriptor descriptor, Attributes attributes) { + this.name_index = name_index; + this.descriptor = descriptor; + this.attributes = attributes; + } + public String getName(ConstantPool constant_pool) throws ConstantPoolException { return constant_pool.getUTF8Value(name_index); } diff --git a/make/langtools/test/sym/CreateSymbolsTest.java b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTest.java similarity index 59% rename from make/langtools/test/sym/CreateSymbolsTest.java rename to test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTest.java index 7b0bba038ac..2f201fd0067 100644 --- a/make/langtools/test/sym/CreateSymbolsTest.java +++ b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTest.java @@ -25,8 +25,14 @@ * @test * @bug 8072480 * @summary Unit test for CreateSymbols + * @modules java.compiler + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.jvm + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * jdk.jdeps/com.sun.tools.classfile * @clean * - * @run main CreateSymbolsTest + * @run main/othervm CreateSymbolsTest */ import java.io.IOException; @@ -38,7 +44,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; import javax.tools.JavaCompiler; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; @@ -56,37 +63,58 @@ public class CreateSymbolsTest { Path compileDir = testClasses.resolve("data"); deleteRecursively(compileDir); Files.createDirectories(compileDir); - Path createSymbols = findFile("../../make/src/classes/build/tools/symbolgenerator/CreateSymbols.java"); + Path createSymbols = findFile("../../make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java"); if (createSymbols == null) { System.err.println("Warning: cannot find CreateSymbols, skipping."); return ; } - Path createTestImpl = findFile("../../make/test/sym/CreateSymbolsTestImpl.java"); + Path createTestImpl = findFile("tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java"); if (createTestImpl == null) { - System.err.println("Warning: cannot find CreateSymbolsTestImpl, skipping."); - return ; + throw new AssertionError("Warning: cannot find CreateSymbolsTestImpl, skipping."); } - Path toolBox = findFile("../../test/tools/lib/ToolBox.java"); + Path toolBox = findFile("tools/lib/toolbox/"); if (toolBox == null) { - System.err.println("Warning: cannot find ToolBox, skipping."); - return ; + throw new AssertionError("Warning: cannot find ToolBox, skipping."); } JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) { - compiler.getTask(null, - null, - null, - Arrays.asList("-d", compileDir.toAbsolutePath().toString()), - null, - fm.getJavaFileObjects(createSymbols, createTestImpl, toolBox) - ).call(); + List files = new ArrayList<>(); + + files.add(createSymbols); + files.add(createTestImpl); + + files.add(toolBox.resolve("AbstractTask.java")); + files.add(toolBox.resolve("JavacTask.java")); + files.add(toolBox.resolve("Task.java")); + files.add(toolBox.resolve("ToolBox.java")); + + Boolean res = + compiler.getTask(null, + null, + null, + List.of("-d", + compileDir.toAbsolutePath().toString(), + "-g", + "--add-modules", "jdk.jdeps", + "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + "--add-exports", "jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED"), + null, + fm.getJavaFileObjectsFromPaths(files) + ).call(); + if (!res) { + throw new IllegalStateException("Cannot compile test."); + } } URLClassLoader cl = new URLClassLoader(new URL[] {testClasses.toUri().toURL(), compileDir.toUri().toURL()}); @@ -100,9 +128,9 @@ public class CreateSymbolsTest { for (Path d = testSrc; d != null; d = d.getParent()) { if (Files.exists(d.resolve("TEST.ROOT"))) { - Path createSymbols = d.resolve(path); - if (Files.exists(createSymbols)) { - return createSymbols; + Path file = d.resolve(path); + if (Files.exists(file)) { + return file; } } } diff --git a/make/langtools/test/sym/CreateSymbolsTestImpl.java b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java similarity index 76% rename from make/langtools/test/sym/CreateSymbolsTestImpl.java rename to test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java index 1751a9c6ec9..83b8f3c7fb7 100644 --- a/make/langtools/test/sym/CreateSymbolsTestImpl.java +++ b/test/langtools/tools/javac/platform/createsymbols/CreateSymbolsTestImpl.java @@ -29,7 +29,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; -import com.sun.tools.javac.file.ZipFileIndexCache; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; @@ -37,14 +36,20 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; +import java.util.Enumeration; import java.util.HashSet; import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import java.util.stream.Collectors; import java.util.stream.Stream; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.Task.Expect; +import toolbox.ToolBox; import build.tools.symbolgenerator.CreateSymbols; import build.tools.symbolgenerator.CreateSymbols.ClassDescription; import build.tools.symbolgenerator.CreateSymbols.ClassList; -import build.tools.symbolgenerator.CreateSymbols.CtSymKind; import build.tools.symbolgenerator.CreateSymbols.ExcludeIncludeList; import build.tools.symbolgenerator.CreateSymbols.VersionDescription; @@ -59,8 +64,6 @@ public class CreateSymbolsTestImpl { void doTest() throws Exception { boolean testRun = false; for (Method m : CreateSymbolsTestImpl.class.getDeclaredMethods()) { - if (!"testIncluded".equals(m.getName())) - continue; if (m.isAnnotationPresent(Test.class)) { m.invoke(this); testRun = true; @@ -76,19 +79,19 @@ public class CreateSymbolsTestImpl { doTest("package t; public class T { public void m() { } }", "package t; public class T { }", "package t; public class Test { { T t = null; t.m(); } }", - ToolBox.Expect.SUCCESS, - ToolBox.Expect.FAIL); + Expect.SUCCESS, + Expect.FAIL); doTest("package t; public class T { public void b() { } public void m() { } public void a() { } }", "package t; public class T { public void b() { } public void a() { } }", "package t; public class Test { { T t = null; t.b(); t.a(); } }", - ToolBox.Expect.SUCCESS, - ToolBox.Expect.SUCCESS); + Expect.SUCCESS, + Expect.SUCCESS); //with additional attribute (need to properly skip the member): doTest("package t; public class T { public void m() throws IllegalStateException { } public void a() { } }", "package t; public class T { public void a() { } }", "package t; public class Test { { T t = null; t.a(); } }", - ToolBox.Expect.SUCCESS, - ToolBox.Expect.SUCCESS); + Expect.SUCCESS, + Expect.SUCCESS); } @Test @@ -96,13 +99,13 @@ public class CreateSymbolsTestImpl { doTest("package t; public class T { }", "package t; public class T { public void m() { } }", "package t; public class Test { { T t = null; t.m(); } }", - ToolBox.Expect.FAIL, - ToolBox.Expect.SUCCESS); + Expect.FAIL, + Expect.SUCCESS); doTest("package t; public class T { public void b() { } public void a() { } }", "package t; public class T { public void b() { } public void m() { } public void a() { } }", "package t; public class Test { { T t = null; t.b(); t.a(); } }", - ToolBox.Expect.SUCCESS, - ToolBox.Expect.SUCCESS); + Expect.SUCCESS, + Expect.SUCCESS); } //verify fields added/modified/removed @@ -112,8 +115,8 @@ public class CreateSymbolsTestImpl { doTest("class Dummy {}", "package t; public class T { }", "package t; public class Test { { T t = new T(); } }", - ToolBox.Expect.FAIL, - ToolBox.Expect.SUCCESS); + Expect.FAIL, + Expect.SUCCESS); } @Test @@ -121,8 +124,8 @@ public class CreateSymbolsTestImpl { doTest("package t; public class T { public void m() { } }", "package t; public class T implements java.io.Serializable { public void m() { } }", "package t; public class Test { { java.io.Serializable t = new T(); } }", - ToolBox.Expect.FAIL, - ToolBox.Expect.SUCCESS); + Expect.FAIL, + Expect.SUCCESS); } @Test @@ -130,17 +133,17 @@ public class CreateSymbolsTestImpl { doTest("package t; public class T { }", "class Dummy {}", "package t; public class Test { { T t = new T(); } }", - ToolBox.Expect.SUCCESS, - ToolBox.Expect.FAIL); + Expect.SUCCESS, + Expect.FAIL); } @Test void testInnerClassAttributes() throws Exception { doTest("package t; public class T { public static class Inner { } }", - "package t; public class T { public static class Inner { } }", + "package t; public class T { public static class Inner { } public void extra() {} }", "package t; import t.T.Inner; public class Test { Inner i; }", - ToolBox.Expect.SUCCESS, - ToolBox.Expect.SUCCESS); + Expect.SUCCESS, + Expect.SUCCESS); } @Test @@ -148,8 +151,8 @@ public class CreateSymbolsTestImpl { doTest("package t; public class T { }", "package t; public class T { public static final int A = 0; }", "package t; public class Test { void t(int i) { switch (i) { case T.A: break;} } }", - ToolBox.Expect.FAIL, - ToolBox.Expect.SUCCESS); + Expect.FAIL, + Expect.SUCCESS); } @Test @@ -173,8 +176,8 @@ public class CreateSymbolsTestImpl { " public SuppressWarnings annotationValue() default @SuppressWarnings(\"cast\");\n" + "}\n", "package t; public @T class Test { }", - ToolBox.Expect.SUCCESS, - ToolBox.Expect.SUCCESS); + Expect.SUCCESS, + Expect.SUCCESS); } @Test @@ -214,7 +217,7 @@ public class CreateSymbolsTestImpl { void testAnnotations() throws Exception { doPrintElementTest("package t;" + "import java.lang.annotation.*;" + - "public @Visible @Invisible class T { }" + + "public @Visible @Invisible class T { public void extra() { } }" + "@Retention(RetentionPolicy.RUNTIME) @interface Visible { }" + "@Retention(RetentionPolicy.CLASS) @interface Invisible { }", "package t;" + @@ -227,11 +230,12 @@ public class CreateSymbolsTestImpl { "@t.Invisible\n" + "@t.Visible\n" + "public class T {\n\n" + - " public T();\n" + + " public T();\n\n" + + " public void extra();\n" + "}\n", "t.Visible", "package t;\n\n" + - "@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)\n" + + "@java.lang.annotation.Retention(RUNTIME)\n" + "@interface Visible {\n" + "}\n"); doPrintElementTest("package t;" + @@ -247,6 +251,7 @@ public class CreateSymbolsTestImpl { "import java.util.*;" + "public class T {" + " public void test(int h, @Invisible int i, @Visible List j, int k) { }" + + " public void extra() { }" + "}" + "@Retention(RetentionPolicy.RUNTIME) @interface Visible { }" + "@Retention(RetentionPolicy.CLASS) @interface Invisible { }", @@ -261,7 +266,7 @@ public class CreateSymbolsTestImpl { "}\n", "t.Visible", "package t;\n\n" + - "@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)\n" + + "@java.lang.annotation.Retention(RUNTIME)\n" + "@interface Visible {\n" + "}\n"); doPrintElementTest("package t;" + @@ -291,10 +296,10 @@ public class CreateSymbolsTestImpl { @Test void testStringConstant() throws Exception { doTest("package t; public class T { public static final String C = \"\"; }", - "package t; public class T { public static final String C = \"\"; }", + "package t; public class T { public static final String C = \"\"; public void extra() { } }", "package t; public class Test { { System.err.println(T.C); } }", - ToolBox.Expect.SUCCESS, - ToolBox.Expect.SUCCESS); + Expect.SUCCESS, + Expect.SUCCESS); } @Test @@ -302,8 +307,8 @@ public class CreateSymbolsTestImpl { String oldProfileAnnotation = CreateSymbols.PROFILE_ANNOTATION; try { CreateSymbols.PROFILE_ANNOTATION = "Lt/Ann;"; - doTestEquivalence("package t; public class T { public void t() {} } @interface Ann { }", - "package t; public @Ann class T { public void t() {} } @interface Ann { }", + doTestEquivalence("package t; public @Ann class T { public void t() {} } @interface Ann { }", + "package t; public class T { public void t() {} }", "t.T"); } finally { CreateSymbols.PROFILE_ANNOTATION = oldProfileAnnotation; @@ -351,13 +356,13 @@ public class CreateSymbolsTestImpl { doTest("package t; public class T { public class TT { public Object t() { return null; } } }", "package t; public class T { public class TT { public E t() { return null; } } }", "package t; public class Test { { T.TT tt = null; tt.t(); } }", - ToolBox.Expect.SUCCESS, - ToolBox.Expect.SUCCESS); + Expect.SUCCESS, + Expect.SUCCESS); } int i = 0; - void doTest(String code7, String code8, String testCode, ToolBox.Expect result7, ToolBox.Expect result8) throws Exception { + void doTest(String code7, String code8, String testCode, Expect result7, Expect result8) throws Exception { ToolBox tb = new ToolBox(); Path classes = prepareVersionedCTSym(code7, code8); Path output = classes.getParent(); @@ -365,22 +370,24 @@ public class CreateSymbolsTestImpl { Files.createDirectories(scratch); - tb.new JavacTask() + new JavacTask(tb) .sources(testCode) - .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "7"), "-XDuseOptimizedZip=false") + .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "7")) .run(result7) .writeAll(); - tb.new JavacTask() + new JavacTask(tb) .sources(testCode) - .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "8"), "-XDuseOptimizedZip=false") + .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "8")) .run(result8) .writeAll(); } private static String computeClassPath(Path classes, String version) throws IOException { try (Stream elements = Files.list(classes)) { - return elements.map(el -> el.toAbsolutePath().toString()) - .collect(Collectors.joining(File.pathSeparator)); + return elements.filter(el -> el.getFileName().toString().contains(version)) + .map(el -> el.resolve("java.base")) + .map(el -> el.toAbsolutePath().toString()) + .collect(Collectors.joining(File.pathSeparator)); } } @@ -393,17 +400,19 @@ public class CreateSymbolsTestImpl { Files.createDirectories(scratch); String out; - out = tb.new JavacTask(ToolBox.Mode.CMDLINE) - .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "7"), "-XDuseOptimizedZip=false", "-Xprint", className7) - .run(ToolBox.Expect.SUCCESS) - .getOutput(ToolBox.OutputKind.STDOUT); + out = new JavacTask(tb, Task.Mode.CMDLINE) + .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "7"), "-Xprint", className7) + .run(Expect.SUCCESS) + .getOutput(Task.OutputKind.STDOUT) + .replaceAll("\\R", "\n"); if (!out.equals(printed7)) { throw new AssertionError("out=" + out + "; printed7=" + printed7); } - out = tb.new JavacTask(ToolBox.Mode.CMDLINE) - .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "8"), "-XDuseOptimizedZip=false", "-Xprint", className8) - .run(ToolBox.Expect.SUCCESS) - .getOutput(ToolBox.OutputKind.STDOUT); + out = new JavacTask(tb, Task.Mode.CMDLINE) + .options("-d", scratch.toAbsolutePath().toString(), "-classpath", computeClassPath(classes, "8"), "-Xprint", className8) + .run(Expect.SUCCESS) + .getOutput(Task.OutputKind.STDOUT) + .replaceAll("\\R", "\n"); if (!out.equals(printed8)) { throw new AssertionError("out=" + out + "; printed8=" + printed8); } @@ -411,7 +420,7 @@ public class CreateSymbolsTestImpl { void doTestEquivalence(String code7, String code8, String testClass) throws Exception { Path classes = prepareVersionedCTSym(code7, code8); - Path classfile = classes.resolve("78").resolve(testClass.replace('.', '/') + ".class"); + Path classfile = classes.resolve("78").resolve("java.base").resolve(testClass.replace('.', '/') + ".class"); if (!Files.isReadable(classfile)) { throw new AssertionError("Cannot find expected class."); @@ -470,12 +479,105 @@ public class CreateSymbolsTestImpl { "t.PPI"); } + @Test + void testRecords() throws Exception { + doPrintElementTest("package t;" + + "public class T {" + + " public record R(int i, java.util.List l) { }" + + "}", + "package t;" + + "public class T {" + + " public record R(@Ann int i, long j, java.util.List l) { }" + + " public @interface Ann {} " + + "}", + "t.T$R", + """ + + public static record R(int i, java.util.List l) { + + public R(int i, + java.util.List l); + + public final java.lang.String toString(); + + public final int hashCode(); + + public final boolean equals(java.lang.Object arg0); + + public int i(); + + public java.util.List l(); + } + """, + "t.T$R", + """ + + public static record R(@t.T.Ann int i, long j, java.util.List l) { + + public final java.lang.String toString(); + + public final int hashCode(); + + public final boolean equals(java.lang.Object arg0); + + public java.util.List l(); + + public R(@t.T.Ann int i, + long j, + java.util.List l); + + @t.T.Ann + public int i(); + + public long j(); + } + """); + doPrintElementTest("package t;" + + "public record R() {" + + "}", + "package t;" + + "public record R(int i) {" + + "}", + "t.R", + """ + package t; + \n\ + public record R() { + \n\ + public R(); + \n\ + public final java.lang.String toString(); + \n\ + public final int hashCode(); + \n\ + public final boolean equals(java.lang.Object arg0); + } + """, + "t.R", + """ + package t; + \n\ + public record R(int i) { + \n\ + public final java.lang.String toString(); + \n\ + public final int hashCode(); + \n\ + public final boolean equals(java.lang.Object arg0); + \n\ + public R(int i); + \n\ + public int i(); + } + """); + } + void doTestIncluded(String code, String... includedClasses) throws Exception { boolean oldIncludeAll = includeAll; try { includeAll = false; Path classes = prepareVersionedCTSym(code, "package other; public class Other {}"); - Path root = classes.resolve("7"); + Path root = classes.resolve("7").resolve("java.base"); try (Stream classFiles = Files.walk(root)) { Set names = classFiles.map(p -> root.relativize(p)) .map(p -> p.toString()) @@ -503,11 +605,7 @@ public class CreateSymbolsTestImpl { Path ver8Jar = output.resolve("8.jar"); compileAndPack(output, ver8Jar, code8); - ZipFileIndexCache.getSharedInstance().clearCache(); - - Path classes = output.resolve("classes"); - - Files.createDirectories(classes); + Path classes = output.resolve("classes.zip"); Path ctSym = output.resolve("ct.sym"); @@ -518,7 +616,21 @@ public class CreateSymbolsTestImpl { testGenerate(ver7Jar, ver8Jar, ctSym, "8", classes.toAbsolutePath().toString()); - return classes; + Path classesDir = output.resolve("classes"); + + try (JarFile jf = new JarFile(classes.toFile())) { + Enumeration en = jf.entries(); + + while (en.hasMoreElements()) { + JarEntry je = en.nextElement(); + if (je.isDirectory()) continue; + Path target = classesDir.resolve(je.getName()); + Files.createDirectories(target.getParent()); + Files.copy(jf.getInputStream(je), target); + } + } + + return classesDir; } boolean includeAll = true; @@ -540,17 +652,18 @@ public class CreateSymbolsTestImpl { protected boolean includeEffectiveAccess(ClassList classes, ClassDescription clazz) { return includeAll ? true : super.includeEffectiveAccess(classes, clazz); } - }.createBaseLine(versions, acceptAll, descDest, null); + }.createBaseLine(versions, acceptAll, descDest, new String[0]); Path symbolsDesc = descDest.resolve("symbols"); - try (Writer symbolsFile = Files.newBufferedWriter(symbolsDesc)) { - symbolsFile.write("generate platforms 7:8"); - symbolsFile.write(System.lineSeparator()); - symbolsFile.write("platform version 7 files java.base-7.sym.txt"); - symbolsFile.write(System.lineSeparator()); - symbolsFile.write("platform version 8 base 7 files java.base-8.sym.txt"); - symbolsFile.write(System.lineSeparator()); + Path systemModules = descDest.resolve("systemModules"); + + Files.newBufferedWriter(systemModules).close(); + + try { + new CreateSymbols().createSymbols(null, symbolsDesc.toAbsolutePath().toString(), classDest, 0, "8", systemModules.toString()); + } catch (Throwable t) { + t.printStackTrace(); + throw t; } - new CreateSymbols().createSymbols(symbolsDesc.toAbsolutePath().toString(), classDest, CtSymKind.JOINED_VERSIONS); } void compileAndPack(Path output, Path outputFile, String... code) throws Exception { @@ -559,7 +672,7 @@ public class CreateSymbolsTestImpl { deleteRecursively(scratch); Files.createDirectories(scratch); System.err.println(Arrays.asList(code)); - tb.new JavacTask().sources(code).options("-d", scratch.toAbsolutePath().toString()).run(ToolBox.Expect.SUCCESS); + new JavacTask(tb).sources(code).options("-d", scratch.toAbsolutePath().toString()).run(Expect.SUCCESS); List classFiles = collectClassFile(scratch); try (Writer out = Files.newBufferedWriter(outputFile)) { for (String classFile : classFiles) { diff --git a/test/langtools/tools/javac/records/RecordReading.java b/test/langtools/tools/javac/records/RecordReading.java new file mode 100644 index 00000000000..6133a04fca9 --- /dev/null +++ b/test/langtools/tools/javac/records/RecordReading.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary test the records can be read by javac properly + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run main RecordReading + */ + + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; +import toolbox.TestRunner; +import toolbox.ToolBox; +import toolbox.JavacTask; +import toolbox.Task; + +public class RecordReading extends TestRunner { + ToolBox tb; + + RecordReading() { + super(System.err); + tb = new ToolBox(); + } + + protected void runTests() throws Exception { + runTests(m -> new Object[]{Paths.get(m.getName())}); + } + + public static void main(String... args) throws Exception { + RecordReading t = new RecordReading(); + t.runTests(); + } + + Path[] findJavaFiles(Path... paths) throws IOException { + return tb.findJavaFiles(paths); + } + + @Test + public void testRecordClassFileReading(Path base) throws Exception { + Path src = base.resolve("src"); + + tb.writeJavaFiles(src, + """ + public record R(int i, @A long j, java.util.List l) {} + """, + """ + public @interface A {} + """); + + Path out = base.resolve("out"); + Files.createDirectories(out); + + new JavacTask(tb) + .outdir(out) + .files(findJavaFiles(src)) + .run(); + + //read the class file back, to verify javac's ClassReader + //reads the Record attribute properly: + String output = new JavacTask(tb) + .options("-Xprint") + .classpath(out.toString()) + .classes("R") + .run() + .writeAll() + .getOutput(Task.OutputKind.STDOUT) + .replaceAll("\\R", "\n"); + + String expected = + """ + \n\ + public record R(int i, @A long j, java.util.List l) { + private final int i; + @A + private final long j; + private final java.util.List l; + \n\ + public R(int i, + @A long j, + java.util.List l); + \n\ + public final java.lang.String toString(); + \n\ + public final int hashCode(); + \n\ + public final boolean equals(java.lang.Object arg0); + \n\ + public int i(); + \n\ + @A + public long j(); + \n\ + public java.util.List l(); + } + """; + if (!Objects.equals(expected, output)) { + throw new AssertionError("Unexpected output: " + output); + } + } + +}