changeset 54:3e27a236f4b7

Added parser for annotation directories. Reviewed-by: Remi Forax * dex/AnnotationsDirectory.java: New parser class. * dex/ClassData.java: Factored out encoded_array decoding. * dex/ClassDef.java: Call new parser class for annotations. * dex/EncodedValue.java (parseArray, parseAnnotation): New decoder methods. * (AnnotationValue): New internal model class for encoded annotations.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Sun, 20 Mar 2011 19:31:39 +0100
parents e6fa81ffbda9
children 80952fd2bbd4
files src/main/java/org/icedrobot/daneel/dex/AnnotationsDirectory.java src/main/java/org/icedrobot/daneel/dex/ClassData.java src/main/java/org/icedrobot/daneel/dex/ClassDef.java src/main/java/org/icedrobot/daneel/dex/EncodedValue.java
diffstat 4 files changed, 275 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/org/icedrobot/daneel/dex/AnnotationsDirectory.java	Sun Mar 20 19:31:39 2011 +0100
@@ -0,0 +1,193 @@
+/*
+ * Daneel - Dalvik to Java bytecode compiler
+ * Copyright (C) 2011  IcedRobot team
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file is subject to the "Classpath" exception:
+ *
+ * Linking this library statically or dynamically with other modules is
+ * making a combined work based on this library.  Thus, the terms and
+ * conditions of the GNU General Public License cover the whole
+ * combination.
+ *
+ * As a special exception, the copyright holders of this library give you
+ * permission to link this library with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under terms
+ * of your choice, provided that you also meet, for each linked independent
+ * module, the terms and conditions of the license of that module.  An
+ * independent module is a module which is not derived from or based on
+ * this library.  If you modify this library, you may extend this exception
+ * to your version of the library, but you are not obligated to do so.  If
+ * you do not wish to do so, delete this exception statement from your
+ * version.
+ */
+
+package org.icedrobot.daneel.dex;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.icedrobot.daneel.dex.EncodedValue.AnnotationValue;
+
+/**
+ * A parser class capable of parsing {@code annotations_directory_item}
+ * structures as part of DEX files. Keep package-private to hide internal API.
+ */
+class AnnotationsDirectory {
+
+    /**
+     * Parses a {@code annotations_directory_item} structure in a DEX file at
+     * the buffer's current position.
+     * 
+     * @param buffer The byte buffer to read from.
+     * @param dex The DEX file currently being parsed.
+     * @return An object representing the parsed data.
+     */
+    public static AnnotationsDirectory parse(ByteBuffer buffer, DexFile dex) {
+        return new AnnotationsDirectory(buffer, dex);
+    }
+
+    private final Annotation[] classAnnotations;
+
+    private final Map<Integer, Annotation[]> fieldAnnotations;
+
+    private final Map<Integer, Annotation[]> methodAnnotations;
+
+    private final Map<Integer, Annotation[][]> parameterAnnotations;
+
+    private AnnotationsDirectory(ByteBuffer buffer, DexFile dex) {
+        int classAnnotationsOff = buffer.getInt();
+        int fieldsSize = buffer.getInt();
+        int methodsSize = buffer.getInt();
+        int parametersSize = buffer.getInt();
+
+        // Parse annotation_set_item structure for class annotations.
+        if (classAnnotationsOff != 0) {
+            ByteBuffer buf = dex.getDataBuffer(classAnnotationsOff);
+            classAnnotations = parseAnnotations(buf, dex);
+        } else
+            classAnnotations = null;
+
+        // Parse annotation_set_item structures in field_annotations array.
+        fieldAnnotations = new HashMap<Integer, Annotation[]>(fieldsSize);
+        for (int i = 0; i < fieldsSize; i++) {
+            int fieldIdx = buffer.getInt();
+            int annotationsOff = buffer.getInt();
+            ByteBuffer buf = dex.getDataBuffer(annotationsOff);
+            Annotation[] annotations = parseAnnotations(buf, dex);
+            fieldAnnotations.put(fieldIdx, annotations);
+        }
+
+        // Parse annotation_set_item structures in method_annotations array.
+        methodAnnotations = new HashMap<Integer, Annotation[]>(methodsSize);
+        for (int i = 0; i < methodsSize; i++) {
+            int methodIdx = buffer.getInt();
+            int annotationsOff = buffer.getInt();
+            ByteBuffer buf = dex.getDataBuffer(annotationsOff);
+            Annotation[] annotations = parseAnnotations(buf, dex);
+            methodAnnotations.put(methodIdx, annotations);
+        }
+
+        // Parse annotation_set_ref_list and contained annotation_set_item
+        // structures in parameter_annotations array.
+        parameterAnnotations = new HashMap<Integer, Annotation[][]>();
+        for (int i = 0; i < parametersSize; i++) {
+            /*int methodIdx = buffer.getInt();
+            int annotationsOff = buffer.getInt();
+            ByteBuffer buf = dex.getDataBuffer(annotationsOff);
+            int size = buf.getInt();
+            int[] list = BufferUtil.getInts(buf, size);
+            Annotation[][] annotationSet = new Annotation[size][];
+            for (int j = 0; j < size; j++) {
+                buf = dex.getDataBuffer(list[j]);
+                annotationSet[j] = parseAnnotations(buf, dex);
+            }
+            parameterAnnotations.put(methodIdx, annotationSet);*/
+            throw new UnsupportedOperationException("Not yet tested!");
+        }
+    }
+
+    /**
+     * Returns all class annotations present in this directory or {@code null}
+     * if there are no such annotations.
+     * 
+     * @return An array containing the annotation items or {@code null}.
+     */
+    public Annotation[] getClassAnnotations() {
+        return classAnnotations;
+    }
+
+    /**
+     * Returns all field annotations for the given field index present in this
+     * directory or {@code null} if there are no such annotations.
+     * 
+     * @param idx The given field index.
+     * @return An array containing the annotation items or {@code null}.
+     */
+    public Annotation[] getFieldAnnotations(int idx) {
+        return fieldAnnotations.get(idx);
+    }
+
+    /**
+     * Returns all method annotations for the given method index present in this
+     * directory or {@code null} if there are no such annotations.
+     * 
+     * @param idx The given field index.
+     * @return An array containing the annotation items or {@code null}.
+     */
+    public Annotation[] getMethodAnnotations(int idx) {
+        return methodAnnotations.get(idx);
+    }
+
+    /**
+     * Helper method decoding an {@code annotation_set_item} array.
+     * 
+     * @param buffer The byte buffer to read from.
+     * @param dex The DEX file currently being parsed.
+     * @return An array containing all the encoded annotation items.
+     */
+    private static Annotation[] parseAnnotations(ByteBuffer buffer, DexFile dex) {
+        int size = buffer.getInt();
+        Annotation[] annotations = new Annotation[size];
+        for (int i = 0; i < size; i++) {
+            int annotationOff = buffer.getInt();
+            ByteBuffer buf = dex.getDataBuffer(annotationOff);
+            annotations[i] = new Annotation(buf, dex);
+        }
+        return annotations;
+    }
+
+    /**
+     * An internal representation of an annotation as encoded in the {@code
+     * annotation_item} structure.
+     */
+    static class Annotation {
+        public static final int VISIBILITY_BUILD   = 0x00;
+        public static final int VISIBILITY_RUNTIME = 0x01;
+        public static final int VISIBILITY_SYSTEM  = 0x02;
+
+        protected final int visibility;
+        protected final AnnotationValue annotationValue;
+
+        public Annotation(ByteBuffer buffer, DexFile dex) {
+            visibility = buffer.get();
+            annotationValue = EncodedValue.parseAnnotation(buffer, dex);
+            System.out.printf("ANNOTATION: vis=0x%02x, val=%s\n", visibility,
+                    annotationValue);
+        }
+    };
+}
--- a/src/main/java/org/icedrobot/daneel/dex/ClassData.java	Sat Mar 19 23:37:56 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/ClassData.java	Sun Mar 20 19:31:39 2011 +0100
@@ -148,10 +148,7 @@
         // Parse encoded_array_item and contained encoded_value structures.
         if (staticValuesOff != 0) {
             ByteBuffer buf = dex.getDataBuffer(staticValuesOff);
-            int staticValuesSize = BufferUtil.getULEB128(buf);
-            staticValues = new Object[staticValuesSize];
-            for (int i = 0; i < staticValuesSize; i++)
-                staticValues[i] = EncodedValue.parse(buf, dex);
+            staticValues = EncodedValue.parseArray(buf, dex);
         } else
             staticValues = new Object[0];
     }
--- a/src/main/java/org/icedrobot/daneel/dex/ClassDef.java	Sat Mar 19 23:37:56 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/ClassDef.java	Sun Mar 20 19:31:39 2011 +0100
@@ -67,6 +67,8 @@
 
     private String sourceFile;
 
+    private final AnnotationsDirectory annotations;
+
     private ClassData classData;
 
     private ClassDef(ByteBuffer buffer, DexFile dex) {
@@ -94,7 +96,14 @@
                 interfaces[i] = dex.getTypeDescriptor(buf.getShort());
         }
 
-        // Parse the associated class_data_item structure.
+        // Parse associated annotations_directory_item structure.
+        if (annotationsOff != 0) {
+            ByteBuffer buf = dex.getDataBuffer(annotationsOff);
+            annotations = AnnotationsDirectory.parse(buf, dex);
+        } else
+            annotations = null;
+
+        // Parse associated class_data_item structure.
         if (classDataOff != 0) {
             ByteBuffer buf = dex.getDataBuffer(classDataOff);
             classData = ClassData.parse(buf, dex, staticValuesOff);
--- a/src/main/java/org/icedrobot/daneel/dex/EncodedValue.java	Sat Mar 19 23:37:56 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/EncodedValue.java	Sun Mar 20 19:31:39 2011 +0100
@@ -34,9 +34,14 @@
  * you do not wish to do so, delete this exception statement from your
  * version.
  */
+
 package org.icedrobot.daneel.dex;
 
 import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.icedrobot.daneel.util.BufferUtil;
 
 /**
  * A parser class capable of parsing {@code encoded_value} structures as part of
@@ -94,6 +99,17 @@
         case VALUE_STRING:
             assertValueArg(arg, 3);
             return dex.getString((int) readU64(buffer, arg));
+        case VALUE_TYPE:
+            assertValueArg(arg, 3);
+            // XXX What kind of boxing should we use?
+            return "type@" + readU64(buffer, arg);
+        case VALUE_METHOD:
+            assertValueArg(arg, 3);
+            // XXX What kind of boxing should we use?
+            return "method@" + readU64(buffer, arg);
+        case VALUE_ARRAY:
+            assertValueArg(arg, 0);
+            return parseArray(buffer, dex);
         case VALUE_NULL:
             assertValueArg(arg, 0);
             return null;
@@ -107,6 +123,42 @@
     }
 
     /**
+     * Parses a {@code encoded_array} structure in a DEX file at the buffer's
+     * current position.
+     * 
+     * @param buffer The byte buffer to read from.
+     * @param dex The DEX file currently being parsed.
+     * @return An object representing the parsed data.
+     */
+    public static Object[] parseArray(ByteBuffer buffer, DexFile dex) {
+        int size = BufferUtil.getULEB128(buffer);
+        Object[] values = new Object[size];
+        for (int i = 0; i < size; i++)
+            values[i] = parse(buffer, dex);
+        return values;
+    }
+
+    /**
+     * Parses a {@code encoded_annotation} structure in a DEX file at the
+     * buffer's current position.
+     * 
+     * @param buffer The byte buffer to read from.
+     * @param dex The DEX file currently being parsed.
+     * @return An object representing the parsed data.
+     */
+    public static AnnotationValue parseAnnotation(ByteBuffer buffer, DexFile dex) {
+        int typeIdx = BufferUtil.getULEB128(buffer);
+        int size = BufferUtil.getULEB128(buffer);
+        String type = dex.getTypeDescriptor(typeIdx);
+        Map<String, Object> elements = new HashMap<String, Object>(size);
+        for (int i = 0; i < size; i++) {
+            int nameIdx = BufferUtil.getULEB128(buffer);
+            elements.put(dex.getString(nameIdx), parse(buffer, dex));
+        }
+        return new AnnotationValue(type, elements);
+    }
+
+    /**
      * Helper method for sanity checking the {@code value_arg} value.
      * 
      * @param arg The given {@code value_arg} value.
@@ -147,4 +199,23 @@
             result |= (buffer.get() & 0xff) << (i * 8);
         return result;
     }
+
+    /**
+     * An internal representation of an annotation as encoded in the {@code
+     * encoded_annotation} structure.
+     */
+    static class AnnotationValue {
+        private final String type;
+        private final Map<String, Object> elements;
+
+        public AnnotationValue(String type, Map<String, Object> elements) {
+            this.type = type;
+            this.elements = elements;
+        }
+
+        @Override
+        public String toString() {
+            return '@' + type + '(' + elements + ')';
+        }
+    };
 }