From 52b31bf325fd1ff5006841e5d4b7ce24275718f5 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 21 Apr 2016 21:22:36 +0200 Subject: [PATCH] [java, java-decompiler] type annotations in class files Step 1: add top-level field/method/parameter annotations to stubs; include them in decompiled text. --- .../java/decompiler/main/ClassWriter.java | 65 ++++-- .../decompiler/exps/AnnotationExprent.java | 29 ++- .../decompiler/exps/TypeAnnotation.java | 67 ++++++ .../attr/StructAnnotationAttribute.java | 7 +- .../attr/StructAnnotationTypeAttribute.java | 194 ------------------ .../struct/attr/StructGeneralAttribute.java | 19 +- .../attr/StructTypeAnnotationAttribute.java | 107 ++++++++++ .../java/decompiler/SingleClassesTest.java | 1 + .../classes/pkg/TypeAnnotations$MixA.class | Bin 0 -> 404 bytes testData/classes/pkg/TypeAnnotations$TA.class | Bin 0 -> 387 bytes testData/classes/pkg/TypeAnnotations.class | Bin 0 -> 745 bytes testData/results/TypeAnnotations.dec | 44 ++++ testData/src/pkg/TypeAnnotations.java | 22 ++ 13 files changed, 315 insertions(+), 240 deletions(-) create mode 100644 src/org/jetbrains/java/decompiler/modules/decompiler/exps/TypeAnnotation.java delete mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java create mode 100644 src/org/jetbrains/java/decompiler/struct/attr/StructTypeAnnotationAttribute.java create mode 100644 testData/classes/pkg/TypeAnnotations$MixA.class create mode 100644 testData/classes/pkg/TypeAnnotations$TA.class create mode 100644 testData/classes/pkg/TypeAnnotations.class create mode 100644 testData/results/TypeAnnotations.dec create mode 100644 testData/src/pkg/TypeAnnotations.java diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index cded67e..2386798 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -23,10 +23,7 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.main.rels.ClassWrapper; import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; -import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent; -import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.*; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; @@ -43,9 +40,7 @@ import org.jetbrains.java.decompiler.struct.gen.VarType; import org.jetbrains.java.decompiler.struct.gen.generics.*; import org.jetbrains.java.decompiler.util.InterpreterUtil; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public class ClassWriter { private final PoolInterceptor interceptor; @@ -311,7 +306,7 @@ public class ClassWriter { appendComment(buffer, "synthetic class", indent); } - appendAnnotations(buffer, cl, indent); + appendAnnotations(buffer, indent, cl, -1); buffer.appendIndent(indent); @@ -407,7 +402,7 @@ public class ClassWriter { appendComment(buffer, "synthetic field", indent); } - appendAnnotations(buffer, fd, indent); + appendAnnotations(buffer, indent, fd, TypeAnnotation.FIELD); buffer.appendIndent(indent); @@ -630,7 +625,7 @@ public class ClassWriter { appendComment(buffer, "bridge method", indent); } - appendAnnotations(buffer, mt, indent); + appendAnnotations(buffer, indent, mt, TypeAnnotation.METHOD_RETURN_TYPE); buffer.appendIndent(indent); @@ -816,7 +811,7 @@ public class ClassWriter { StructAnnDefaultAttribute attr = (StructAnnDefaultAttribute)mt.getAttributes().getWithKey("AnnotationDefault"); if (attr != null) { buffer.append(" default "); - buffer.append(attr.getDefaultValue().toJava(0, new BytecodeMappingTracer())); // dummy tracer + buffer.append(attr.getDefaultValue().toJava(0, BytecodeMappingTracer.DUMMY)); } } @@ -951,26 +946,30 @@ public class ClassWriter { private static final String[] ANNOTATION_ATTRIBUTES = { StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS}; + private static final String[] PARAMETER_ANNOTATION_ATTRIBUTES = { + StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; + private static final String[] TYPE_ANNOTATION_ATTRIBUTES = { + StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS, StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS}; - private static void appendAnnotations(TextBuffer buffer, StructMember mb, int indent) { - BytecodeMappingTracer tracer_dummy = new BytecodeMappingTracer(); // FIXME: replace with a real one + private static void appendAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType) { + Set filter = new HashSet<>(); for (String name : ANNOTATION_ATTRIBUTES) { StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttributes().getWithKey(name); if (attribute != null) { for (AnnotationExprent annotation : attribute.getAnnotations()) { - buffer.append(annotation.toJava(indent, tracer_dummy)).appendLineSeparator(); + String text = annotation.toJava(indent, BytecodeMappingTracer.DUMMY).toString(); + filter.add(text); + buffer.append(text).appendLineSeparator(); } } } - } - private static final String[] PARAMETER_ANNOTATION_ATTRIBUTES = { - StructGeneralAttribute.ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, - StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS}; + appendTypeAnnotations(buffer, indent, mb, targetType, -1, filter); + } private static void appendParameterAnnotations(TextBuffer buffer, StructMethod mt, int param) { - BytecodeMappingTracer tracer_dummy = new BytecodeMappingTracer(); // FIXME: replace with a real one + Set filter = new HashSet<>(); for (String name : PARAMETER_ANNOTATION_ATTRIBUTES) { StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute)mt.getAttributes().getWithKey(name); @@ -978,7 +977,33 @@ public class ClassWriter { List> annotations = attribute.getParamAnnotations(); if (param < annotations.size()) { for (AnnotationExprent annotation : annotations.get(param)) { - buffer.append(annotation.toJava(0, tracer_dummy)).append(' '); + String text = annotation.toJava(-1, BytecodeMappingTracer.DUMMY).toString(); + filter.add(text); + buffer.append(text).append(' '); + } + } + } + } + + appendTypeAnnotations(buffer, -1, mt, TypeAnnotation.METHOD_PARAMETER, param, filter); + } + + private static void appendTypeAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType, int index, Set filter) { + for (String name : TYPE_ANNOTATION_ATTRIBUTES) { + StructTypeAnnotationAttribute attribute = (StructTypeAnnotationAttribute)mb.getAttributes().getWithKey(name); + if (attribute != null) { + for (TypeAnnotation annotation : attribute.getAnnotations()) { + if (annotation.isTopLevel() && annotation.getTargetType() == targetType && (index < 0 || annotation.getIndex() == index)) { + String text = annotation.getAnnotation().toJava(indent, BytecodeMappingTracer.DUMMY).toString(); + if (!filter.contains(text)) { + buffer.append(text); + if (indent < 0) { + buffer.append(' '); + } + else { + buffer.appendLineSeparator(); + } + } } } } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java index 9b797a9..c17d921 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java @@ -47,22 +47,31 @@ public class AnnotationExprent extends Exprent { buffer.append('@'); buffer.append(DecompilerContext.getImportCollector().getShortName(ExprProcessor.buildJavaClassName(className))); - if (!parNames.isEmpty()) { + int type = getAnnotationType(); + + if (type != ANNOTATION_MARKER) { buffer.append('('); - if (getAnnotationType() == ANNOTATION_SINGLE_ELEMENT) { - buffer.append(parValues.get(0).toJava(indent + 1, tracer)); - } - else { - for (int i = 0; i < parNames.size(); i++) { + + boolean oneLiner = type == ANNOTATION_SINGLE_ELEMENT || indent < 0; + + for (int i = 0; i < parNames.size(); i++) { + if (!oneLiner) { buffer.appendLineSeparator().appendIndent(indent + 1); + } + + if (type != ANNOTATION_SINGLE_ELEMENT) { buffer.append(parNames.get(i)); buffer.append(" = "); - buffer.append(parValues.get(i).toJava(0, tracer)); + } + + buffer.append(parValues.get(i).toJava(0, tracer)); - if (i < parNames.size() - 1) { - buffer.append(','); - } + if (i < parNames.size() - 1) { + buffer.append(','); } + } + + if (!oneLiner) { buffer.appendLineSeparator().appendIndent(indent); } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/TypeAnnotation.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/TypeAnnotation.java new file mode 100644 index 0000000..93ab87b --- /dev/null +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/TypeAnnotation.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.java.decompiler.modules.decompiler.exps; + +public class TypeAnnotation { + public static final int CLASS_TYPE_PARAMETER = 0x00; + public static final int METHOD_TYPE_PARAMETER = 0x01; + public static final int SUPER_TYPE_REFERENCE = 0x10; + public static final int CLASS_TYPE_PARAMETER_BOUND = 0x11; + public static final int METHOD_TYPE_PARAMETER_BOUND = 0x12; + public static final int FIELD = 0x13; + public static final int METHOD_RETURN_TYPE = 0x14; + public static final int METHOD_RECEIVER = 0x15; + public static final int METHOD_PARAMETER = 0x16; + public static final int THROWS_REFERENCE = 0x17; + public static final int LOCAL_VARIABLE = 0x40; + public static final int RESOURCE_VARIABLE = 0x41; + public static final int CATCH_CLAUSE = 0x42; + public static final int EXPR_INSTANCEOF = 0x43; + public static final int EXPR_NEW = 0x44; + public static final int EXPR_CONSTRUCTOR_REF = 0x45; + public static final int EXPR_METHOD_REF = 0x46; + public static final int TYPE_ARG_CAST = 0x47; + public static final int TYPE_ARG_CONSTRUCTOR_CALL = 0x48; + public static final int TYPE_ARG_METHOD_CALL = 0x49; + public static final int TYPE_ARG_CONSTRUCTOR_REF = 0x4A; + public static final int TYPE_ARG_METHOD_REF = 0x4B; + + private final int target; + private final byte[] path; + private final AnnotationExprent annotation; + + public TypeAnnotation(int target, byte[] path, AnnotationExprent annotation) { + this.target = target; + this.path = path; + this.annotation = annotation; + } + + public int getTargetType() { + return target >> 24; + } + + public int getIndex() { + return target & 0x0FFFF; + } + + public boolean isTopLevel() { + return path == null; + } + + public AnnotationExprent getAnnotation() { + return annotation; + } +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java index 73af88d..1380427 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,6 @@ import java.util.Collections; import java.util.List; public class StructAnnotationAttribute extends StructGeneralAttribute { - private List annotations; @Override @@ -143,7 +142,7 @@ public class StructAnnotationAttribute extends StructGeneralAttribute { newType = new VarType(elementType.type, 1, elementType.value); } - NewExprent newExpr = new NewExprent(newType, Collections.emptyList(), null); + NewExprent newExpr = new NewExprent(newType, Collections.emptyList(), null); newExpr.setDirectArrayInit(true); newExpr.setLstArrayElements(elements); return newExpr; @@ -181,4 +180,4 @@ public class StructAnnotationAttribute extends StructGeneralAttribute { public List getAnnotations() { return annotations; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java deleted file mode 100644 index 59324e3..0000000 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.struct.attr; - -import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; -import org.jetbrains.java.decompiler.struct.consts.ConstantPool; - -import java.io.DataInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class StructAnnotationTypeAttribute extends StructGeneralAttribute { - - private static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS = 0x00; - private static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD = 0x01; - private static final int ANNOTATION_TARGET_TYPE_EXTENDS_IMPLEMENTS = 0x10; - private static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS_BOUND = 0x11; - private static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD_BOUND = 0x12; - private static final int ANNOTATION_TARGET_TYPE_FIELD = 0x13; - private static final int ANNOTATION_TARGET_TYPE_RETURN = 0x14; - private static final int ANNOTATION_TARGET_TYPE_RECEIVER = 0x15; - private static final int ANNOTATION_TARGET_TYPE_FORMAL = 0x16; - private static final int ANNOTATION_TARGET_TYPE_THROWS = 0x17; - private static final int ANNOTATION_TARGET_TYPE_LOCAL_VARIABLE = 0x40; - private static final int ANNOTATION_TARGET_TYPE_RESOURCE_VARIABLE = 0x41; - private static final int ANNOTATION_TARGET_TYPE_EXCEPTION = 0x42; - private static final int ANNOTATION_TARGET_TYPE_INSTANCEOF = 0x43; - private static final int ANNOTATION_TARGET_TYPE_NEW = 0x44; - private static final int ANNOTATION_TARGET_TYPE_DOUBLE_COLON_NEW = 0x45; - private static final int ANNOTATION_TARGET_TYPE_DOUBLE_COLON_ID = 0x46; - private static final int ANNOTATION_TARGET_TYPE_CAST = 0x47; - private static final int ANNOTATION_TARGET_TYPE_INVOCATION_CONSTRUCTOR = 0x48; - private static final int ANNOTATION_TARGET_TYPE_INVOCATION_METHOD = 0x49; - private static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLE_COLON_NEW = 0x4A; - private static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLE_COLON_ID = 0x4B; - - private static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER = 1; - private static final int ANNOTATION_TARGET_UNION_SUPERTYPE = 2; - private static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND = 3; - private static final int ANNOTATION_TARGET_UNION_EMPTY = 4; - private static final int ANNOTATION_TARGET_UNION_FORMAL_PARAMETER = 5; - private static final int ANNOTATION_TARGET_UNION_THROWS = 6; - private static final int ANNOTATION_TARGET_UNION_LOCAL_VAR = 7; - private static final int ANNOTATION_TARGET_UNION_CATCH = 8; - private static final int ANNOTATION_TARGET_UNION_OFFSET = 9; - private static final int ANNOTATION_TARGET_UNION_TYPE_ARGUMENT = 10; - - @SuppressWarnings("FieldCanBeLocal") private List locations; - @SuppressWarnings("FieldCanBeLocal") private List annotations; - - @Override - public void initContent(ConstantPool pool) throws IOException { - DataInputStream data = stream(); - - int len = data.readUnsignedByte(); - if (len > 0) { - locations = new ArrayList(len); - annotations = new ArrayList(len); - for (int i = 0; i < len; i++) { - locations.add(parseAnnotationLocation(data)); - annotations.add(StructAnnotationAttribute.parseAnnotation(data, pool)); - } - } - else { - locations = Collections.emptyList(); - annotations = Collections.emptyList(); - } - } - - private static AnnotationLocation parseAnnotationLocation(DataInputStream data) throws IOException { - AnnotationLocation ann_location = new AnnotationLocation(); - - // target type - ann_location.target_type = data.readUnsignedByte(); - - // target union - switch (ann_location.target_type) { - case ANNOTATION_TARGET_TYPE_GENERIC_CLASS: - case ANNOTATION_TARGET_TYPE_GENERIC_METHOD: - ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_PARAMETER; - break; - case ANNOTATION_TARGET_TYPE_EXTENDS_IMPLEMENTS: - ann_location.target_union = ANNOTATION_TARGET_UNION_SUPERTYPE; - break; - case ANNOTATION_TARGET_TYPE_GENERIC_CLASS_BOUND: - case ANNOTATION_TARGET_TYPE_GENERIC_METHOD_BOUND: - ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND; - break; - case ANNOTATION_TARGET_TYPE_FIELD: - case ANNOTATION_TARGET_TYPE_RETURN: - case ANNOTATION_TARGET_TYPE_RECEIVER: - ann_location.target_union = ANNOTATION_TARGET_UNION_EMPTY; - break; - case ANNOTATION_TARGET_TYPE_FORMAL: - ann_location.target_union = ANNOTATION_TARGET_UNION_FORMAL_PARAMETER; - break; - case ANNOTATION_TARGET_TYPE_THROWS: - ann_location.target_union = ANNOTATION_TARGET_UNION_THROWS; - break; - case ANNOTATION_TARGET_TYPE_LOCAL_VARIABLE: - case ANNOTATION_TARGET_TYPE_RESOURCE_VARIABLE: - ann_location.target_union = ANNOTATION_TARGET_UNION_LOCAL_VAR; - break; - case ANNOTATION_TARGET_TYPE_EXCEPTION: - ann_location.target_union = ANNOTATION_TARGET_UNION_CATCH; - break; - case ANNOTATION_TARGET_TYPE_INSTANCEOF: - case ANNOTATION_TARGET_TYPE_NEW: - case ANNOTATION_TARGET_TYPE_DOUBLE_COLON_NEW: - case ANNOTATION_TARGET_TYPE_DOUBLE_COLON_ID: - ann_location.target_union = ANNOTATION_TARGET_UNION_OFFSET; - break; - case ANNOTATION_TARGET_TYPE_CAST: - case ANNOTATION_TARGET_TYPE_INVOCATION_CONSTRUCTOR: - case ANNOTATION_TARGET_TYPE_INVOCATION_METHOD: - case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLE_COLON_NEW: - case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLE_COLON_ID: - ann_location.target_union = ANNOTATION_TARGET_UNION_TYPE_ARGUMENT; - break; - default: - throw new RuntimeException("Unknown target type in a type annotation!"); - } - - // target union data - - switch (ann_location.target_union) { - case ANNOTATION_TARGET_UNION_TYPE_PARAMETER: - case ANNOTATION_TARGET_UNION_FORMAL_PARAMETER: - ann_location.data = new int[]{data.readUnsignedByte()}; - break; - case ANNOTATION_TARGET_UNION_SUPERTYPE: - case ANNOTATION_TARGET_UNION_THROWS: - case ANNOTATION_TARGET_UNION_CATCH: - case ANNOTATION_TARGET_UNION_OFFSET: - ann_location.data = new int[]{data.readUnsignedShort()}; - break; - case ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND: - ann_location.data = new int[]{data.readUnsignedByte(), data.readUnsignedByte()}; - break; - case ANNOTATION_TARGET_UNION_EMPTY: - break; - case ANNOTATION_TARGET_UNION_LOCAL_VAR: - int table_length = data.readUnsignedShort(); - - ann_location.data = new int[table_length * 3 + 1]; - ann_location.data[0] = table_length; - - for (int i = 0; i < table_length; ++i) { - ann_location.data[3 * i + 1] = data.readUnsignedShort(); - ann_location.data[3 * i + 2] = data.readUnsignedShort(); - ann_location.data[3 * i + 3] = data.readUnsignedShort(); - } - break; - case ANNOTATION_TARGET_UNION_TYPE_ARGUMENT: - ann_location.data = new int[]{data.readUnsignedShort(), data.readUnsignedByte()}; - } - - // target path - int path_length = data.readUnsignedByte(); - - ann_location.target_path_kind = new int[path_length]; - ann_location.target_argument_index = new int[path_length]; - - for (int i = 0; i < path_length; ++i) { - ann_location.target_path_kind[i] = data.readUnsignedByte(); - ann_location.target_argument_index[i] = data.readUnsignedByte(); - } - - return ann_location; - } - - private static class AnnotationLocation { - public int target_type; - public int target_union; - public int[] data; - public int[] target_path_kind; - public int[] target_argument_index; - } -} diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java index dc0ebc6..73cc535 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ import java.io.IOException; } */ public class StructGeneralAttribute { - public static final String ATTRIBUTE_CODE = "Code"; public static final String ATTRIBUTE_INNER_CLASSES = "InnerClasses"; public static final String ATTRIBUTE_SIGNATURE = "Signature"; @@ -73,17 +72,14 @@ public class StructGeneralAttribute { else if (ATTRIBUTE_ENCLOSING_METHOD.equals(name)) { attr = new StructEnclosingMethodAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.equals(name) || - ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.equals(name)) { + else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.equals(name)) { attr = new StructAnnotationAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(name) || - ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(name)) { + else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(name)) { attr = new StructAnnotationParameterAttribute(); } - else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(name) || - ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(name)) { - attr = new StructAnnotationTypeAttribute(); + else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(name)) { + attr = new StructTypeAnnotationAttribute(); } else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.equals(name)) { attr = new StructLocalVariableTableAttribute(); @@ -91,8 +87,7 @@ public class StructGeneralAttribute { else if (ATTRIBUTE_BOOTSTRAP_METHODS.equals(name)) { attr = new StructBootstrapMethodsAttribute(); } - else if (ATTRIBUTE_SYNTHETIC.equals(name) || - ATTRIBUTE_DEPRECATED.equals(name)) { + else if (ATTRIBUTE_SYNTHETIC.equals(name) || ATTRIBUTE_DEPRECATED.equals(name)) { attr = new StructGeneralAttribute(); } else if (ATTRIBUTE_LINE_NUMBER_TABLE.equals(name)) { @@ -123,4 +118,4 @@ public class StructGeneralAttribute { public String getName() { return name; } -} +} \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructTypeAnnotationAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructTypeAnnotationAttribute.java new file mode 100644 index 0000000..1cb9485 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructTypeAnnotationAttribute.java @@ -0,0 +1,107 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.java.decompiler.struct.attr; + +import org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent; +import org.jetbrains.java.decompiler.modules.decompiler.exps.TypeAnnotation; +import org.jetbrains.java.decompiler.struct.consts.ConstantPool; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class StructTypeAnnotationAttribute extends StructGeneralAttribute { + private List annotations = Collections.emptyList(); + + @Override + public void initContent(ConstantPool pool) throws IOException { + DataInputStream data = stream(); + + int len = data.readUnsignedShort(); + if (len > 0) { + annotations = new ArrayList<>(len); + for (int i = 0; i < len; i++) { + annotations.add(parse(data, pool)); + } + } + else { + annotations = Collections.emptyList(); + } + } + + private static TypeAnnotation parse(DataInputStream data, ConstantPool pool) throws IOException { + int targetType = data.readUnsignedByte(); + int target = targetType << 24; + + switch (targetType) { + case TypeAnnotation.CLASS_TYPE_PARAMETER: + case TypeAnnotation.METHOD_TYPE_PARAMETER: + case TypeAnnotation.METHOD_PARAMETER: + target |= data.readUnsignedByte(); + break; + + case TypeAnnotation.SUPER_TYPE_REFERENCE: + case TypeAnnotation.CLASS_TYPE_PARAMETER_BOUND: + case TypeAnnotation.METHOD_TYPE_PARAMETER_BOUND: + case TypeAnnotation.THROWS_REFERENCE: + case TypeAnnotation.CATCH_CLAUSE: + case TypeAnnotation.EXPR_INSTANCEOF: + case TypeAnnotation.EXPR_NEW: + case TypeAnnotation.EXPR_CONSTRUCTOR_REF: + case TypeAnnotation.EXPR_METHOD_REF: + target |= data.readUnsignedShort(); + break; + + case TypeAnnotation.TYPE_ARG_CAST: + case TypeAnnotation.TYPE_ARG_CONSTRUCTOR_CALL: + case TypeAnnotation.TYPE_ARG_METHOD_CALL: + case TypeAnnotation.TYPE_ARG_CONSTRUCTOR_REF: + case TypeAnnotation.TYPE_ARG_METHOD_REF: + data.skipBytes(3); + break; + + case TypeAnnotation.LOCAL_VARIABLE: + case TypeAnnotation.RESOURCE_VARIABLE: + data.skipBytes(data.readUnsignedShort() * 6); + break; + + case TypeAnnotation.FIELD: + case TypeAnnotation.METHOD_RETURN_TYPE: + case TypeAnnotation.METHOD_RECEIVER: + break; + + default: + throw new RuntimeException("unknown target type: " + targetType); + } + + int pathLength = data.readUnsignedByte(); + byte[] path = null; + if (pathLength > 0) { + path = new byte[2 * pathLength]; + data.readFully(path); + } + + AnnotationExprent annotation = StructAnnotationAttribute.parseAnnotation(data, pool); + + return new TypeAnnotation(target, path, annotation); + } + + public List getAnnotations() { + return annotations; + } +} \ No newline at end of file diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 84434a9..3d14216 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -84,6 +84,7 @@ public class SingleClassesTest { @Test public void testMethodReferenceLetterClass() { doTest("pkg/TestMethodReferenceLetterClass"); } @Test public void testMemberAnnotations() { doTest("pkg/TestMemberAnnotations"); } @Test public void testMoreAnnotations() { doTest("pkg/MoreAnnotations"); } + @Test public void testTypeAnnotations() { doTest("pkg/TypeAnnotations"); } @Test public void testStaticNameClash() { doTest("pkg/TestStaticNameClash"); } @Test public void testExtendingSubclass() { doTest("pkg/TestExtendingSubclass"); } @Test public void testSyntheticAccess() { doTest("pkg/TestSyntheticAccess"); } diff --git a/testData/classes/pkg/TypeAnnotations$MixA.class b/testData/classes/pkg/TypeAnnotations$MixA.class new file mode 100644 index 0000000000000000000000000000000000000000..2795bce3a3ce50fbdd6f761accd7a5745bb9129a GIT binary patch literal 404 zcmZutO-sW-5Pg#-CdR7OR?ve8B1JFe;>}ah6bco^L_s{rwk%<5x+%$~=wI{T5Aa8c z69f~ahxuUMn|U+y_5JY)-~v4tTP}87>=K-%NJ~lRot}+nVkv@D=p=|tu5@xq@T08E zr*f#$iW9#t968zif2=;GikF=KgRVs=?7KBcH-|b{3 zr-pD;mm1sEClb0VRQdqHVr%g`$!0w`oVBpV-hvB{;~wMdZ1yuO2_GAkYoo)E&t<|U Hw)@@>xdm&8 literal 0 HcmV?d00001 diff --git a/testData/classes/pkg/TypeAnnotations$TA.class b/testData/classes/pkg/TypeAnnotations$TA.class new file mode 100644 index 0000000000000000000000000000000000000000..d02171e1070d5a4a5a106a5c0ebe06920d3b2385 GIT binary patch literal 387 zcmZutO-sW-5Pj38CdRL5MerbkNYRVAc=J>wf*>d+3gSVwWe8iho04n_{xuK&0DqJ? zxtJh5%K#?ByYOdrA`@dxV3#p-ZHDg!cD|*?>PYp>L??O>s^HgHb&VNWY4yk? z0vrADk6|a1ZU 14 +21 <-> 18 diff --git a/testData/src/pkg/TypeAnnotations.java b/testData/src/pkg/TypeAnnotations.java new file mode 100644 index 0000000..0760dde --- /dev/null +++ b/testData/src/pkg/TypeAnnotations.java @@ -0,0 +1,22 @@ +package pkg; + +import java.lang.annotation.*; +import java.util.*; + +class TypeAnnotations { + @Target(ElementType.TYPE_USE) + @interface TA { String value(); } + + @Target({ElementType.FIELD, ElementType.TYPE_USE}) + @interface MixA { String value(); } + + private @TA("field type") String f1; + + private @MixA("field and type") String f2; + + @TA("return type") int m1() { + return 42; + } + + void m2(@TA("parameter") int i) { } +} \ No newline at end of file