8256950: Add record attribute support to symbol generator CreateSymbols

Reviewed-by: jjg, chegar
This commit is contained in:
Jan Lahoda 2020-12-09 15:05:01 +00:00
parent f148915d54
commit 6eff9315e1
10 changed files with 563 additions and 110 deletions

View File

@ -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<String, Attribute> 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<CPInfo> constantPool, Map<String, Attribute> 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<RecordComponentDescription> 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<String> implementsAttr;
String nestHost;
List<String> nestMembers;
boolean isRecord;
List<RecordComponentDescription> 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<List<AnnotationDescription>> classParameterAnnotations;
List<List<AnnotationDescription>> runtimeParameterAnnotations;
List<MethodParam> methodParameters;
public MethodDescription() {
flagsNormalization = METHODS_FLAGS_NORMALIZATION;
@ -3221,6 +3323,15 @@ public class CreateSymbols {
output.append(";");
}
}
if (methodParameters != null && !methodParameters.isEmpty()) {
Function<MethodParam, String> param2String =
p -> Integer.toHexString(p.flags) + ":" + p.name;
List<String> 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<String, MethodParam> 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<String, Object> values;

View File

@ -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<RecordComponent> 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<JCAnnotation> 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<JCAnnotation> 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

View File

@ -1205,7 +1205,16 @@ public class ClassReader {
if (sym.kind == TYP) {
sym.flags_field |= RECORD;
}
bp = bp + attrLen;
int componentCount = nextChar();
ListBuffer<RecordComponent> 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) {

View File

@ -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<Modifier> modifiers = new LinkedHashSet<>();

View File

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

View File

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

View File

@ -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, D> R accept(Visitor<R, D> 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);
}

View File

@ -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<Path> 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;
}
}
}

View File

@ -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<String> 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<E> { 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<Path> 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<String> l) { }" +
"}",
"package t;" +
"public class T {" +
" public record R(@Ann int i, long j, java.util.List<String> l) { }" +
" public @interface Ann {} " +
"}",
"t.T$R",
"""
public static record R(int i, java.util.List<java.lang.String> l) {
public R(int i,
java.util.List<java.lang.String> 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<java.lang.String> l();
}
""",
"t.T$R",
"""
public static record R(@t.T.Ann int i, long j, java.util.List<java.lang.String> l) {
public final java.lang.String toString();
public final int hashCode();
public final boolean equals(java.lang.Object arg0);
public java.util.List<java.lang.String> l();
public R(@t.T.Ann int i,
long j,
java.util.List<java.lang.String> 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<Path> classFiles = Files.walk(root)) {
Set<String> 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<JarEntry> 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<String> classFiles = collectClassFile(scratch);
try (Writer out = Files.newBufferedWriter(outputFile)) {
for (String classFile : classFiles) {

View File

@ -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<String> 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<java.lang.String> l) {
private final int i;
@A
private final long j;
private final java.util.List<java.lang.String> l;
\n\
public R(int i,
@A long j,
java.util.List<java.lang.String> 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<java.lang.String> l();
}
""";
if (!Objects.equals(expected, output)) {
throw new AssertionError("Unexpected output: " + output);
}
}
}