Skip to content

Add support for annotations to TypeTable #5485

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import java.util.zip.ZipException;

import static java.util.Collections.emptyList;
import static java.util.Objects.requireNonNull;
import static org.objectweb.asm.ClassReader.SKIP_CODE;
import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
import static org.objectweb.asm.Opcodes.V1_8;
Expand All @@ -66,6 +67,7 @@
* <li>signature</li>
* <li>parameterNames</li>
* <li>exceptions[]</li>
* <li>annotations[]</li>
* </ul>
* <p>
* Descriptor and signature are in <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3">JVMS 4.3</a> format.
Expand Down Expand Up @@ -208,7 +210,8 @@ public void read(InputStream is, Collection<String> artifactNames) throws IOExce
name,
fields[5].isEmpty() ? null : fields[5],
fields[6].isEmpty() ? null : fields[6],
fields[7].isEmpty() ? null : fields[7].split("\\|")
fields[7].isEmpty() ? null : fields[7].split("\\|"),
fields.length > 14 && !fields[14].isEmpty() ? fields[14].split("\\|") : null
));
int lastIndexOf$ = className.lastIndexOf('$');
if (lastIndexOf$ != -1) {
Expand All @@ -225,7 +228,8 @@ public void read(InputStream is, Collection<String> artifactNames) throws IOExce
fields[10],
fields[11].isEmpty() ? null : fields[11],
fields[12].isEmpty() ? null : fields[12].split("\\|"),
fields[13].isEmpty() ? null : fields[13].split("\\|")
fields[13].isEmpty() ? null : fields[13].split("\\|"),
fields.length > 14 && !fields[14].isEmpty() ? fields[14].split("\\|") : null
));
}
}
Expand Down Expand Up @@ -267,6 +271,13 @@ private void writeClassesDir(@Nullable GroupArtifactVersion gav, Map<String, Cla
classDef.getSuperinterfaceSignatures()
);

// Apply annotations to the class
if (classDef.getAnnotations() != null) {
for (String annotation : classDef.getAnnotations()) {
AnnotationApplier.applyAnnotation(annotation, classWriter::visitAnnotation);
}
}

for (ClassDefinition innerClassDef : nestedTypesByOwner.getOrDefault(classDef.getName(), emptyList())) {
classWriter.visitInnerClass(
innerClassDef.getName(),
Expand All @@ -277,7 +288,7 @@ private void writeClassesDir(@Nullable GroupArtifactVersion gav, Map<String, Cla
}

for (Member member : classDef.getMembers()) {
if (member.getDescriptor().contains("(")) {
if (member.getDescriptor().startsWith("(")) {
MethodVisitor mv = classWriter
.visitMethod(
member.getAccess(),
Expand All @@ -292,18 +303,34 @@ private void writeClassesDir(@Nullable GroupArtifactVersion gav, Map<String, Cla
mv.visitParameter(parameterName, 0);
}
}

// Apply annotations to the method
if (member.getAnnotations() != null) {
for (String annotation : member.getAnnotations()) {
AnnotationApplier.applyAnnotation(annotation, mv::visitAnnotation);
}
}

writeMethodBody(member, mv);
mv.visitEnd();
} else {
classWriter
FieldVisitor fv = classWriter
.visitField(
member.getAccess(),
member.getName(),
member.getDescriptor(),
member.getSignature(),
null
)
.visitEnd();
);

// Apply annotations to the field
if (member.getAnnotations() != null) {
for (String annotation : member.getAnnotations()) {
AnnotationApplier.applyAnnotation(annotation, fv::visitAnnotation);
}
}

fv.visitEnd();
}
}

Expand All @@ -316,7 +343,6 @@ private void writeClassesDir(@Nullable GroupArtifactVersion gav, Map<String, Cla
future.complete(classesDir);
} catch (Exception e) {
future.completeExceptionally(e);
classesDirByArtifact.remove(gav);
}
}

Expand Down Expand Up @@ -423,7 +449,7 @@ public static class Writer implements AutoCloseable {
public Writer(OutputStream out) throws IOException {
this.deflater = new GZIPOutputStream(out);
this.out = new PrintStream(deflater);
this.out.println("groupId\tartifactId\tversion\tclassAccess\tclassName\tclassSignature\tclassSuperclassSignature\tclassSuperinterfaceSignatures\taccess\tname\tdescriptor\tsignature\tparameterNames\texceptions");
this.out.println("groupId\tartifactId\tversion\tclassAccess\tclassName\tclassSignature\tclassSuperclassSignature\tclassSuperinterfaceSignatures\taccess\tname\tdescriptor\tsignature\tparameterNames\texceptions\tannotations");
}

public Jar jar(String groupId, String artifactId, String version) {
Expand Down Expand Up @@ -453,60 +479,95 @@ public void write(Path jar) {
@Nullable
ClassDefinition classDefinition;

boolean wroteFieldOrMethod;
final List<String> collectedParameterNames = new ArrayList<>();

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
int lastIndexOf$ = name.lastIndexOf('$');
if (lastIndexOf$ != -1 && lastIndexOf$ < name.length() - 1 && !Character.isJavaIdentifierStart(name.charAt(lastIndexOf$ + 1))) {
// skip anonymous subclasses
classDefinition = null;
} else {
classDefinition = new ClassDefinition(Jar.this, access, name, signature, superName, interfaces);
wroteFieldOrMethod = false;
classDefinition = new ClassDefinition(
Jar.this,
access,
name,
signature,
superName,
interfaces
);
super.visit(version, access, name, signature, superName, interfaces);
if (!wroteFieldOrMethod && !"module-info".equals(name)) {
// No fields or methods, which can happen for marker annotations for example
classDefinition.writeClass();
}
}
}

@Override
public @Nullable AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (visible && classDefinition != null) {
return AnnotationCollectorHelper.createCollector(descriptor, requireNonNull(classDefinition).classAnnotations);
}
return null;
}

@Override
public @Nullable FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
if (classDefinition != null) {
wroteFieldOrMethod |= classDefinition
.writeField(access, name, descriptor, signature);
Writer.Member member = new Writer.Member(access, name, descriptor, signature, null);
return new FieldVisitor(Opcodes.ASM9) {
@Override
public @Nullable AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (visible) {
return AnnotationCollectorHelper.createCollector(descriptor, member.annotations);
}
return null;
}

@Override
public void visitEnd() {
classDefinition.addField(member);
}
};
}

return null;
}

@Override
public @Nullable MethodVisitor visitMethod(int access, @Nullable String name, String descriptor,
String signature, String[] exceptions) {
@Nullable String signature, String @Nullable [] exceptions) {
// Repeating check from `writeMethod()` for performance reasons
if (classDefinition != null && ((Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC) & access) == 0 &&
name != null && !"<clinit>".equals(name)) {
Writer.Member member = new Writer.Member(access, name, descriptor, signature, exceptions);
return new MethodVisitor(Opcodes.ASM9) {
@Override
public void visitParameter(@Nullable String name, int access) {
if (name != null) {
collectedParameterNames.add(name);
member.parameterNames.add(name);
}
}

@Override
public @Nullable AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
if (visible) {
return AnnotationCollectorHelper.createCollector(descriptor, member.annotations);
}
return null;
}

@Override
public void visitEnd() {
wroteFieldOrMethod |= classDefinition
.writeMethod(access, name, descriptor, signature, collectedParameterNames.isEmpty() ? null : collectedParameterNames, exceptions);
collectedParameterNames.clear();
classDefinition.addMethod(member);
}
};
}
return null;
}

@Override
public void visitEnd() {
if (classDefinition != null && !"module-info".equals(classDefinition.className)) {
// No fields or methods, which can happen for marker annotations for example
classDefinition.writeClass();
}
}
}, SKIP_CODE);
}
}
Expand All @@ -518,55 +579,78 @@ public void visitEnd() {
}

@Value
public class ClassDefinition {
class ClassDefinition {
Jar jar;
int classAccess;
String className;

@Nullable
String classSignature;

@Nullable
String classSuperclassName;

String @Nullable [] classSuperinterfaceSignatures;

List<String> classAnnotations = new ArrayList<>(4);
List<Writer.Member> members = new ArrayList<>();

public void writeClass() {
if (((Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC) & classAccess) == 0) {
out.printf(
"%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s%n",
"%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s%n",
jar.groupId, jar.artifactId, jar.version,
classAccess, className,
classSignature == null ? "" : classSignature,
classSuperclassName,
classSuperinterfaceSignatures == null ? "" : String.join("|", classSuperinterfaceSignatures),
-1, "", "", "", "", "");
-1, "", "", "", "", "",
classAnnotations.isEmpty() ? "" : String.join("|", classAnnotations));

for (Writer.Member member : members) {
member.writeMember(jar, this);
}
}
}

public boolean writeMethod(int access, @Nullable String name, String descriptor,
@Nullable String signature,
@Nullable List<String> parameterNames,
String @Nullable [] exceptions) {
if (((Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC) & access) == 0 && name != null && !name.equals("<clinit>")) {
void addMethod(Writer.Member member) {
members.add(member);
}

void addField(Writer.Member member) {
members.add(member);
}
}

@Value
private class Member {
int access;
String name;
String descriptor;

@Nullable
String signature;

String @Nullable [] exceptions;
List<String> parameterNames = new ArrayList<>(4);
List<String> annotations = new ArrayList<>(4);

private void writeMember(Jar jar, ClassDefinition classDefinition) {
if (((Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC) & access) == 0) {
out.printf(
"%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s%n",
"%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s%n",
jar.groupId, jar.artifactId, jar.version,
classAccess, className,
classSignature == null ? "" : classSignature,
classSuperclassName,
classSuperinterfaceSignatures == null ? "" : String.join("|", classSuperinterfaceSignatures),
classDefinition.classAccess, classDefinition.className,
classDefinition.classSignature == null ? "" : classDefinition.classSignature,
classDefinition.classSuperclassName,
classDefinition.classSuperinterfaceSignatures == null ? "" : String.join("|", classDefinition.classSuperinterfaceSignatures),
access, name, descriptor,
signature == null ? "" : signature,
parameterNames == null ? "" : String.join("|", parameterNames),
exceptions == null ? "" : String.join("|", exceptions)
parameterNames.isEmpty() ? "" : String.join("|", parameterNames),
exceptions == null ? "" : String.join("|", exceptions),
annotations.isEmpty() ? "" : String.join("|", annotations)
);
return true;
}
return false;
}

public boolean writeField(int access, String name, String descriptor, @Nullable String signature) {
// Fits into the same table structure
return writeMethod(access, name, descriptor, signature, null, null);
}
}
}
Expand All @@ -592,6 +676,8 @@ private static class ClassDefinition {

String @Nullable [] superinterfaceSignatures;

String @Nullable [] annotations;

@NonFinal
@Nullable
@ToString.Exclude
Expand Down Expand Up @@ -621,5 +707,6 @@ private static class Member {

String @Nullable [] parameterNames;
String @Nullable [] exceptions;
String @Nullable [] annotations;
}
}
Loading
Loading