Add class writing support for record and nest attributes
* https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-4.html#jvms-4.7.28
* https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-4.html#jvms-4.7.29
* https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-4.html#jvms-4.7.30
PiperOrigin-RevId: 397416507
diff --git a/java/com/google/turbine/bytecode/Attribute.java b/java/com/google/turbine/bytecode/Attribute.java
index 7b415a7..581de3d 100644
--- a/java/com/google/turbine/bytecode/Attribute.java
+++ b/java/com/google/turbine/bytecode/Attribute.java
@@ -42,6 +42,9 @@
RUNTIME_INVISIBLE_TYPE_ANNOTATIONS("RuntimeInvisibleTypeAnnotations"),
METHOD_PARAMETERS("MethodParameters"),
MODULE("Module"),
+ NEST_HOST("NestHost"),
+ NEST_MEMBERS("NestMembers"),
+ RECORD("Record"),
TURBINE_TRANSITIVE_JAR("TurbineTransitiveJar");
private final String signature;
@@ -311,6 +314,88 @@
}
}
+ /** A JVMS §4.7.28 NestHost attribute. */
+ class NestHost implements Attribute {
+
+ private final String hostClass;
+
+ public NestHost(String hostClass) {
+ this.hostClass = hostClass;
+ }
+
+ String hostClass() {
+ return hostClass;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.NEST_HOST;
+ }
+ }
+
+ /** A JVMS §4.7.29 NestHost attribute. */
+ class NestMembers implements Attribute {
+
+ private final ImmutableList<String> classes;
+
+ public NestMembers(ImmutableList<String> classes) {
+ this.classes = classes;
+ }
+
+ ImmutableList<String> classes() {
+ return classes;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.NEST_MEMBERS;
+ }
+ }
+
+ /** A JVMS §4.7.30 Record attribute. */
+ class Record implements Attribute {
+
+ private final ImmutableList<Component> components;
+
+ public Record(ImmutableList<Component> components) {
+ this.components = components;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.RECORD;
+ }
+
+ ImmutableList<Component> components() {
+ return components;
+ }
+
+ /** A JVMS §4.7.30 Record component info. */
+ static class Component {
+ private final String name;
+ private final String descriptor;
+ private final List<Attribute> attributes;
+
+ Component(String name, String descriptor, List<Attribute> attributes) {
+ this.name = name;
+ this.descriptor = descriptor;
+ this.attributes = attributes;
+ }
+
+ String name() {
+ return name;
+ }
+
+ String descriptor() {
+ return descriptor;
+ }
+
+ List<Attribute> attributes() {
+ return attributes;
+ }
+ }
+ }
+
/** A custom attribute for recording the original jar of repackaged transitive classes. */
class TurbineTransitiveJar implements Attribute {
diff --git a/java/com/google/turbine/bytecode/AttributeWriter.java b/java/com/google/turbine/bytecode/AttributeWriter.java
index b92d2e7..ed7b2ab 100644
--- a/java/com/google/turbine/bytecode/AttributeWriter.java
+++ b/java/com/google/turbine/bytecode/AttributeWriter.java
@@ -42,59 +42,66 @@
public class AttributeWriter {
private final ConstantPool pool;
- private final ByteArrayDataOutput output;
- public AttributeWriter(ConstantPool pool, ByteArrayDataOutput output) {
+ public AttributeWriter(ConstantPool pool) {
this.pool = pool;
- this.output = output;
}
/** Writes a single attribute. */
- public void write(Attribute attribute) {
+ public void write(ByteArrayDataOutput output, Attribute attribute) {
switch (attribute.kind()) {
case SIGNATURE:
- writeSignatureAttribute((Signature) attribute);
+ writeSignatureAttribute(output, (Signature) attribute);
break;
case EXCEPTIONS:
- writeExceptionsAttribute((ExceptionsAttribute) attribute);
+ writeExceptionsAttribute(output, (ExceptionsAttribute) attribute);
break;
case INNER_CLASSES:
- writeInnerClasses((InnerClasses) attribute);
+ writeInnerClasses(output, (InnerClasses) attribute);
break;
case CONSTANT_VALUE:
- writeConstantValue((ConstantValue) attribute);
+ writeConstantValue(output, (ConstantValue) attribute);
break;
case RUNTIME_VISIBLE_ANNOTATIONS:
case RUNTIME_INVISIBLE_ANNOTATIONS:
- writeAnnotation((Attribute.Annotations) attribute);
+ writeAnnotation(output, (Attribute.Annotations) attribute);
break;
case ANNOTATION_DEFAULT:
- writeAnnotationDefault((Attribute.AnnotationDefault) attribute);
+ writeAnnotationDefault(output, (Attribute.AnnotationDefault) attribute);
break;
case RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS:
case RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS:
- writeParameterAnnotations((Attribute.ParameterAnnotations) attribute);
+ writeParameterAnnotations(output, (Attribute.ParameterAnnotations) attribute);
break;
case DEPRECATED:
- writeDeprecated(attribute);
+ writeDeprecated(output, attribute);
break;
case RUNTIME_INVISIBLE_TYPE_ANNOTATIONS:
case RUNTIME_VISIBLE_TYPE_ANNOTATIONS:
- writeTypeAnnotation((Attribute.TypeAnnotations) attribute);
+ writeTypeAnnotation(output, (Attribute.TypeAnnotations) attribute);
break;
case METHOD_PARAMETERS:
- writeMethodParameters((Attribute.MethodParameters) attribute);
+ writeMethodParameters(output, (Attribute.MethodParameters) attribute);
break;
case MODULE:
- writeModule((Attribute.Module) attribute);
+ writeModule(output, (Attribute.Module) attribute);
+ break;
+ case NEST_HOST:
+ writeNestHost(output, (Attribute.NestHost) attribute);
+ break;
+ case NEST_MEMBERS:
+ writeNestMembers(output, (Attribute.NestMembers) attribute);
+ break;
+ case RECORD:
+ writeRecord(output, (Attribute.Record) attribute);
break;
case TURBINE_TRANSITIVE_JAR:
- writeTurbineTransitiveJar((Attribute.TurbineTransitiveJar) attribute);
+ writeTurbineTransitiveJar(output, (Attribute.TurbineTransitiveJar) attribute);
break;
}
}
- private void writeInnerClasses(InnerClasses attribute) {
+ private void writeInnerClasses(ByteArrayDataOutput output, InnerClasses attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(attribute.inners.size() * 8 + 2);
output.writeShort(attribute.inners.size());
@@ -106,7 +113,7 @@
}
}
- private void writeExceptionsAttribute(ExceptionsAttribute attribute) {
+ private void writeExceptionsAttribute(ByteArrayDataOutput output, ExceptionsAttribute attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(2 + attribute.exceptions.size() * 2);
output.writeShort(attribute.exceptions.size());
@@ -115,13 +122,13 @@
}
}
- private void writeSignatureAttribute(Signature attribute) {
+ private void writeSignatureAttribute(ByteArrayDataOutput output, Signature attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(2);
output.writeShort(pool.utf8(attribute.signature));
}
- public void writeConstantValue(ConstantValue attribute) {
+ public void writeConstantValue(ByteArrayDataOutput output, ConstantValue attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(2);
Const.Value value = attribute.value;
@@ -158,7 +165,7 @@
}
}
- public void writeAnnotation(Annotations attribute) {
+ public void writeAnnotation(ByteArrayDataOutput output, Annotations attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
tmp.writeShort(attribute.annotations().size());
@@ -170,7 +177,8 @@
output.write(data);
}
- public void writeAnnotationDefault(Attribute.AnnotationDefault attribute) {
+ public void writeAnnotationDefault(
+ ByteArrayDataOutput output, Attribute.AnnotationDefault attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
new AnnotationWriter(pool, tmp).writeElementValue(attribute.value());
@@ -179,7 +187,8 @@
output.write(data);
}
- public void writeParameterAnnotations(Attribute.ParameterAnnotations attribute) {
+ public void writeParameterAnnotations(
+ ByteArrayDataOutput output, Attribute.ParameterAnnotations attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
tmp.writeByte(attribute.annotations().size());
@@ -194,12 +203,12 @@
output.write(data);
}
- private void writeDeprecated(Attribute attribute) {
+ private void writeDeprecated(ByteArrayDataOutput output, Attribute attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(0);
}
- private void writeTypeAnnotation(TypeAnnotations attribute) {
+ private void writeTypeAnnotation(ByteArrayDataOutput output, TypeAnnotations attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
tmp.writeShort(attribute.annotations().size());
@@ -211,7 +220,7 @@
output.write(data);
}
- private void writeMethodParameters(MethodParameters attribute) {
+ private void writeMethodParameters(ByteArrayDataOutput output, MethodParameters attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(attribute.parameters().size() * 4 + 1);
output.writeByte(attribute.parameters().size());
@@ -221,7 +230,7 @@
}
}
- private void writeModule(Attribute.Module attribute) {
+ private void writeModule(ByteArrayDataOutput output, Attribute.Module attribute) {
ModuleInfo module = attribute.module();
ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
@@ -277,7 +286,40 @@
output.write(data);
}
- private void writeTurbineTransitiveJar(TurbineTransitiveJar attribute) {
+ private void writeNestHost(ByteArrayDataOutput output, Attribute.NestHost attribute) {
+ output.writeShort(pool.utf8(attribute.kind().signature()));
+ output.writeInt(2);
+ output.writeShort(pool.classInfo(attribute.hostClass()));
+ }
+
+ private void writeNestMembers(ByteArrayDataOutput output, Attribute.NestMembers attribute) {
+ output.writeShort(pool.utf8(attribute.kind().signature()));
+ output.writeInt(2 + attribute.classes().size() * 2);
+ output.writeShort(attribute.classes().size());
+ for (String classes : attribute.classes()) {
+ output.writeShort(pool.classInfo(classes));
+ }
+ }
+
+ private void writeRecord(ByteArrayDataOutput output, Attribute.Record attribute) {
+ output.writeShort(pool.utf8(attribute.kind().signature()));
+ ByteArrayDataOutput tmp = ByteStreams.newDataOutput();
+ tmp.writeShort(attribute.components().size());
+ for (Attribute.Record.Component c : attribute.components()) {
+ tmp.writeShort(pool.utf8(c.name()));
+ tmp.writeShort(pool.utf8(c.descriptor()));
+ tmp.writeShort(c.attributes().size());
+ for (Attribute a : c.attributes()) {
+ write(tmp, a);
+ }
+ }
+ byte[] data = tmp.toByteArray();
+ output.writeInt(data.length);
+ output.write(data);
+ }
+
+ private void writeTurbineTransitiveJar(
+ ByteArrayDataOutput output, TurbineTransitiveJar attribute) {
output.writeShort(pool.utf8(attribute.kind().signature()));
output.writeInt(2);
output.writeShort(pool.utf8(attribute.transitiveJar));
diff --git a/java/com/google/turbine/bytecode/ClassFile.java b/java/com/google/turbine/bytecode/ClassFile.java
index af5ee12..ed28329 100644
--- a/java/com/google/turbine/bytecode/ClassFile.java
+++ b/java/com/google/turbine/bytecode/ClassFile.java
@@ -42,6 +42,9 @@
private final List<InnerClass> innerClasses;
private final ImmutableList<TypeAnnotationInfo> typeAnnotations;
private final @Nullable ModuleInfo module;
+ private final @Nullable String nestHost;
+ private final ImmutableList<String> nestMembers;
+ private final @Nullable RecordInfo record;
private final @Nullable String transitiveJar;
public ClassFile(
@@ -56,6 +59,9 @@
List<InnerClass> innerClasses,
ImmutableList<TypeAnnotationInfo> typeAnnotations,
@Nullable ModuleInfo module,
+ @Nullable String nestHost,
+ ImmutableList<String> nestMembers,
+ @Nullable RecordInfo record,
@Nullable String transitiveJar) {
this.access = access;
this.name = name;
@@ -68,6 +74,9 @@
this.innerClasses = innerClasses;
this.typeAnnotations = typeAnnotations;
this.module = module;
+ this.nestHost = nestHost;
+ this.nestMembers = nestMembers;
+ this.record = record;
this.transitiveJar = transitiveJar;
}
@@ -126,6 +135,18 @@
return module;
}
+ public @Nullable String nestHost() {
+ return nestHost;
+ }
+
+ public ImmutableList<String> nestMembers() {
+ return nestMembers;
+ }
+
+ public @Nullable RecordInfo record() {
+ return record;
+ }
+
/** The original jar of a repackaged transitive class. */
public @Nullable String transitiveJar() {
return transitiveJar;
@@ -937,4 +958,61 @@
}
}
}
+
+ /** A JVMS §4.7.30 Record attribute. */
+ public static class RecordInfo {
+
+ /** A JVMS §4.7.30 Record component attribute. */
+ public static class RecordComponentInfo {
+
+ private final String name;
+ private final String descriptor;
+ private final @Nullable String signature;
+ private final ImmutableList<AnnotationInfo> annotations;
+ private final ImmutableList<TypeAnnotationInfo> typeAnnotations;
+
+ public RecordComponentInfo(
+ String name,
+ String descriptor,
+ @Nullable String signature,
+ ImmutableList<AnnotationInfo> annotations,
+ ImmutableList<TypeAnnotationInfo> typeAnnotations) {
+ this.name = name;
+ this.descriptor = descriptor;
+ this.signature = signature;
+ this.annotations = annotations;
+ this.typeAnnotations = typeAnnotations;
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public String descriptor() {
+ return descriptor;
+ }
+
+ public @Nullable String signature() {
+ return signature;
+ }
+
+ public ImmutableList<AnnotationInfo> annotations() {
+ return annotations;
+ }
+
+ public ImmutableList<TypeAnnotationInfo> typeAnnotations() {
+ return typeAnnotations;
+ }
+ }
+
+ public RecordInfo(ImmutableList<RecordComponentInfo> recordComponents) {
+ this.recordComponents = recordComponents;
+ }
+
+ private final ImmutableList<RecordComponentInfo> recordComponents;
+
+ public ImmutableList<RecordComponentInfo> recordComponents() {
+ return recordComponents;
+ }
+ }
}
diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java
index e3ade24..6518230 100644
--- a/java/com/google/turbine/bytecode/ClassReader.java
+++ b/java/com/google/turbine/bytecode/ClassReader.java
@@ -148,6 +148,9 @@
innerclasses,
ImmutableList.of(),
module,
+ /* nestHost= */ null,
+ /* nestMembers= */ ImmutableList.of(),
+ /* record= */ null,
transitiveJar);
}
diff --git a/java/com/google/turbine/bytecode/ClassWriter.java b/java/com/google/turbine/bytecode/ClassWriter.java
index de975f2..8b17e04 100644
--- a/java/com/google/turbine/bytecode/ClassWriter.java
+++ b/java/com/google/turbine/bytecode/ClassWriter.java
@@ -31,10 +31,6 @@
private static final int MAGIC = 0xcafebabe;
private static final int MINOR_VERSION = 0;
- // use the lowest classfile version possible given the class file features
- // TODO(cushon): is there a reason to support --release?
- private static final int MAJOR_VERSION = 52;
- private static final int MODULE_MAJOR_VERSION = 53;
/** Writes a {@link ClassFile} to bytecode. */
public static byte[] writeClass(ClassFile classfile) {
@@ -79,7 +75,7 @@
ConstantPool pool, ByteArrayDataOutput body, List<Attribute> attributes) {
body.writeShort(attributes.size());
for (Attribute attribute : attributes) {
- new AttributeWriter(pool, body).write(attribute);
+ new AttributeWriter(pool).write(body, attribute);
}
}
@@ -119,11 +115,25 @@
ByteArrayDataOutput result = ByteStreams.newDataOutput();
result.writeInt(MAGIC);
result.writeShort(MINOR_VERSION);
- result.writeShort(classfile.module() != null ? MODULE_MAJOR_VERSION : MAJOR_VERSION);
+ result.writeShort(majorVersion(classfile));
writeConstantPool(pool, result);
result.write(body.toByteArray());
return result.toByteArray();
}
+ // use the lowest classfile version possible given the class file features
+ // TODO(cushon): is there a reason to support --release?
+ private static int majorVersion(ClassFile classfile) {
+ if (classfile.nestHost() != null
+ || !classfile.nestMembers().isEmpty()
+ || classfile.record() != null) {
+ return 60;
+ }
+ if (classfile.module() != null) {
+ return 53;
+ }
+ return 52;
+ }
+
private ClassWriter() {}
}
diff --git a/java/com/google/turbine/bytecode/LowerAttributes.java b/java/com/google/turbine/bytecode/LowerAttributes.java
index 5ae42af..54937fc 100644
--- a/java/com/google/turbine/bytecode/LowerAttributes.java
+++ b/java/com/google/turbine/bytecode/LowerAttributes.java
@@ -45,12 +45,36 @@
if (classfile.module() != null) {
attributes.add(new Attribute.Module(classfile.module()));
}
+ if (classfile.nestHost() != null) {
+ attributes.add(new Attribute.NestHost(classfile.nestHost()));
+ }
+ if (!classfile.nestMembers().isEmpty()) {
+ attributes.add(new Attribute.NestMembers(classfile.nestMembers()));
+ }
+ if (classfile.record() != null) {
+ attributes.add(recordAttribute(classfile.record()));
+ }
if (classfile.transitiveJar() != null) {
attributes.add(new Attribute.TurbineTransitiveJar(classfile.transitiveJar()));
}
return attributes;
}
+ private static Attribute recordAttribute(ClassFile.RecordInfo record) {
+ ImmutableList.Builder<Attribute.Record.Component> components = ImmutableList.builder();
+ for (ClassFile.RecordInfo.RecordComponentInfo component : record.recordComponents()) {
+ List<Attribute> attributes = new ArrayList<>();
+ if (component.signature() != null) {
+ attributes.add(new Attribute.Signature(component.signature()));
+ }
+ addAllAnnotations(attributes, component.annotations());
+ addAllTypeAnnotations(attributes, component.typeAnnotations());
+ components.add(
+ new Attribute.Record.Component(component.name(), component.descriptor(), attributes));
+ }
+ return new Attribute.Record(components.build());
+ }
+
/** Collects the {@link Attribute}s for a {@link MethodInfo}. */
static List<Attribute> methodAttributes(ClassFile.MethodInfo method) {
List<Attribute> attributes = new ArrayList<>();
diff --git a/java/com/google/turbine/deps/Transitive.java b/java/com/google/turbine/deps/Transitive.java
index 673c372..e80484b 100644
--- a/java/com/google/turbine/deps/Transitive.java
+++ b/java/com/google/turbine/deps/Transitive.java
@@ -105,6 +105,9 @@
innerClasses.build(),
cf.typeAnnotations(),
/* module= */ null,
+ /* nestHost= */ null,
+ /* nestMembers= */ ImmutableList.of(),
+ /* record= */ null,
/* transitiveJar = */ transitiveJar);
}
diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java
index ad1be7c..05f0ec2 100644
--- a/java/com/google/turbine/lower/Lower.java
+++ b/java/com/google/turbine/lower/Lower.java
@@ -187,6 +187,9 @@
innerClasses.build(),
/* typeAnnotations= */ ImmutableList.of(),
moduleInfo,
+ /* nestHost= */ null,
+ /* nestMembers= */ ImmutableList.of(),
+ /* record= */ null,
/* transitiveJar= */ null);
symbols.addAll(sig.classes);
return ClassWriter.writeClass(classfile);
@@ -282,6 +285,9 @@
inners,
typeAnnotations,
/* module= */ null,
+ /* nestHost= */ null,
+ /* nestMembers= */ ImmutableList.of(),
+ /* record= */ null,
/* transitiveJar= */ null);
symbols.addAll(sig.classes);
diff --git a/javatests/com/google/turbine/bytecode/ClassWriterTest.java b/javatests/com/google/turbine/bytecode/ClassWriterTest.java
index f488bbe..eceea21 100644
--- a/javatests/com/google/turbine/bytecode/ClassWriterTest.java
+++ b/javatests/com/google/turbine/bytecode/ClassWriterTest.java
@@ -21,6 +21,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.google.common.jimfs.Configuration;
@@ -46,6 +47,7 @@
import org.junit.runners.JUnit4;
import org.objectweb.asm.ModuleVisitor;
import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.RecordComponentVisitor;
@RunWith(JUnit4.class)
public class ClassWriterTest {
@@ -154,4 +156,102 @@
assertThat(AsmUtils.textify(inputBytes, /* skipDebug= */ true))
.isEqualTo(AsmUtils.textify(outputBytes, /* skipDebug= */ true));
}
+
+ @Test
+ public void record() {
+
+ org.objectweb.asm.ClassWriter cw = new org.objectweb.asm.ClassWriter(0);
+
+ cw.visit(
+ Opcodes.V16,
+ Opcodes.ACC_FINAL | Opcodes.ACC_SUPER | Opcodes.ACC_RECORD,
+ "R",
+ /* signature= */ null,
+ "java/lang/Record",
+ /* interfaces= */ null);
+
+ RecordComponentVisitor rv =
+ cw.visitRecordComponent("x", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/Integer;>;");
+ rv.visitAnnotation("LA;", true);
+ rv.visitTypeAnnotation(318767104, null, "LA;", true);
+ cw.visitRecordComponent("y", "I", null);
+
+ byte[] expectedBytes = cw.toByteArray();
+
+ ClassFile classFile =
+ new ClassFile(
+ /* access= */ Opcodes.ACC_FINAL | Opcodes.ACC_SUPER | Opcodes.ACC_RECORD,
+ /* name= */ "R",
+ /* signature= */ null,
+ /* superClass= */ "java/lang/Record",
+ /* interfaces= */ ImmutableList.of(),
+ /* methods= */ ImmutableList.of(),
+ /* fields= */ ImmutableList.of(),
+ /* annotations= */ ImmutableList.of(),
+ /* innerClasses= */ ImmutableList.of(),
+ /* typeAnnotations= */ ImmutableList.of(),
+ /* module= */ null,
+ /* nestHost= */ null,
+ /* nestMembers= */ ImmutableList.of(),
+ /* record= */ new ClassFile.RecordInfo(
+ ImmutableList.of(
+ new ClassFile.RecordInfo.RecordComponentInfo(
+ "x",
+ "Ljava/util/List;",
+ "Ljava/util/List<Ljava/lang/Integer;>;",
+ ImmutableList.of(
+ new ClassFile.AnnotationInfo("LA;", true, ImmutableMap.of())),
+ ImmutableList.of(
+ new ClassFile.TypeAnnotationInfo(
+ ClassFile.TypeAnnotationInfo.TargetType.FIELD,
+ ClassFile.TypeAnnotationInfo.EMPTY_TARGET,
+ ClassFile.TypeAnnotationInfo.TypePath.root(),
+ new ClassFile.AnnotationInfo("LA;", true, ImmutableMap.of())))),
+ new ClassFile.RecordInfo.RecordComponentInfo(
+ "y", "I", null, ImmutableList.of(), ImmutableList.of()))),
+ /* transitiveJar= */ null);
+
+ byte[] actualBytes = ClassWriter.writeClass(classFile);
+
+ assertThat(AsmUtils.textify(actualBytes, /* skipDebug= */ true))
+ .isEqualTo(AsmUtils.textify(expectedBytes, /* skipDebug= */ true));
+ }
+
+ @Test
+ public void nestHost() {
+
+ org.objectweb.asm.ClassWriter cw = new org.objectweb.asm.ClassWriter(0);
+
+ cw.visit(Opcodes.V16, Opcodes.ACC_SUPER, "N", null, null, null);
+
+ cw.visitNestHost("H");
+ cw.visitNestMember("A");
+ cw.visitNestMember("B");
+ cw.visitNestMember("C");
+
+ byte[] expectedBytes = cw.toByteArray();
+
+ ClassFile classFile =
+ new ClassFile(
+ /* access= */ Opcodes.ACC_SUPER,
+ /* name= */ "N",
+ /* signature= */ null,
+ /* superClass= */ null,
+ /* interfaces= */ ImmutableList.of(),
+ /* methods= */ ImmutableList.of(),
+ /* fields= */ ImmutableList.of(),
+ /* annotations= */ ImmutableList.of(),
+ /* innerClasses= */ ImmutableList.of(),
+ /* typeAnnotations= */ ImmutableList.of(),
+ /* module= */ null,
+ /* nestHost= */ "H",
+ /* nestMembers= */ ImmutableList.of("A", "B", "C"),
+ /* record= */ null,
+ /* transitiveJar= */ null);
+
+ byte[] actualBytes = ClassWriter.writeClass(classFile);
+
+ assertThat(AsmUtils.textify(actualBytes, /* skipDebug= */ true))
+ .isEqualTo(AsmUtils.textify(expectedBytes, /* skipDebug= */ true));
+ }
}