changeset 121:03ad7dd3dec1

Implemented rewriting of annotations. * dex/AnnotationsDirectory.java: Moved constants into public interface. * dex/DexAnnotationVisitor.java: Added visibility constants. * rewriter/DexRewriter.java (visitAnnotation): Rewrite runtime-visible annotations for classes, fields and methods. * (AnnotationRewriter): New internal rewriter for annotation parameters.
author Michael Starzinger <michi@complang.tuwien.ac.at>
date Fri, 08 Apr 2011 16:47:50 +0200
parents 08de240ca6d8
children 1f55b68e19b0
files src/main/java/org/icedrobot/daneel/dex/AnnotationsDirectory.java src/main/java/org/icedrobot/daneel/dex/DexAnnotationVisitor.java src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java src/test/java/org/icedrobot/daneel/rewriter/AnnotationTest.java
diffstat 4 files changed, 184 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/org/icedrobot/daneel/dex/AnnotationsDirectory.java	Mon Apr 04 23:41:06 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/dex/AnnotationsDirectory.java	Fri Apr 08 16:47:50 2011 +0200
@@ -253,10 +253,6 @@
      * 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;
-
         final int visibility;
         final AnnotationValue annotationValue;
 
--- a/src/main/java/org/icedrobot/daneel/dex/DexAnnotationVisitor.java	Mon Apr 04 23:41:06 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/dex/DexAnnotationVisitor.java	Fri Apr 08 16:47:50 2011 +0200
@@ -42,6 +42,15 @@
  */
 public interface DexAnnotationVisitor {
 
+    /** Annotation should be visible at compile time only. */
+    public static final int VISIBILITY_BUILD   = 0x00;
+
+    /** Annotation should be visible at runtime. */
+    public static final int VISIBILITY_RUNTIME = 0x01;
+
+    /** Annotation should be visible to the VM only. */
+    public static final int VISIBILITY_SYSTEM  = 0x02;
+
     /**
      * Visits a primitive annotation parameter. The primitive value is passed as
      * a boxed value. The {@code null} reference is passed as {@code null}
--- a/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Mon Apr 04 23:41:06 2011 +0200
+++ b/src/main/java/org/icedrobot/daneel/rewriter/DexRewriter.java	Fri Apr 08 16:47:50 2011 +0200
@@ -58,6 +58,7 @@
 import org.icedrobot.daneel.dex.Label;
 import org.icedrobot.daneel.dex.Opcode;
 import org.icedrobot.daneel.util.TypeUtil;
+import org.objectweb.asm.AnnotationVisitor;
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.ClassWriter;
 import org.objectweb.asm.FieldVisitor;
@@ -113,7 +114,12 @@
 
     @Override
     public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
-        // XXX Ignore annotations for now.
+        if (visibility == DexAnnotationVisitor.VISIBILITY_RUNTIME) {
+            AnnotationVisitor av = cv.visitAnnotation(type, true);
+            if (av == null)
+                return null;
+            return new AnnotationRewriter(av);
+        }
         return null;
     }
 
@@ -134,7 +140,12 @@
             @Override
             public DexAnnotationVisitor visitAnnotation(int visibility,
                     String type) {
-                // XXX Ignore annotations for now.
+                if (visibility == DexAnnotationVisitor.VISIBILITY_RUNTIME) {
+                    AnnotationVisitor av = fv.visitAnnotation(type, true);
+                    if (av == null)
+                        return null;
+                    return new AnnotationRewriter(av);
+                }
                 return null;
             }
         };
@@ -321,7 +332,12 @@
 
         @Override
         public DexAnnotationVisitor visitAnnotation(int visibility, String type) {
-            // XXX Ignore annotations for now.
+            if (visibility == DexAnnotationVisitor.VISIBILITY_RUNTIME) {
+                AnnotationVisitor av = mv.visitAnnotation(type, true);
+                if (av == null)
+                    return null;
+                return new AnnotationRewriter(av);
+            }
             return null;
         }
 
@@ -1147,8 +1163,59 @@
             mv.visitMaxs(-1, -1);
             mv.visitEnd();
         }
-    }
-    
+    };
+
+    private static class AnnotationRewriter implements DexAnnotationVisitor {
+        private final AnnotationVisitor av;
+
+        public AnnotationRewriter(AnnotationVisitor av) {
+            this.av = av;
+        }
+
+        @Override
+        public DexAnnotationVisitor visitAnnotation(String name, String type) {
+            AnnotationVisitor av = this.av.visitAnnotation(name, type);
+            if (av == null)
+                return null;
+            return new AnnotationRewriter(av);
+        }
+
+        @Override
+        public DexAnnotationVisitor visitArray(String name, int size) {
+            AnnotationVisitor av = this.av.visitArray(name);
+            if (av == null)
+                return null;
+            return new AnnotationRewriter(av);
+        }
+
+        @Override
+        public void visitField(String name, String fieldOwner,
+                String fieldName, String fieldDesc) {
+            throw new UnsupportedOperationException("Unexpected parameter");
+        }
+
+        @Override
+        public void visitMethod(String name, String methodOwner,
+                String methodName, String methodDesc) {
+            throw new UnsupportedOperationException("Unexpected parameter");
+        }
+
+        @Override
+        public void visitPrimitive(String name, Object value) {
+            av.visit(name, value);
+        }
+
+        @Override
+        public void visitType(String name, String typeDesc) {
+            av.visit(name, Type.getType(typeDesc));
+        }
+
+        @Override
+        public void visitEnd() {
+            av.visitEnd();
+        }
+    };
+
     static final int[] toJavaOpcode;
     static { // this string is generated using DalvikToJVMEncoder
         String text = "AA@@@@@@@@@@@@@@@@@@@@@@@@@@LB@@@@@@@@@@@@@@@@@@@@@@@@@@@@MCMDMAMBLOLLLM@@@@@@LPKHKHKH@@@@JFJGJHJIJE@@@@KBKCKDKE@@@@JLJMJNJO@@@@@@@@@@@@@@@@DCDDDDDEDF@@@@FDFEFEFFFG@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@HE@@HF@@HGHHIFIGIHIIIJIKILIMINIOIPJAJBJCJDGAGEGIGMHAGAIAICHIHKHMGBGFGJGNHBHPIBIDHJHLHNGCGGGKGOHCGDGHGLGPHDGAGEGIGMHAHOIAICHIHKHMGBGFGJGNHBHPIBIDHJHLHNGCGGGKGOHCGDGHGLGPHDGAGEGIGMHAGAIAICGAGEGIGMHAHOIAICHIHKHM@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/java/org/icedrobot/daneel/rewriter/AnnotationTest.java	Fri Apr 08 16:47:50 2011 +0200
@@ -0,0 +1,103 @@
+/*
+ * 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.rewriter;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.icedrobot.daneel.DexifyingRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DexifyingRunner.class)
+public class AnnotationTest {
+
+    @Test
+    public void testClassAnnotation() {
+        Annotation[] a = DEXCode.class.getAnnotations();
+        assertEquals(1, a.length);
+        assertSame(TestAnnotationA.class, a[0].annotationType());
+    }
+
+    @Test
+    public void testFieldAnnotation() throws Exception {
+        Annotation[] a = DEXCode.class.getField("field").getAnnotations();
+        assertEquals(1, a.length);
+        assertSame(TestAnnotationB.class, a[0].annotationType());
+    }
+
+    @Test
+    public void testMethodAnnotation() throws Exception {
+        Annotation[] a = DEXCode.class.getMethod("method").getAnnotations();
+        assertEquals(1, a.length);
+        assertSame(TestAnnotationC.class, a[0].annotationType());
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ ElementType.TYPE })
+    private static @interface TestAnnotationA {
+    };
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ ElementType.FIELD })
+    private static @interface TestAnnotationB {
+    };
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ ElementType.METHOD })
+    private static @interface TestAnnotationC {
+    };
+
+    // Keep this class named "DEXCode" to push it through Daneel.
+    @TestAnnotationA
+    public static class DEXCode {
+
+        @TestAnnotationB
+        public int field;
+
+        @TestAnnotationC
+        public void method() {
+        }
+    };
+}