From c9c426ded7c91f7452f060a6d31e7ec5ff4731cf Mon Sep 17 00:00:00 2001 From: Stiver Date: Tue, 4 Mar 2014 17:00:10 +0100 Subject: [PATCH] Annotation parsing (Java 8) --- src/de/fernflower/code/CodeConstants.java | 13 ++ .../StructAnnotationParameterAttribute.java | 2 +- .../attr/StructAnnotationTypeAttribute.java | 188 ++++++++++++++++++ .../attr/StructBootstrapMethodsAttribute.java | 50 +++++ .../struct/attr/StructGeneralAttribute.java | 8 + .../struct/consts/ConstantPool.java | 9 +- .../struct/consts/LinkConstant.java | 22 +- .../struct/consts/PrimitiveConstant.java | 6 +- 8 files changed, 288 insertions(+), 10 deletions(-) create mode 100644 src/de/fernflower/struct/attr/StructAnnotationTypeAttribute.java create mode 100644 src/de/fernflower/struct/attr/StructBootstrapMethodsAttribute.java diff --git a/src/de/fernflower/code/CodeConstants.java b/src/de/fernflower/code/CodeConstants.java index a6617f1..90ed425 100644 --- a/src/de/fernflower/code/CodeConstants.java +++ b/src/de/fernflower/code/CodeConstants.java @@ -135,6 +135,19 @@ public interface CodeConstants { public final static int CONSTANT_MethodType = 16; public final static int CONSTANT_InvokeDynamic = 18; + // ---------------------------------------------------------------------- + // MethodHandle reference_kind values + // ---------------------------------------------------------------------- + + public final static int CONSTANT_MethodHandle_REF_getField = 1; + public final static int CONSTANT_MethodHandle_REF_getStatic = 2; + public final static int CONSTANT_MethodHandle_REF_putField = 3; + public final static int CONSTANT_MethodHandle_REF_putStatic = 4; + public final static int CONSTANT_MethodHandle_REF_invokeVirtual = 5; + public final static int CONSTANT_MethodHandle_REF_invokeStatic = 6; + public final static int CONSTANT_MethodHandle_REF_invokeSpecial = 7; + public final static int CONSTANT_MethodHandle_REF_newInvokeSpecial = 8; + public final static int CONSTANT_MethodHandle_REF_invokeInterface = 9; // ---------------------------------------------------------------------- // VM OPCODES diff --git a/src/de/fernflower/struct/attr/StructAnnotationParameterAttribute.java b/src/de/fernflower/struct/attr/StructAnnotationParameterAttribute.java index d89303a..c7f9f7c 100644 --- a/src/de/fernflower/struct/attr/StructAnnotationParameterAttribute.java +++ b/src/de/fernflower/struct/attr/StructAnnotationParameterAttribute.java @@ -23,7 +23,7 @@ import java.util.List; import de.fernflower.modules.decompiler.exps.AnnotationExprent; import de.fernflower.struct.consts.ConstantPool; -public class StructAnnotationParameterAttribute extends StructGeneralAttribute { +public class StructAnnotationParameterAttribute extends StructGeneralAttribute { private List> paramAnnotations; diff --git a/src/de/fernflower/struct/attr/StructAnnotationTypeAttribute.java b/src/de/fernflower/struct/attr/StructAnnotationTypeAttribute.java new file mode 100644 index 0000000..0c8aec9 --- /dev/null +++ b/src/de/fernflower/struct/attr/StructAnnotationTypeAttribute.java @@ -0,0 +1,188 @@ +package de.fernflower.struct.attr; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import de.fernflower.modules.decompiler.exps.AnnotationExprent; +import de.fernflower.struct.consts.ConstantPool; + +public class StructAnnotationTypeAttribute extends StructGeneralAttribute { + + public static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS = 0x00; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD = 0x01; + public static final int ANNOTATION_TARGET_TYPE_EXTENDS_IMPLEMENTS = 0x10; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_CLASS_BOUND = 0x11; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_METHOD_BOUND = 0x12; + public static final int ANNOTATION_TARGET_TYPE_FIELD = 0x13; + public static final int ANNOTATION_TARGET_TYPE_RETURN = 0x14; + public static final int ANNOTATION_TARGET_TYPE_RECEIVER = 0x15; + public static final int ANNOTATION_TARGET_TYPE_FORMAL = 0x16; + public static final int ANNOTATION_TARGET_TYPE_THROWS = 0x17; + public static final int ANNOTATION_TARGET_TYPE_LOCAL_VARIABLE = 0x40; + public static final int ANNOTATION_TARGET_TYPE_RESOURCE_VARIABLE = 0x41; + public static final int ANNOTATION_TARGET_TYPE_EXCEPTION = 0x42; + public static final int ANNOTATION_TARGET_TYPE_INSTANCEOF = 0x43; + public static final int ANNOTATION_TARGET_TYPE_NEW = 0x44; + public static final int ANNOTATION_TARGET_TYPE_DOUBLECOLON_NEW = 0x45; + public static final int ANNOTATION_TARGET_TYPE_DOUBLECOLON_ID = 0x46; + public static final int ANNOTATION_TARGET_TYPE_CAST = 0x47; + public static final int ANNOTATION_TARGET_TYPE_INVOKATION_CONSTRUCTOR = 0x48; + public static final int ANNOTATION_TARGET_TYPE_INVOKATION_METHOD = 0x49; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_NEW = 0x4A; + public static final int ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_ID = 0x4B; + + public static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER = 1; + public static final int ANNOTATION_TARGET_UNION_SUPERTYPE = 2; + public static final int ANNOTATION_TARGET_UNION_TYPE_PARAMETER_BOUND = 3; + public static final int ANNOTATION_TARGET_UNION_EMPTY = 4; + public static final int ANNOTATION_TARGET_UNION_FORMAL_PARAMETER = 5; + public static final int ANNOTATION_TARGET_UNION_THROWS = 6; + public static final int ANNOTATION_TARGET_UNION_LOCALVAR = 7; + public static final int ANNOTATION_TARGET_UNION_CATCH = 8; + public static final int ANNOTATION_TARGET_UNION_OFFSET = 9; + public static final int ANNOTATION_TARGET_UNION_TYPE_ARGUMENT = 10; + + + List locations = new ArrayList(); + List annotations = new ArrayList(); + + public void initContent(ConstantPool pool) { + + super.initContent(pool); + + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info)); + + try { + + int ann_number = data.readUnsignedByte(); + for(int i = 0; i < ann_number; i++) { + locations.add(parseAnnotationLocation(data)); + annotations.add(StructAnnotationAttribute.parseAnnotation(data, pool)); + } + + } catch(IOException ex) { + throw new RuntimeException(ex); + } + } + + public 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_LOCALVAR; + 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_DOUBLECOLON_NEW: + case ANNOTATION_TARGET_TYPE_DOUBLECOLON_ID: + ann_location.target_union = ANNOTATION_TARGET_UNION_OFFSET; + break; + case ANNOTATION_TARGET_TYPE_CAST: + case ANNOTATION_TARGET_TYPE_INVOKATION_CONSTRUCTOR: + case ANNOTATION_TARGET_TYPE_INVOKATION_METHOD: + case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_NEW: + case ANNOTATION_TARGET_TYPE_GENERIC_DOUBLECOLON_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_LOCALVAR: + 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/de/fernflower/struct/attr/StructBootstrapMethodsAttribute.java b/src/de/fernflower/struct/attr/StructBootstrapMethodsAttribute.java new file mode 100644 index 0000000..e9e8837 --- /dev/null +++ b/src/de/fernflower/struct/attr/StructBootstrapMethodsAttribute.java @@ -0,0 +1,50 @@ +package de.fernflower.struct.attr; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import de.fernflower.struct.consts.ConstantPool; +import de.fernflower.struct.consts.LinkConstant; +import de.fernflower.struct.consts.PooledConstant; + +public class StructBootstrapMethodsAttribute extends StructGeneralAttribute { + + private List method_refs = new ArrayList(); + private List> method_arguments = new ArrayList>(); + + public void initContent(ConstantPool pool) { + + name = ATTRIBUTE_BOOTSTRAP_METHODS; + + try { + + DataInputStream data = new DataInputStream(new ByteArrayInputStream(info, 0, info.length)); + + int method_number = data.readUnsignedShort(); + + for(int i = 0; i < method_number; ++i) { + int bootstrap_method_ref = data.readUnsignedShort(); + int num_bootstrap_arguments = data.readUnsignedShort(); + + List list_arguments = new ArrayList(); + + for(int j = 0; j < num_bootstrap_arguments; ++j) { + int bootstrap_argument_ref = data.readUnsignedShort(); + + list_arguments.add(pool.getConstant(bootstrap_argument_ref)); + } + + method_refs.add(pool.getLinkConstant(bootstrap_method_ref)); + method_arguments.add(list_arguments); + } + + } catch(IOException ex) { + throw new RuntimeException(ex); + } + + } + +} diff --git a/src/de/fernflower/struct/attr/StructGeneralAttribute.java b/src/de/fernflower/struct/attr/StructGeneralAttribute.java index 7f600b6..830721a 100644 --- a/src/de/fernflower/struct/attr/StructGeneralAttribute.java +++ b/src/de/fernflower/struct/attr/StructGeneralAttribute.java @@ -39,8 +39,11 @@ public class StructGeneralAttribute { public static final String ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; public static final String ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; public static final String ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations"; + public static final String ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations"; + public static final String ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations"; public static final String ATTRIBUTE_LOCAL_VARIABLE_TABLE = "LocalVariableTable"; public static final String ATTRIBUTE_CONSTANT_VALUE = "ConstantValue"; + public static final String ATTRIBUTE_BOOTSTRAP_METHODS = "BootstrapMethods"; @@ -92,8 +95,13 @@ public class StructGeneralAttribute { } else if(ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(attrname) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(attrname)) { attr = new StructAnnotationParameterAttribute(); + } else if(ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attrname) || + ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attrname)) { + attr = new StructAnnotationTypeAttribute(); } else if(ATTRIBUTE_LOCAL_VARIABLE_TABLE.equals(attrname)) { attr = new StructLocalVariableTableAttribute(); + } else if(ATTRIBUTE_BOOTSTRAP_METHODS.equals(attrname)) { + attr = new StructBootstrapMethodsAttribute(); } else { // unsupported attribute return null; diff --git a/src/de/fernflower/struct/consts/ConstantPool.java b/src/de/fernflower/struct/consts/ConstantPool.java index 72ade37..c1d6976 100644 --- a/src/de/fernflower/struct/consts/ConstantPool.java +++ b/src/de/fernflower/struct/consts/ConstantPool.java @@ -90,6 +90,7 @@ public class ConstantPool { case CodeConstants.CONSTANT_Methodref: case CodeConstants.CONSTANT_InterfaceMethodref: case CodeConstants.CONSTANT_NameAndType: + case CodeConstants.CONSTANT_InvokeDynamic: pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort())); if(tag == CodeConstants.CONSTANT_NameAndType) { pass[i] = 1; @@ -101,9 +102,6 @@ public class ConstantPool { pool.add(new LinkConstant(tag, in.readUnsignedByte(), in.readUnsignedShort())); pass[i] = 3; break; - case CodeConstants.CONSTANT_InvokeDynamic: - pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort())); - pass[i] = 2; } } @@ -151,6 +149,7 @@ public class ConstantPool { case CodeConstants.CONSTANT_Methodref: case CodeConstants.CONSTANT_InterfaceMethodref: case CodeConstants.CONSTANT_NameAndType: + case CodeConstants.CONSTANT_InvokeDynamic: in.skip(4); break; case CodeConstants.CONSTANT_Long: @@ -160,7 +159,11 @@ public class ConstantPool { break; case CodeConstants.CONSTANT_Class: case CodeConstants.CONSTANT_String: + case CodeConstants.CONSTANT_MethodType: in.skip(2); + break; + case CodeConstants.CONSTANT_MethodHandle: + in.skip(3); } } } diff --git a/src/de/fernflower/struct/consts/LinkConstant.java b/src/de/fernflower/struct/consts/LinkConstant.java index 3ee66c6..ec0a629 100644 --- a/src/de/fernflower/struct/consts/LinkConstant.java +++ b/src/de/fernflower/struct/consts/LinkConstant.java @@ -19,6 +19,7 @@ import java.io.IOException; /* * NameAndType, FieldRef, MethodRef, InterfaceMethodref + * InvokeDynamic, MethodHandle */ public class LinkConstant extends PooledConstant { @@ -39,7 +40,7 @@ public class LinkConstant extends PooledConstant { public boolean isVoid = false;; - public boolean returnCategory2 = false;; + public boolean returnCategory2 = false; // ***************************************************************************** @@ -71,8 +72,17 @@ public class LinkConstant extends PooledConstant { if(type == CONSTANT_NameAndType) { elementname = pool.getPrimitiveConstant(index1).getString(); descriptor = pool.getPrimitiveConstant(index2).getString(); + } else if(type == CONSTANT_MethodHandle) { + LinkConstant ref_info = pool.getLinkConstant(index2); + + classname = ref_info.classname; + elementname = ref_info.elementname; + descriptor = ref_info.descriptor; + } else { - classname = pool.getPrimitiveConstant(index1).getString(); + if(type != CONSTANT_InvokeDynamic) { + classname = pool.getPrimitiveConstant(index1).getString(); + } LinkConstant nametype = pool.getLinkConstant(index2); elementname = nametype.elementname; @@ -84,7 +94,11 @@ public class LinkConstant extends PooledConstant { public void writeToStream(DataOutputStream out) throws IOException { out.writeByte(type); - out.writeShort(index1); + if(type == CONSTANT_MethodHandle) { + out.writeByte(index1); + } else { + out.writeShort(index1); + } out.writeShort(index2); } @@ -117,7 +131,7 @@ public class LinkConstant extends PooledConstant { private void initConstant() { - if(type == CONSTANT_Methodref || type == CONSTANT_InterfaceMethodref) { + if(type == CONSTANT_Methodref || type == CONSTANT_InterfaceMethodref || type == CONSTANT_InvokeDynamic || type == CONSTANT_MethodHandle) { resolveDescriptor(descriptor); } else if(type == CONSTANT_Fieldref) { returnCategory2 = ("D".equals(descriptor) || "J".equals(descriptor)); diff --git a/src/de/fernflower/struct/consts/PrimitiveConstant.java b/src/de/fernflower/struct/consts/PrimitiveConstant.java index 035447c..e9f949c 100644 --- a/src/de/fernflower/struct/consts/PrimitiveConstant.java +++ b/src/de/fernflower/struct/consts/PrimitiveConstant.java @@ -17,6 +17,8 @@ package de.fernflower.struct.consts; import java.io.DataOutputStream; import java.io.IOException; +import de.fernflower.code.CodeConstants; + /* * Integer, Long, Float, Double, String, Class, UTF8 */ @@ -75,7 +77,7 @@ public class PrimitiveConstant extends PooledConstant { public void resolveConstant(ConstantPool pool) { - if(type == CONSTANT_Class || type == CONSTANT_String) { + if(type == CONSTANT_Class || type == CONSTANT_String || type == CONSTANT_MethodType) { value = pool.getPrimitiveConstant(index).getString(); initConstant(); } @@ -100,7 +102,7 @@ public class PrimitiveConstant extends PooledConstant { case CONSTANT_Utf8: out.writeUTF(getString()); break; - default: // CONSTANT_Class or CONSTANT_String + default: // CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType out.writeShort(index); } }