[java-decompiler] Avoid annotation duplication when annotation has multiple targets

Fixes a bug where type use annotations where duplicated when they were also applicable to other targets.

GitOrigin-RevId: 8a2373a0423dd31691b83509128bb6065ff71905
This commit is contained in:
Bart van Helvert
2022-01-24 10:42:33 +01:00
committed by intellij-monorepo-bot
parent 999fe6d746
commit f733e9c8fa
34 changed files with 358 additions and 341 deletions

View File

@@ -285,108 +285,6 @@ public class ClassWriter {
return false;
}
public static void packageInfoToJava(StructClass cl, TextBuffer buffer) {
appendAnnotations(buffer, 0, cl);
int index = cl.qualifiedName.lastIndexOf('/');
String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');
buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator();
}
public static void moduleInfoToJava(StructClass cl, TextBuffer buffer) {
appendAnnotations(buffer, 0, cl);
StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);
if ((moduleAttribute.moduleFlags & CodeConstants.ACC_OPEN) != 0) {
buffer.append("open ");
}
buffer.append("module ").append(moduleAttribute.moduleName).append(" {").appendLineSeparator();
writeModuleInfoBody(buffer, moduleAttribute);
buffer.append('}').appendLineSeparator();
}
private static void writeModuleInfoBody(TextBuffer buffer, StructModuleAttribute moduleAttribute) {
boolean newLineNeeded = false;
List<StructModuleAttribute.RequiresEntry> requiresEntries = moduleAttribute.requires;
if (!requiresEntries.isEmpty()) {
for (StructModuleAttribute.RequiresEntry requires : requiresEntries) {
if (!isGenerated(requires.flags)) {
buffer.appendIndent(1).append("requires ").append(requires.moduleName.replace('/', '.')).append(';').appendLineSeparator();
newLineNeeded = true;
}
}
}
List<StructModuleAttribute.ExportsEntry> exportsEntries = moduleAttribute.exports;
if (!exportsEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (StructModuleAttribute.ExportsEntry exports : exportsEntries) {
if (!isGenerated(exports.flags)) {
buffer.appendIndent(1).append("exports ").append(exports.packageName.replace('/', '.'));
List<String> exportToModules = exports.exportToModules;
if (exportToModules.size() > 0) {
buffer.append(" to").appendLineSeparator();
appendFQClassNames(buffer, exportToModules);
}
buffer.append(';').appendLineSeparator();
newLineNeeded = true;
}
}
}
List<StructModuleAttribute.OpensEntry> opensEntries = moduleAttribute.opens;
if (!opensEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (StructModuleAttribute.OpensEntry opens : opensEntries) {
if (!isGenerated(opens.flags)) {
buffer.appendIndent(1).append("opens ").append(opens.packageName.replace('/', '.'));
List<String> opensToModules = opens.opensToModules;
if (opensToModules.size() > 0) {
buffer.append(" to").appendLineSeparator();
appendFQClassNames(buffer, opensToModules);
}
buffer.append(';').appendLineSeparator();
newLineNeeded = true;
}
}
}
List<String> usesEntries = moduleAttribute.uses;
if (!usesEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (String uses : usesEntries) {
buffer.appendIndent(1).append("uses ").append(ExprProcessor.buildJavaClassName(uses)).append(';').appendLineSeparator();
}
newLineNeeded = true;
}
List<StructModuleAttribute.ProvidesEntry> providesEntries = moduleAttribute.provides;
if (!providesEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (StructModuleAttribute.ProvidesEntry provides : providesEntries) {
buffer.appendIndent(1).append("provides ").append(ExprProcessor.buildJavaClassName(provides.interfaceName)).append(" with").appendLineSeparator();
appendFQClassNames(buffer, provides.implementationNames.stream().map(ExprProcessor::buildJavaClassName).collect(Collectors.toList()));
buffer.append(';').appendLineSeparator();
}
}
}
private static boolean isGenerated(int flags) {
return (flags & (CodeConstants.ACC_SYNTHETIC | CodeConstants.ACC_MANDATED)) != 0;
}
private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) {
StructLineNumberTableAttribute table = method.getAttribute(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE);
tracer.setLineNumberTable(table);
String key = InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor());
DecompilerContext.getBytecodeSourceMapper().addTracer(cls.qualifiedName, key, tracer);
}
private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent) {
if (node.type == ClassNode.CLASS_ANONYMOUS) {
buffer.append(" {").appendLineSeparator();
@@ -416,7 +314,7 @@ public class ClassWriter {
appendComment(buffer, "synthetic class", indent);
}
appendAnnotations(buffer, indent, cl);
appendAnnotations(buffer, 0, indent, cl);
buffer.appendIndent(indent);
@@ -548,13 +446,6 @@ public class ClassWriter {
buffer.append('{').appendLineSeparator();
}
private static boolean isVarArgRecord(StructClass cl) {
String canonicalConstructorDescriptor =
cl.getRecordComponents().stream().map(StructField::getDescriptor).collect(Collectors.joining("", "(", ")V"));
StructMethod init = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor);
return init != null && init.hasModifier(CodeConstants.ACC_VARARGS);
}
private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {
int start = buffer.length();
boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE);
@@ -577,7 +468,7 @@ public class ClassWriter {
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);
VarType fieldType = fieldTypeData.getKey();
appendAnnotations(buffer, indent, fd);
appendAnnotations(buffer, fieldType.arrayDim, indent, fd);
buffer.appendIndent(indent);
@@ -642,138 +533,82 @@ public class ClassWriter {
}
}
private static void recordComponentToJava(StructRecordComponent cd, TextBuffer buffer, boolean varArgComponent) {
appendAnnotations(buffer, -1, cd);
private static void writeModuleInfoBody(TextBuffer buffer, StructModuleAttribute moduleAttribute) {
boolean newLineNeeded = false;
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(cd);
VarType fieldType = fieldTypeData.getKey();
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
final List<TypeAnnotationWriteHelper> typeAnnwriteHelper = createTypeAnnWriteHelper(cd);
if (descriptor != null) {
buffer.append(GenericMain.getGenericCastTypeName(varArgComponent ? descriptor.type.decreaseArrayDim() : descriptor.type, typeAnnwriteHelper));
}
else {
buffer.append(ExprProcessor.getCastTypeName(varArgComponent ? fieldType.decreaseArrayDim() : fieldType, typeAnnwriteHelper));
}
if (varArgComponent) {
buffer.append("...");
}
buffer.append(' ');
buffer.append(cd.getName());
}
private static void methodLambdaToJava(ClassNode lambdaNode,
ClassWrapper classWrapper,
StructMethod mt,
TextBuffer buffer,
int indent,
boolean codeOnly, BytecodeMappingTracer tracer) {
MethodWrapper methodWrapper = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());
MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper);
try {
String method_name = lambdaNode.lambdaInformation.method_name;
MethodDescriptor md_content = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.content_method_descriptor);
MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.method_descriptor);
if (!codeOnly) {
buffer.appendIndent(indent);
buffer.append("public ");
buffer.append(method_name);
buffer.append("(");
boolean firstParameter = true;
int index = lambdaNode.lambdaInformation.is_content_method_static ? 0 : 1;
int start_index = md_content.params.length - md_lambda.params.length;
for (int i = 0; i < md_content.params.length; i++) {
if (i >= start_index) {
if (!firstParameter) {
buffer.append(", ");
}
String typeName = ExprProcessor.getCastTypeName(md_content.params[i].copy(), Collections.emptyList());
if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) &&
DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {
typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, Collections.emptyList());
}
buffer.append(typeName);
buffer.append(" ");
String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0));
buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors
firstParameter = false;
}
index += md_content.params[i].stackSize;
}
buffer.append(") {").appendLineSeparator();
indent += 1;
}
RootStatement root = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root;
if (!methodWrapper.decompiledWithErrors) {
if (root != null) { // check for existence
try {
buffer.append(root.toJava(indent, tracer));
}
catch (Throwable t) {
String message = "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t);
methodWrapper.decompiledWithErrors = true;
}
List<StructModuleAttribute.RequiresEntry> requiresEntries = moduleAttribute.requires;
if (!requiresEntries.isEmpty()) {
for (StructModuleAttribute.RequiresEntry requires : requiresEntries) {
if (!isGenerated(requires.flags)) {
buffer.appendIndent(1).append("requires ").append(requires.moduleName.replace('/', '.')).append(';').appendLineSeparator();
newLineNeeded = true;
}
}
}
if (methodWrapper.decompiledWithErrors) {
buffer.appendIndent(indent);
buffer.append("// $FF: Couldn't be decompiled");
buffer.appendLineSeparator();
}
if (root != null) {
tracer.addMapping(root.getDummyExit().bytecode);
}
if (!codeOnly) {
indent -= 1;
buffer.appendIndent(indent).append('}').appendLineSeparator();
List<StructModuleAttribute.ExportsEntry> exportsEntries = moduleAttribute.exports;
if (!exportsEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (StructModuleAttribute.ExportsEntry exports : exportsEntries) {
if (!isGenerated(exports.flags)) {
buffer.appendIndent(1).append("exports ").append(exports.packageName.replace('/', '.'));
List<String> exportToModules = exports.exportToModules;
if (exportToModules.size() > 0) {
buffer.append(" to").appendLineSeparator();
appendFQClassNames(buffer, exportToModules);
}
buffer.append(';').appendLineSeparator();
newLineNeeded = true;
}
}
}
finally {
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper);
List<StructModuleAttribute.OpensEntry> opensEntries = moduleAttribute.opens;
if (!opensEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (StructModuleAttribute.OpensEntry opens : opensEntries) {
if (!isGenerated(opens.flags)) {
buffer.appendIndent(1).append("opens ").append(opens.packageName.replace('/', '.'));
List<String> opensToModules = opens.opensToModules;
if (opensToModules.size() > 0) {
buffer.append(" to").appendLineSeparator();
appendFQClassNames(buffer, opensToModules);
}
buffer.append(';').appendLineSeparator();
newLineNeeded = true;
}
}
}
List<String> usesEntries = moduleAttribute.uses;
if (!usesEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (String uses : usesEntries) {
buffer.appendIndent(1).append("uses ").append(ExprProcessor.buildJavaClassName(uses)).append(';').appendLineSeparator();
}
newLineNeeded = true;
}
List<StructModuleAttribute.ProvidesEntry> providesEntries = moduleAttribute.provides;
if (!providesEntries.isEmpty()) {
if (newLineNeeded) buffer.appendLineSeparator();
for (StructModuleAttribute.ProvidesEntry provides : providesEntries) {
buffer.appendIndent(1).append("provides ").append(ExprProcessor.buildJavaClassName(provides.interfaceName)).append(" with").appendLineSeparator();
appendFQClassNames(buffer, provides.implementationNames.stream().map(ExprProcessor::buildJavaClassName).collect(Collectors.toList()));
buffer.append(';').appendLineSeparator();
}
}
}
private static String toValidJavaIdentifier(String name) {
if (name == null || name.isEmpty()) return name;
private static boolean isGenerated(int flags) {
return (flags & (CodeConstants.ACC_SYNTHETIC | CodeConstants.ACC_MANDATED)) != 0;
}
boolean changed = false;
StringBuilder res = new StringBuilder(name.length());
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if ((i == 0 && !Character.isJavaIdentifierStart(c))
|| (i > 0 && !Character.isJavaIdentifierPart(c))) {
changed = true;
res.append("_");
}
else {
res.append(c);
}
}
if (!changed) {
return name;
}
return res.append("/* $FF was: ").append(name).append("*/").toString();
private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) {
StructLineNumberTableAttribute table = method.getAttribute(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE);
tracer.setLineNumberTable(table);
String key = InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor());
DecompilerContext.getBytecodeSourceMapper().addTracer(cls.qualifiedName, key, tracer);
}
private boolean methodToJava(ClassNode node, StructMethod mt, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {
@@ -822,7 +657,31 @@ public class ClassWriter {
appendComment(buffer, "bridge method", indent);
}
appendAnnotations(buffer, indent, mt);
GenericMethodDescriptor descriptor = null;
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
StructGenericSignatureAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);
if (attr != null) {
descriptor = GenericMain.parseMethodSignature(attr.getSignature());
if (descriptor != null) {
long actualParams = md.params.length;
List<VarVersionPair> mask = methodWrapper.synthParameters;
if (mask != null) {
actualParams = mask.stream().filter(Objects::isNull).count();
}
else if (isEnum && init) {
actualParams -= 2;
}
if (actualParams != descriptor.parameterTypes.size()) {
String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor() + " in " + cl.qualifiedName;
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
descriptor = null;
}
}
}
}
int arrayDim = 0;
if (descriptor != null) arrayDim = descriptor.returnType.arrayDim;
appendAnnotations(buffer, arrayDim, indent, mt);
buffer.appendIndent(indent);
@@ -849,29 +708,6 @@ public class ClassWriter {
clInit = true;
}
GenericMethodDescriptor descriptor = null;
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
StructGenericSignatureAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);
if (attr != null) {
descriptor = GenericMain.parseMethodSignature(attr.getSignature());
if (descriptor != null) {
long actualParams = md.params.length;
List<VarVersionPair> mask = methodWrapper.synthParameters;
if (mask != null) {
actualParams = mask.stream().filter(Objects::isNull).count();
}
else if (isEnum && init) {
actualParams -= 2;
}
if (actualParams != descriptor.parameterTypes.size()) {
String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor() + " in " + cl.qualifiedName;
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
descriptor = null;
}
}
}
}
boolean throwsExceptions = false;
int paramCount = 0;
final List<TypeAnnotationWriteHelper> typeAnnwriteHelper = createTypeAnnWriteHelper(mt);
@@ -1076,6 +912,171 @@ public class ClassWriter {
return !hideMethod;
}
private static boolean isVarArgRecord(StructClass cl) {
String canonicalConstructorDescriptor =
cl.getRecordComponents().stream().map(StructField::getDescriptor).collect(Collectors.joining("", "(", ")V"));
StructMethod init = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor);
return init != null && init.hasModifier(CodeConstants.ACC_VARARGS);
}
public static void packageInfoToJava(StructClass cl, TextBuffer buffer) {
appendAnnotations(buffer, 0, 0, cl);
int index = cl.qualifiedName.lastIndexOf('/');
String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');
buffer.append("package ").append(packageName).append(';').appendLineSeparator().appendLineSeparator();
}
public static void moduleInfoToJava(StructClass cl, TextBuffer buffer) {
appendAnnotations(buffer, 0, 0, cl);
StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);
if ((moduleAttribute.moduleFlags & CodeConstants.ACC_OPEN) != 0) {
buffer.append("open ");
}
buffer.append("module ").append(moduleAttribute.moduleName).append(" {").appendLineSeparator();
writeModuleInfoBody(buffer, moduleAttribute);
buffer.append('}').appendLineSeparator();
}
private static void methodLambdaToJava(ClassNode lambdaNode,
ClassWrapper classWrapper,
StructMethod mt,
TextBuffer buffer,
int indent,
boolean codeOnly, BytecodeMappingTracer tracer) {
MethodWrapper methodWrapper = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());
MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper);
try {
String method_name = lambdaNode.lambdaInformation.method_name;
MethodDescriptor md_content = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.content_method_descriptor);
MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.method_descriptor);
if (!codeOnly) {
buffer.appendIndent(indent);
buffer.append("public ");
buffer.append(method_name);
buffer.append("(");
boolean firstParameter = true;
int index = lambdaNode.lambdaInformation.is_content_method_static ? 0 : 1;
int start_index = md_content.params.length - md_lambda.params.length;
for (int i = 0; i < md_content.params.length; i++) {
if (i >= start_index) {
if (!firstParameter) {
buffer.append(", ");
}
String typeName = ExprProcessor.getCastTypeName(md_content.params[i].copy(), Collections.emptyList());
if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) &&
DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {
typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, Collections.emptyList());
}
buffer.append(typeName);
buffer.append(" ");
String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0));
buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors
firstParameter = false;
}
index += md_content.params[i].stackSize;
}
buffer.append(") {").appendLineSeparator();
indent += 1;
}
RootStatement root = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root;
if (!methodWrapper.decompiledWithErrors) {
if (root != null) { // check for existence
try {
buffer.append(root.toJava(indent, tracer));
}
catch (Throwable t) {
String message = "Method " + mt.getName() + " " + mt.getDescriptor() + " couldn't be written.";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t);
methodWrapper.decompiledWithErrors = true;
}
}
}
if (methodWrapper.decompiledWithErrors) {
buffer.appendIndent(indent);
buffer.append("// $FF: Couldn't be decompiled");
buffer.appendLineSeparator();
}
if (root != null) {
tracer.addMapping(root.getDummyExit().bytecode);
}
if (!codeOnly) {
indent -= 1;
buffer.appendIndent(indent).append('}').appendLineSeparator();
}
}
finally {
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper);
}
}
private static String toValidJavaIdentifier(String name) {
if (name == null || name.isEmpty()) return name;
boolean changed = false;
StringBuilder res = new StringBuilder(name.length());
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if ((i == 0 && !Character.isJavaIdentifierStart(c))
|| (i > 0 && !Character.isJavaIdentifierPart(c))) {
changed = true;
res.append("_");
}
else {
res.append(c);
}
}
if (!changed) {
return name;
}
return res.append("/* $FF was: ").append(name).append("*/").toString();
}
private static void recordComponentToJava(StructRecordComponent cd, TextBuffer buffer, boolean varArgComponent) {
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(cd);
VarType fieldType = fieldTypeData.getKey();
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
appendAnnotations(buffer, fieldType.arrayDim, -1, cd);
final List<TypeAnnotationWriteHelper> typeAnnwriteHelper = createTypeAnnWriteHelper(cd);
if (descriptor != null) {
buffer.append(GenericMain.getGenericCastTypeName(varArgComponent ? descriptor.type.decreaseArrayDim() : descriptor.type, typeAnnwriteHelper));
}
else {
buffer.append(ExprProcessor.getCastTypeName(varArgComponent ? fieldType.decreaseArrayDim() : fieldType, typeAnnwriteHelper));
}
if (varArgComponent) {
buffer.append("...");
}
buffer.append(' ');
buffer.append(cd.getName());
}
private static boolean hideConstructor(
ClassNode node,
boolean hasAnnotation,
@@ -1194,11 +1195,32 @@ public class ClassWriter {
.collect(Collectors.toList());
}
private static void appendAnnotations(TextBuffer buffer, int indent, StructMember mb) {
private static void appendAnnotations(TextBuffer buffer, int arrayDim, int indent, StructMember mb) {
// Check whether annotation will already be written as a type annotation to avoid duplication
final var firstTypeAnnotation = Arrays.stream(StructGeneralAttribute.TYPE_ANNOTATION_ATTRIBUTES)
.flatMap(attrKey -> {
StructTypeAnnotationAttribute attribute = (StructTypeAnnotationAttribute)mb.getAttribute(attrKey);
if (attribute == null) {
return Stream.empty();
} else {
return attribute.getAnnotations().stream();
}
}).filter(typeAnnotation -> {
StructTypePathEntry pathEntry = typeAnnotation.getPaths().stream().findFirst().orElse(null);
if (pathEntry == null && arrayDim == 0) return true;
if (pathEntry != null && pathEntry.getTypePathEntryKind() == StructTypePathEntry.Kind.ARRAY.getOpcode() &&
typeAnnotation.getPaths().size() == arrayDim
) return true;
return false;
}).findFirst();
for (StructGeneralAttribute.Key<?> key : StructGeneralAttribute.ANNOTATION_ATTRIBUTES) {
StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttribute(key);
if (attribute != null) {
for (AnnotationExprent annotation : attribute.getAnnotations()) {
if (firstTypeAnnotation.isPresent() &&
firstTypeAnnotation.get().getAnnotationExpr().getClassName().equals(annotation.getClassName())
) continue;
String text = annotation.toJava(indent, BytecodeMappingTracer.DUMMY).toString();
buffer.append(text);
if (indent < 0) {

View File

@@ -6,5 +6,6 @@ public class ArrayNestedTypeAnnotations {
Z.Y.@C X.W @F [] @A [] @B [] w3;
Z.Y.X.@D W @D [] w4;
@A Z.@B Y.@C X.@D W[][] w5;
@L Z.Y.X.@L W[] @L [] w6;
}

View File

@@ -17,6 +17,10 @@ public class ArrayTypeAnnotations implements ParentInterface {
@A String @B [] @C [] @D [] s7() {
return null;// 10
}
@L String @L [][] @L [] s8() {
return null;// 11
}
}
class 'typeAnnotations/ArrayTypeAnnotations' {
@@ -34,9 +38,15 @@ class 'typeAnnotations/ArrayTypeAnnotations' {
0 17
1 17
}
method 's8 ()[[[Ljava/lang/String;' {
0 21
1 21
}
}
Lines mapping:
8 <-> 10
9 <-> 14
10 <-> 18
11 <-> 22

View File

@@ -1,5 +1,5 @@
package typeAnnotations;
public class ClassSuperTypeAnnotations extends @A Foo implements @B Bar, @B BarGeneric<@F String, @A String @B []> {
public class ClassSuperTypeAnnotations extends @L @A Foo implements @B Bar, @B BarGeneric<@F String, @L @A String @B []> {
}

View File

@@ -6,5 +6,6 @@ public class GenericArrayNestedTypeAnnotations {
V.U<String>.@C T<Boolean, Integer, Float> @B [] @D [] t3;
V.U<@D String>.T<Boolean, Integer, Float> @F [] t4;
@B V.@A U<@A String>.@A T<@E Boolean, @F Integer, Float>[] t5;
@L V.U<String>.T<Boolean, Integer, Float>[][] t6;
}

View File

@@ -27,6 +27,10 @@ public class GenericArrayTypeAnnotations {
@A List<@B Object @C [] @D []> @E [] @F [] l2() {
return null;// 28
}
@L List<Object[][]>[] @L [] l3() {
return null;// 32
}
}
class 'typeAnnotations/GenericArrayTypeAnnotations' {
@@ -44,9 +48,15 @@ class 'typeAnnotations/GenericArrayTypeAnnotations' {
0 27
1 27
}
method 'l3 ()[[Ljava/util/List;' {
0 31
1 31
}
}
Lines mapping:
20 <-> 20
24 <-> 24
28 <-> 28
32 <-> 32

View File

@@ -6,5 +6,6 @@ public class GenericNestedTypeAnnotations {
V.U<String>.@C T<Boolean, Integer, Float> t3;
V.U<@D String>.T<Boolean, Integer, Float> t4;
V.U<String>.T<@E Boolean, @F Integer, Float> t5;
@L V.U<String>.T<@L Boolean, @F Integer, Float> t6;
}

View File

@@ -14,7 +14,8 @@ public class GenericTypeAnnotations {
Map<? extends @C @D String, List<Object>> m8;
Map<? extends String, @D @E List<Object>> m9;
Map<? extends String, List<@E @F Object>> m10;
@A Map<@B ? extends @C String, @D List<@E Object>> m11;
@A Map<@B ? extends @C String, @L @D List<@E Object>> m11;
@L Map<? extends String, List<Object>> m13;
@A Map<@A Object, @B List<@C Object>> m12() {
return null;// 18
@@ -27,16 +28,16 @@ public class GenericTypeAnnotations {
class 'typeAnnotations/GenericTypeAnnotations' {
method 'm12 ()Ljava/util/Map;' {
0 19
1 19
0 20
1 20
}
method 'l1 ()Ljava/util/List;' {
0 23
1 23
0 24
1 24
}
}
Lines mapping:
18 <-> 20
19 <-> 24
18 <-> 21
19 <-> 25

View File

@@ -1,5 +1,5 @@
package typeAnnotations;
public interface InterfaceSuperTypeAnnotations extends @B Bar, @B BarGeneric<@F String, @A String @B []> {
public interface InterfaceSuperTypeAnnotations extends @B Bar, @L @B BarGeneric<@F String, @L @A String @B []> {
}

View File

@@ -4,45 +4,47 @@ import java.io.IOException;
import java.io.Serializable;
public class MemberDeclarationTypeAnnotations<@A P extends @B Number & @F Serializable> {
@L String s1 = "";
@B int f1 = 0;
@K
public @A MemberDeclarationTypeAnnotations() {
}// 11
public @L @A MemberDeclarationTypeAnnotations() {
}// 13
@K
public <@A T extends @B Number & @F Serializable> @C Number foo(@D T @E [] a) {
return 0;// 15
public <@A T extends @B Number & @F Serializable> @L @C Number foo(@D T @E [] a) {
return 0;// 17
}
@K
public <T> @C Number bar(@D T @E [] a) throws @A IOException, @B IllegalStateException {
return 0;// 20
return 0;// 22
}
}
class 'typeAnnotations/MemberDeclarationTypeAnnotations' {
method '<init> ()V' {
9 10
f 11
}
method 'foo ([Ljava/lang/Number;)Ljava/lang/Number;' {
0 14
1 14
4 14
0 15
1 15
4 15
}
method 'bar ([Ljava/lang/Object;)Ljava/lang/Number;' {
0 19
1 19
4 19
0 20
1 20
4 20
}
}
Lines mapping:
11 <-> 11
15 <-> 15
20 <-> 20
13 <-> 12
17 <-> 16
22 <-> 21
Not mapped:
7
10
9
12

View File

@@ -6,5 +6,6 @@ public class NestedTypeAnnotations {
Z.Y.@C X.W w3;
Z.Y.X.@D W w4;
@A Z.@B Y.@C X.@D W w5;
@L Z.Y.X.W w6;
}

View File

@@ -6,4 +6,5 @@ public class ArrayNestedTypeAnnotations {
Z.Y.@C X.W @F [] @A [] @B [] w3;
Z.Y.X.@D W @D [] w4;
@A Z.@B Y.@C X.@D W[][] w5;
@L Z. Y.X.@L W[] @L [] w6;
}

View File

@@ -8,4 +8,5 @@ public class ArrayTypeAnnotations implements ParentInterface {
@A String[] s5() { return null; }
String @B [] s6() { return null; }
@A String @B [] @C [] @D [] s7() { return null; }
@L String @L [][] @L [] s8() { return null; }
}

View File

@@ -1,4 +1,4 @@
package typeAnnotations;
public class ClassSuperTypeAnnotations extends @A Foo implements @B Bar, @B BarGeneric<@F String, @A String @B []> {
public class ClassSuperTypeAnnotations extends @L @A Foo implements @B Bar, @B BarGeneric<@F String, @L @A String @B []> {
}

View File

@@ -6,4 +6,5 @@ public class GenericArrayNestedTypeAnnotations {
V.U<String>.@C T<Boolean, Integer, Float> @B [] @D [] t3;
V.U<@D String>.T<Boolean, Integer, Float> @F [] t4;
@B V.@A U<@A String>.@A T<@E Boolean, @F Integer, Float>[] t5;
@L V.U<String>.T<Boolean, Integer, Float>[][] t6;
}

View File

@@ -27,4 +27,8 @@ public class GenericArrayTypeAnnotations {
@A List<@B Object @C [] @D []> @E [] @F [] l2() {
return null;
}
@L List<Object[][]>[] @L [] l3() {
return null;
}
}

View File

@@ -6,4 +6,5 @@ public class GenericNestedTypeAnnotations {
V.U<String>.@C T<Boolean, Integer, Float> t3;
V.U<@D String>.T<Boolean, Integer, Float> t4;
V.U<String>.T<@E Boolean, @F Integer, Float> t5;
@L V.U<String>.T<@L Boolean, @F Integer, Float> t6;
}

View File

@@ -14,7 +14,8 @@ public class GenericTypeAnnotations {
Map<? extends @C @D String, List<Object>> m8;
Map<? extends String, @D @E List<Object>> m9;
Map<? extends String, List<@E @F Object>> m10;
@A Map<@B ? extends @C String, @D List<@E Object>> m11;
@A Map<@B ? extends @C String, @L @D List<@E Object>> m11;
@A Map<@A Object, @B List<@C Object>> m12() { return null; }
@A @B List<@C Object> l1() { return null; }
@L Map<? extends String, List<Object>> m13;
}

View File

@@ -1,4 +1,4 @@
package typeAnnotations;
public interface InterfaceSuperTypeAnnotations extends @B Bar, @B BarGeneric<@F String, @A String @B []> {
public interface InterfaceSuperTypeAnnotations extends @B Bar, @L @B BarGeneric<@F String, @L @A String @B []> {
}

View File

@@ -0,0 +1,9 @@
package typeAnnotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.TYPE_USE, ElementType.FIELD})
public @interface L {
}

View File

@@ -1,53 +0,0 @@
package typeAnnotations;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.function.BiFunction;
public class LocalTypeAnnotations {
// void simple() {
// @A String hello = "Hello";
// @B String space = " ";
// @C String world = "World";
// }
//
// void instanceOf() {
// boolean isobj = "Hello world!" instanceof @B Object;
// }
//
// void newArray() {
// @A String @B [] hw = new @C String @D [0];
// }
//
// void methodRef() {
// BiFunction<String, Integer, Character> stringIntegerCharacterBiFunction = @C String::charAt;
// }
//
// void typeArg() {
// BiFunction<@D String, @E Integer, @F Character> stringIntegerCharacterBiFunction = String::charAt;
// }
//
// void tryWithResources() {
// try (@A BufferedReader br = new @B BufferedReader(new @C FileReader("test"))) {
// } catch (@A FileNotFoundException e) {
// e.printStackTrace();
// } catch (@B IOException e) {
// e.printStackTrace();
// }
// }
void combined() {
@A String hello = "Hello!";
boolean isobj = hello instanceof @B Object;
@C String world = " World!";
BiFunction<String, Integer, Character> stringIntegerCharacterBiFunction = @D String::charAt;
try (BufferedReader br = new BufferedReader(new FileReader("test"))) {
} catch (@E IOException e) {
e.printStackTrace();
}
@F String test = " Test!";
}
}

View File

@@ -4,14 +4,16 @@ import java.io.IOException;
import java.io.Serializable;
public class MemberDeclarationTypeAnnotations<@A P extends @B Number & @F Serializable> {
@L String s1 = "";
@B int f1 = 0;
@K
public @A MemberDeclarationTypeAnnotations() {
public @L @A MemberDeclarationTypeAnnotations() {
}
@K
public <@A T extends @B Number & @F Serializable> @C Number foo(@D T @E [] a) {
public <@A T extends @B Number & @F Serializable> @L @C Number foo(@D T @E [] a) {
return 0;
}

View File

@@ -6,4 +6,5 @@ public class NestedTypeAnnotations {
Z.Y.@C X.W w3;
Z.Y.X.@D W w4;
@A Z.@B Y.@C X.@D W w5;
@L Z.Y.X.W w6;
}