changeset 56:ef7e8137da03

Adapted parser interface for annotations. Reviewed-by: Remi Forax * dex/AnnotationsDirectory.java (acceptClassAnnotations): Implemented acceptor. * (acceptFieldAnnotations, acceptMethodAnnotations): Likewise. * (acceptAnnotation): Still missing, coming soon. * dex/ClassData.java: Pass class definition as argument. * (acceptField, acceptMethod): Also visit annotations. * dex/ClassDef.java (accept): Also visit annotations. * dex/DexAnnotationVisitor.java: New parser interface for annotations. * dex/DexClassVisitor.java: Adapted for annotations. * dex/DexFieldVisitor.java: Likewise. * dex/DexMethodVisitor.java: Likewise. * dex/EncodedValue.java (AnnotationValue): Make fields package-private. * rewriter/DexRewriter.java: Adapted to above changes.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Sun, 20 Mar 2011 20:56:43 +0100
parents 80952fd2bbd4
children 8c9542073cf4
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/DexAnnotationVisitor.java src/main/java/org/icedrobot/daneel/dex/DexClassVisitor.java src/main/java/org/icedrobot/daneel/dex/DexFieldVisitor.java src/main/java/org/icedrobot/daneel/dex/DexMethodVisitor.java src/main/java/org/icedrobot/daneel/dex/EncodedValue.java src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java
diffstat 9 files changed, 236 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/dex/AnnotationsDirectory.java	Sun Mar 20 19:51:58 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/AnnotationsDirectory.java	Sun Mar 20 20:56:43 2011 +0100
@@ -63,11 +63,11 @@
 
     private final Annotation[] classAnnotations;
 
-    private final Map<Integer, Annotation[]> fieldAnnotations;
+    private final Map<FieldId, Annotation[]> fieldAnnotations;
 
-    private final Map<Integer, Annotation[]> methodAnnotations;
+    private final Map<MethodId, Annotation[]> methodAnnotations;
 
-    private final Map<Integer, Annotation[][]> parameterAnnotations;
+    private final Map<MethodId, Annotation[][]> parameterAnnotations;
 
     private AnnotationsDirectory(ByteBuffer buffer, DexFile dex) {
         int classAnnotationsOff = buffer.getInt();
@@ -83,28 +83,28 @@
             classAnnotations = null;
 
         // Parse annotation_set_item structures in field_annotations array.
-        fieldAnnotations = new HashMap<Integer, Annotation[]>(fieldsSize);
+        fieldAnnotations = new HashMap<FieldId, 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);
+            fieldAnnotations.put(dex.getFieldId(fieldIdx), annotations);
         }
 
         // Parse annotation_set_item structures in method_annotations array.
-        methodAnnotations = new HashMap<Integer, Annotation[]>(methodsSize);
+        methodAnnotations = new HashMap<MethodId, 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);
+            methodAnnotations.put(dex.getMethodId(methodIdx), annotations);
         }
 
         // Parse annotation_set_ref_list and contained annotation_set_item
         // structures in parameter_annotations array.
-        parameterAnnotations = new HashMap<Integer, Annotation[][]>();
+        parameterAnnotations = new HashMap<MethodId, Annotation[][]>();
         for (int i = 0; i < parametersSize; i++) {
             /*int methodIdx = buffer.getInt();
             int annotationsOff = buffer.getInt();
@@ -116,41 +116,85 @@
                 buf = dex.getDataBuffer(list[j]);
                 annotationSet[j] = parseAnnotations(buf, dex);
             }
-            parameterAnnotations.put(methodIdx, annotationSet);*/
+            parameterAnnotations.put(dex.getMethodId(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.
+     * Allows the given visitor to visit all class annotations present in this
+     * directory.
      * 
-     * @return An array containing the annotation items or {@code null}.
+     * @param visitor The given DEX class visitor object.
      */
-    public Annotation[] getClassAnnotations() {
-        return classAnnotations;
+    public void acceptClassAnnotations(DexClassVisitor visitor) {
+        if (classAnnotations != null)
+            for (Annotation annotation : classAnnotations) {
+                int visibility = annotation.visibility;
+                String type = annotation.annotationValue.type;
+                acceptAnnotation(visitor.visitAnnotation(visibility, type),
+                        annotation);
+            }
+    }
+
+    /**
+     * Allows the given visitor to visit all field for a given field present in
+     * this directory.
+     * 
+     * @param visitor The given DEX field visitor object.
+     * @param field The given field identifier.
+     */
+    public void acceptFieldAnnotations(DexFieldVisitor visitor, FieldId field) {
+        Annotation[] annotations = fieldAnnotations.get(field);
+        if (annotations != null)
+            for (Annotation annotation : annotations) {
+                int visibility = annotation.visibility;
+                String type = annotation.annotationValue.type;
+                acceptAnnotation(visitor.visitAnnotation(visibility, type),
+                        annotation);
+            }
     }
 
     /**
-     * Returns all field annotations for the given field index present in this
-     * directory or {@code null} if there are no such annotations.
+     * Allows the given visitor to visit all method and method parameter
+     * annotations for a given method present in this directory.
      * 
-     * @param idx The given field index.
-     * @return An array containing the annotation items or {@code null}.
+     * @param visitor The given DEX method visitor object.
+     * @param method The given method identifier.
      */
-    public Annotation[] getFieldAnnotations(int idx) {
-        return fieldAnnotations.get(idx);
+    public void acceptMethodAnnotations(DexMethodVisitor visitor,
+            MethodId method) {
+        Annotation[] annotations = methodAnnotations.get(method);
+        if (annotations != null)
+            for (Annotation annotation : annotations) {
+                int visibility = annotation.visibility;
+                String type = annotation.annotationValue.type;
+                acceptAnnotation(visitor.visitAnnotation(visibility, type),
+                        annotation);
+            }
+        Annotation[][] annotationSet = parameterAnnotations.get(method);
+        if (annotationSet != null)
+            for (int i = 0; i < annotationSet.length; i++)
+                for (Annotation annotation : annotationSet[i]) {
+                    int visibility = annotation.visibility;
+                    String type = annotation.annotationValue.type;
+                    acceptAnnotation(visitor.visitParameterAnnotation(i,
+                            visibility, type), annotation);
+                }
     }
 
     /**
-     * Returns all method annotations for the given method index present in this
-     * directory or {@code null} if there are no such annotations.
+     * Allows the given visitor to visit the given annotation.
      * 
-     * @param idx The given field index.
-     * @return An array containing the annotation items or {@code null}.
+     * @param visitor The given DEX annotation visitor or {@code null}.
+     * @param annotation The given annotation to visit.
      */
-    public Annotation[] getMethodAnnotations(int idx) {
-        return methodAnnotations.get(idx);
+    private static void acceptAnnotation(DexAnnotationVisitor visitor,
+            Annotation annotation) {
+        if (visitor == null)
+            return;
+        // XXX Visit the annotation!
+        visitor.visitEnd();
     }
 
     /**
@@ -186,8 +230,6 @@
         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	Sun Mar 20 19:51:58 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/ClassData.java	Sun Mar 20 20:56:43 2011 +0100
@@ -53,16 +53,16 @@
      * 
      * @param buffer The byte buffer to read from.
      * @param dex The DEX file currently being parsed.
-     * @param staticValuesOff Offset to the list of initial values for static
-     *            fields as stored in {@code class_def_item}, or 0 in case there
-     *            are none.
+     * @param classDef The class definition this data belongs to.
      * @return An object representing the parsed data.
      */
     public static ClassData parse(ByteBuffer buffer, DexFile dex,
-            int staticValuesOff) {
-        return new ClassData(buffer, dex, staticValuesOff);
+            ClassDef classDef) {
+        return new ClassData(buffer, dex, classDef);
     }
 
+    private final ClassDef classDef;
+
     private int staticFieldsSize;
 
     private int instanceFieldsSize;
@@ -87,7 +87,8 @@
 
     private final Object[] staticValues;
 
-    private ClassData(ByteBuffer buffer, DexFile dex, int staticValuesOff) {
+    private ClassData(ByteBuffer buffer, DexFile dex, ClassDef classDef) {
+        this.classDef = classDef;
         staticFieldsSize = BufferUtil.getULEB128(buffer);
         instanceFieldsSize = BufferUtil.getULEB128(buffer);
         directMethodsSize = BufferUtil.getULEB128(buffer);
@@ -146,6 +147,7 @@
         }
 
         // Parse encoded_array_item and contained encoded_value structures.
+        int staticValuesOff = classDef.getStaticValuesOff();
         if (staticValuesOff != 0) {
             ByteBuffer buf = dex.getDataBuffer(staticValuesOff);
             staticValues = EncodedValue.parseArray(buf, dex);
@@ -235,18 +237,22 @@
     /**
      * Helper method to visit a field.
      */
-    private static void acceptField(DexClassVisitor visitor, int access,
+    private void acceptField(DexClassVisitor visitor, int access,
             FieldId field, Object value) {
-        DexFieldVisitor dfv = visitor.visitField(access, field.getName(),
-                field.getTypeDescriptor(), value);
-        if (dfv != null)
-            dfv.visitEnd();
+        DexFieldVisitor dfv = visitor.visitField(access, field.getName(), field
+                .getTypeDescriptor(), value);
+        if (dfv == null)
+            return;
+        AnnotationsDirectory annotations = classDef.getAnnotations();
+        if (annotations != null)
+            annotations.acceptFieldAnnotations(dfv, field);
+        dfv.visitEnd();
     }
 
     /**
      * Helper method to visit a method.
      */
-    private static void acceptMethod(DexClassVisitor visitor, int access,
+    private void acceptMethod(DexClassVisitor visitor, int access,
             MethodId method, Code code) {
         ProtoId proto = method.getProtoId();
         DexMethodVisitor dmv = visitor
@@ -254,6 +260,9 @@
                         proto.getReturnType(), proto.getParameters());
         if (dmv == null)
             return;
+        AnnotationsDirectory annotations = classDef.getAnnotations();
+        if (annotations != null)
+            annotations.acceptMethodAnnotations(dmv, method);
         if (code != null)
             code.accept(dmv);
         dmv.visitEnd();
--- a/src/main/java/org/icedrobot/daneel/dex/ClassDef.java	Sun Mar 20 19:51:58 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/ClassDef.java	Sun Mar 20 20:56:43 2011 +0100
@@ -71,6 +71,8 @@
 
     private ClassData classData;
 
+    private final int staticValuesOff;
+
     private ClassDef(ByteBuffer buffer, DexFile dex) {
         int classIdx = buffer.getInt();
         accessFlags = buffer.getInt();
@@ -79,7 +81,7 @@
         int sourceFileIdx = buffer.getInt();
         int annotationsOff = buffer.getInt();
         int classDataOff = buffer.getInt();
-        int staticValuesOff = buffer.getInt();
+        staticValuesOff = buffer.getInt();
 
         // Resolve string and type indices.
         className = dex.getTypeDescriptor(classIdx);
@@ -106,7 +108,7 @@
         // Parse associated class_data_item structure.
         if (classDataOff != 0) {
             ByteBuffer buf = dex.getDataBuffer(classDataOff);
-            classData = ClassData.parse(buf, dex, staticValuesOff);
+            classData = ClassData.parse(buf, dex, this);
         }
     }
 
@@ -130,10 +132,18 @@
         return sourceFile;
     }
 
+    public AnnotationsDirectory getAnnotations() {
+        return annotations;
+    }
+
     public ClassData getClassData() {
         return classData;
     }
 
+    public int getStaticValuesOff() {
+        return staticValuesOff;
+    }
+
     /**
      * Allows the given visitor to visit this class definition.
      * 
@@ -143,6 +153,8 @@
         visitor.visit(accessFlags, className, superclass, interfaces);
         if (sourceFile != null)
             visitor.visitSource(sourceFile);
+        if (annotations != null)
+            annotations.acceptClassAnnotations(visitor);
         if (classData != null)
             classData.accept(visitor);
         visitor.visitEnd();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/org/icedrobot/daneel/dex/DexAnnotationVisitor.java	Sun Mar 20 20:56:43 2011 +0100
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+/**
+ * A visitor for annotations contained in DEX files.
+ */
+public interface DexAnnotationVisitor {
+
+    // XXX Define further methods!
+
+    /**
+     * Visits the end of the annotation.
+     */
+    void visitEnd();
+}
--- a/src/main/java/org/icedrobot/daneel/dex/DexClassVisitor.java	Sun Mar 20 19:51:58 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/DexClassVisitor.java	Sun Mar 20 20:56:43 2011 +0100
@@ -64,6 +64,18 @@
     void visitSource(String source);
 
     /**
+     * Visits an annotation of the class. In case this visitor is interested in
+     * further details about the annotation it should return a new visitor
+     * object, otherwise it should return {@code null}.
+     * 
+     * @param visibility The annotation's visibility flags.
+     * @param type The annotation's type as a type descriptor.
+     * @return A visitor object for the annotation or {@code null} if this
+     *         visitor is not interested in details about the annotation.
+     */
+    DexAnnotationVisitor visitAnnotation(int visibility, String type);
+
+    /**
      * Visits a field of the class. In case this visitor is interested in
      * further details about the field (i.e. annotations) it should return a new
      * visitor object, otherwise it should return {@code null}.
--- a/src/main/java/org/icedrobot/daneel/dex/DexFieldVisitor.java	Sun Mar 20 19:51:58 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/DexFieldVisitor.java	Sun Mar 20 20:56:43 2011 +0100
@@ -43,6 +43,18 @@
 public interface DexFieldVisitor {
 
     /**
+     * Visits an annotation of the field. In case this visitor is interested in
+     * further details about the annotation it should return a new visitor
+     * object, otherwise it should return {@code null}.
+     * 
+     * @param visibility The annotation's visibility flags.
+     * @param type The annotation's type as a type descriptor.
+     * @return A visitor object for the annotation or {@code null} if this
+     *         visitor is not interested in details about the annotation.
+     */
+    DexAnnotationVisitor visitAnnotation(int visibility, String type);
+
+    /**
      * Visits the end of the field.
      */
     void visitEnd();
--- a/src/main/java/org/icedrobot/daneel/dex/DexMethodVisitor.java	Sun Mar 20 19:51:58 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/DexMethodVisitor.java	Sun Mar 20 20:56:43 2011 +0100
@@ -42,6 +42,33 @@
  * A visitor for methods contained in DEX files.
  */
 public interface DexMethodVisitor {
+
+    /**
+     * Visits an annotation of the method. In case this visitor is interested in
+     * further details about the annotation it should return a new visitor
+     * object, otherwise it should return {@code null}.
+     * 
+     * @param visibility The annotation's visibility flags.
+     * @param type The annotation's type as a type descriptor.
+     * @return A visitor object for the annotation or {@code null} if this
+     *         visitor is not interested in details about the annotation.
+     */
+    DexAnnotationVisitor visitAnnotation(int visibility, String type);
+
+    /**
+     * Visits an annotation of a parameter to the method. In case this visitor
+     * is interested in further details about the annotation it should return a
+     * new visitor object, otherwise it should return {@code null}.
+     * 
+     * @param parameter The parameter index.
+     * @param visibility The annotation's visibility flags.
+     * @param type The annotation's type as a type descriptor.
+     * @return A visitor object for the annotation or {@code null} if this
+     *         visitor is not interested in details about the annotation.
+     */
+    DexAnnotationVisitor visitParameterAnnotation(int parameter,
+            int visibility, String type);
+
     /**
      * Starts visiting the method's code. The code is visited by subsequent
      * calls to {@code visitLabel()} and {@code visitInstr*()} methods.
--- a/src/main/java/org/icedrobot/daneel/dex/EncodedValue.java	Sun Mar 20 19:51:58 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/dex/EncodedValue.java	Sun Mar 20 20:56:43 2011 +0100
@@ -205,8 +205,8 @@
      * encoded_annotation} structure.
      */
     static class AnnotationValue {
-        private final String type;
-        private final Map<String, Object> elements;
+        final String type;
+        final Map<String, Object> elements;
 
         public AnnotationValue(String type, Map<String, Object> elements) {
             this.type = type;
--- a/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Sun Mar 20 19:51:58 2011 +0100
+++ b/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Sun Mar 20 20:56:43 2011 +0100
@@ -45,6 +45,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import org.icedrobot.daneel.dex.DexAnnotationVisitor;
 import org.icedrobot.daneel.dex.DexClassVisitor;
 import org.icedrobot.daneel.dex.DexFieldVisitor;
 import org.icedrobot.daneel.dex.DexMethodVisitor;
@@ -103,6 +104,12 @@
     }
 
     @Override
+    public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
+        // XXX Ignore annotations for now.
+        return null;
+    }
+
+    @Override
     public DexFieldVisitor visitField(int access, String name, String type,
             Object value) {
         final FieldVisitor fv = cv.visitField(access, name, type, null, value);
@@ -115,6 +122,13 @@
             public void visitEnd() {
                 fv.visitEnd();
             }
+
+            @Override
+            public DexAnnotationVisitor visitAnnotation(int visibility,
+                    String type) {
+                // XXX Ignore annotations for now.
+                return null;
+            }
         };
     }
 
@@ -448,6 +462,19 @@
         }
         
         @Override
+        public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
+            // XXX Ignore annotations for now.
+            return null;
+        }
+
+        @Override
+        public DexAnnotationVisitor visitParameterAnnotation(int parameter,
+                int visibility, String type) {
+            // XXX Ignore annotations for now.
+            return null;
+        }
+
+        @Override
         public void visitLabel(Label label) {
             Register[] registers = interpreter.getJoinPoint(label);   
             if (registers != null) { // target of an already seen jump