# HG changeset patch # User Michael Starzinger # Date 1301952105 -7200 # Node ID 8c0170381072a6277ba1859e1ff0a99982ca630d # Parent 56844aab0e5c1edc64dce3aa211815e87dc9546f Adapted parser interface for annotations. * dex/AnnotationsDirectory.java: Implemented annotation parameter visiting. * dex/Code.java (acceptInsns): Switched to new method description getter. * dex/DexAnnotationVisitor.java: Defined rest of the interface. * dex/EncodedValue.java (parse): Implemented for type, field and method refs. * dex/MethodId.java (getMethodDesc): Added new method description getter. diff -r 56844aab0e5c -r 8c0170381072 src/main/java/org/icedrobot/daneel/dex/AnnotationsDirectory.java --- a/src/main/java/org/icedrobot/daneel/dex/AnnotationsDirectory.java Mon Apr 04 23:11:15 2011 +0200 +++ b/src/main/java/org/icedrobot/daneel/dex/AnnotationsDirectory.java Mon Apr 04 23:21:45 2011 +0200 @@ -42,6 +42,7 @@ import java.util.Map; import org.icedrobot.daneel.dex.EncodedValue.AnnotationValue; +import org.icedrobot.daneel.dex.EncodedValue.AnnotationVisitable; /** * A parser class capable of parsing {@code annotations_directory_item} @@ -193,11 +194,43 @@ Annotation annotation) { if (visitor == null) return; - // XXX Visit the annotation! + Map params = annotation.annotationValue.elements; + for (Map.Entry entry : params.entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); + acceptAnnotationParam(visitor, name, value); + } visitor.visitEnd(); } /** + * Allows the given visitor to visit the given annotation parameter. + * + * @param visitor The given DEX annotation visitor. + * @param name The annotation parameter name. + * @param value The annotation parameter value. + */ + private static void acceptAnnotationParam(DexAnnotationVisitor visitor, + String name, Object value) { + if (value instanceof AnnotationVisitable) { + AnnotationVisitable visitable = (AnnotationVisitable) value; + visitable.accept(visitor, name); + return; + } + if (value instanceof Object[]) { + Object[] array = (Object[]) value; + DexAnnotationVisitor v = visitor.visitArray(name, array.length); + if (v == null) + return; + for (Object element : array) + acceptAnnotationParam(v, null, element); + v.visitEnd(); + return; + } + visitor.visitPrimitive(name, value); + } + + /** * Helper method decoding an {@code annotation_set_item} array. * * @param buffer The byte buffer to read from. diff -r 56844aab0e5c -r 8c0170381072 src/main/java/org/icedrobot/daneel/dex/Code.java --- a/src/main/java/org/icedrobot/daneel/dex/Code.java Mon Apr 04 23:11:15 2011 +0200 +++ b/src/main/java/org/icedrobot/daneel/dex/Code.java Mon Apr 04 23:21:45 2011 +0200 @@ -47,7 +47,6 @@ import org.icedrobot.daneel.dex.DebugInfo.LineNumber; import org.icedrobot.daneel.dex.DebugInfo.LocalVariable; import org.icedrobot.daneel.util.BufferUtil; -import org.icedrobot.daneel.util.TypeUtil; /** * A parser class capable of parsing {@code code_item} structures as part of DEX @@ -588,10 +587,8 @@ // Format 35c B|A|op CCCC G|F|E|D // Syntax: op {vD, vE, vF, vG, vA}, meth@CCCC method = dex.getMethodId(u2); - string = TypeUtil.convertProtoToDesc(method.getProtoId() - .getReturnType(), method.getProtoId().getParameters()); v.visitInstrMethod(op, n1, n2, u3, method.getClassName(), - method.getName(), string); + method.getName(), method.getMethodDesc()); break; case INVOKE_VIRTUAL_RANGE: @@ -602,10 +599,8 @@ // Format 3rc AA|op BBBB CCCC // Syntax: op {vCCCC .. vNNNN}, meth@BBBB method = dex.getMethodId(u2); - string = TypeUtil.convertProtoToDesc(method.getProtoId() - .getReturnType(), method.getProtoId().getParameters()); v.visitInstrMethod(op, b1, u3, 0, method.getClassName(), - method.getName(), string); + method.getName(), method.getMethodDesc()); break; case CMPL_FLOAT: diff -r 56844aab0e5c -r 8c0170381072 src/main/java/org/icedrobot/daneel/dex/DexAnnotationVisitor.java --- a/src/main/java/org/icedrobot/daneel/dex/DexAnnotationVisitor.java Mon Apr 04 23:11:15 2011 +0200 +++ b/src/main/java/org/icedrobot/daneel/dex/DexAnnotationVisitor.java Mon Apr 04 23:21:45 2011 +0200 @@ -42,7 +42,75 @@ */ public interface DexAnnotationVisitor { - // XXX Define further methods! + /** + * Visits a primitive annotation parameter. The primitive value is passed as + * a boxed value. The {@code null} reference is passed as {@code null} + * value. + * + * @param name The annotation parameter name. + * @param value The boxed primitive value or a {@code null} reference. + */ + void visitPrimitive(String name, Object value); + + /** + * Visits a type-reference (i.e. class or interface) annotation parameter. + * The type is passed as a type descriptor. + * + * @param name The annotation parameter name. + * @param typeDesc The type's type descriptor. + */ + void visitType(String name, String typeDesc); + + /** + * Visits a field-reference annotation parameter. The field is passed as a + * combination of the field owner, name and type. + * + * @param name The annotation parameter name. + * @param fieldOwner The field's owner as a type descriptor. + * @param fieldName The field's name. + * @param fieldDesc The field's type descriptor. + */ + void visitField(String name, String fieldOwner, String fieldName, + String fieldDesc); + + /** + * Visits a method-reference annotation parameter. The field is passed as a + * combination of the method owner, name and type. + * + * @param name The annotation parameter name. + * @param methodOwner The method's owner as a type descriptor. + * @param methodName The method's name. + * @param methodDesc The method's method descriptor. + */ + void visitMethod(String name, String methodOwner, String methodName, + String methodDesc); + + /** + * Visits a sub-annotation annotation parameter. In case this visitor is + * interested in further details about the annotation parameter it should + * return a new visitor object, otherwise it should return {@code null}. + * + * @param name The annotation parameter name. + * @param type The sub-annotation's type as a type descriptor. + * @return A visitor object for the parameter or {@code null} if this + * visitor is not interested in details about the parameter. + */ + DexAnnotationVisitor visitAnnotation(String name, String type); + + /** + * Visits an array annotation parameter. In case this visitor is interested + * in further details about the annotation parameter it should return a new + * visitor object, otherwise it should return {@code null}. + *

+ * This nifty little trick to reduce interface complexity by reusing + * annotation visitors for arrays was kindly borrowed from ASM's interface. + * + * @param name The annotation parameter name. + * @param size The size of the array to be visited. + * @return A visitor object for the parameter or {@code null} if this + * visitor is not interested in details about the parameter. + */ + DexAnnotationVisitor visitArray(String name, int size); /** * Visits the end of the annotation. diff -r 56844aab0e5c -r 8c0170381072 src/main/java/org/icedrobot/daneel/dex/EncodedValue.java --- a/src/main/java/org/icedrobot/daneel/dex/EncodedValue.java Mon Apr 04 23:11:15 2011 +0200 +++ b/src/main/java/org/icedrobot/daneel/dex/EncodedValue.java Mon Apr 04 23:21:45 2011 +0200 @@ -107,12 +107,30 @@ 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); + final String t = dex.getTypeDescriptor((int) readU64(buffer, arg)); + return new AnnotationVisitable() { + public void accept(DexAnnotationVisitor visitor, String name) { + visitor.visitType(name, t); + } + }; + case VALUE_FIELD: + assertValueArg(arg, 3); + final FieldId f = dex.getFieldId((int) readU64(buffer, arg)); + return new AnnotationVisitable() { + public void accept(DexAnnotationVisitor visitor, String name) { + visitor.visitField(name, f.getClassName(), f.getName(), f + .getTypeDescriptor()); + } + }; case VALUE_METHOD: assertValueArg(arg, 3); - // XXX What kind of boxing should we use? - return "method@" + readU64(buffer, arg); + final MethodId m = dex.getMethodId((int) readU64(buffer, arg)); + return new AnnotationVisitable() { + public void accept(DexAnnotationVisitor visitor, String name) { + visitor.visitMethod(name, m.getClassName(), m.getName(), m + .getMethodDesc()); + } + }; case VALUE_ARRAY: assertValueArg(arg, 0); return parseArray(buffer, dex); @@ -222,6 +240,15 @@ } /** + * An internal interface to allow boxing of certain encoded values + * representing reflective references. Allows those boxed types to be + * visited by an annotation visitor. + */ + static interface AnnotationVisitable { + public void accept(DexAnnotationVisitor visitor, String name); + }; + + /** * An internal representation of an annotation as encoded in the {@code * encoded_annotation} structure. */ diff -r 56844aab0e5c -r 8c0170381072 src/main/java/org/icedrobot/daneel/dex/MethodId.java --- a/src/main/java/org/icedrobot/daneel/dex/MethodId.java Mon Apr 04 23:11:15 2011 +0200 +++ b/src/main/java/org/icedrobot/daneel/dex/MethodId.java Mon Apr 04 23:21:45 2011 +0200 @@ -39,6 +39,8 @@ import java.nio.ByteBuffer; +import org.icedrobot.daneel.util.TypeUtil; + /** * A parser class capable of parsing {@code method_id_item} structures as part * of DEX files. Keep package-private to hide internal API. @@ -85,4 +87,15 @@ public String getName() { return name; } + + /** + * Returns the method descriptor for this method. That descriptor is + * computed from various parts of the prototype identifier. + * + * @return The method descriptor. + */ + public String getMethodDesc() { + return TypeUtil.convertProtoToDesc(protoId.getReturnType(), protoId + .getParameters()); + } }