1. Annotation的實現
1.1 Java實現的Annotation
java 的自定義註解實現,通常我們會先自定義一個註解
import java.lang.annotation.ElementType; import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,ElementType.TYPE_PARAMETER,ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) public @interface testAnnotation { boolean value() default false; } |
反編譯代碼如下表,其本質就是構建一個接口testAnnotation,繼承了java.lang.annotation.Annotation的接口,定義了一個抽象類 value,並且設置了AnnotationDefault: default_value: Z#10 而Z#10是一個integer的值0,也就是boolean的false
public interface testAnnotation extends java.lang.annotation.Annotation minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION Constant pool: #1 = Class #2 // pointto/testAnnotation #2 = Utf8 pointto/testAnnotation #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Class #6 // java/lang/annotation/Annotation #6 = Utf8 java/lang/annotation/Annotation #7 = Utf8 value #8 = Utf8 ()Z #9 = Utf8 AnnotationDefault #10 = Integer 0 #11 = Utf8 SourceFile #12 = Utf8 testAnnotation.java #13 = Utf8 RuntimeVisibleAnnotations #14 = Utf8 Ljava/lang/annotation/Target; #15 = Utf8 Ljava/lang/annotation/ElementType; #16 = Utf8 TYPE #17 = Utf8 FIELD #18 = Utf8 METHOD #19 = Utf8 PARAMETER #20 = Utf8 TYPE_PARAMETER #21 = Utf8 TYPE_USE #22 = Utf8 Ljava/lang/annotation/Retention; #23 = Utf8 Ljava/lang/annotation/RetentionPolicy; #24 = Utf8 RUNTIME { public abstract boolean value(); descriptor: ()Z flags: ACC_PUBLIC, ACC_ABSTRACT AnnotationDefault: default_value: Z#10} SourceFile: "testAnnotation.java" RuntimeVisibleAnnotations: 0: #14(#7=[e#15.#16,e#15.#17,e#15.#18,e#15.#19,e#15.#20,e#15.#21]) 1: #22(#7=e#23.#24)
|
接着我們會去定義我們自己的實現的annotation, 可以定義在field,class,method, parameter,localvar裏, 雖然註解在寫代碼的時候就已經確定了,但在實現的類裏面並不會去在編譯的時候去實現這個定義的接口,而是在字節碼裏標識了一下。
public void method(); descriptor: (Lpointto/Container;Z)V flags: ACC_PUBLIC RuntimeVisibleAnnotations: 0: #53(#54=Z#55) RuntimeVisibleParameterAnnotations: parameter 0: 0: #53(#54=Z#55) parameter 1: 0: #53(#54=Z#55) code: ……. RuntimeVisibleTypeAnnotations: 0: #53(#54=Z#55): LOCAL_VARIABLE, {start_pc=18, length=72, index=4} RuntimeVisibleTypeAnnotations: 0: #53(#54=Z#55): METHOD_FORMAL_PARAMETER, param_index=0 1: #53(#54=Z#55): METHOD_FORMAL_PARAMETER, param_index=1 |
定義了 RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations 以及對應的RuntimeVisibleTypeAnnotations
LOCAL_VARIABLE 不同的是,就直接定義了
RuntimeVisibleTypeAnnotations:
0: #53(#54=Z#55): LOCAL_VARIABLE, {start_pc=18, length=72, index=4}
並沒有定義RuntimeVisibleAnnotations
當通過class, method來獲取annotation的時候,JVM會通過AnnotationParser的方法來生成動態代理類
public static Annotation annotationForMap(final Class<? extends Annotation> var0, final Map<String, Object> var1) { return (Annotation)AccessController.doPrivileged(new PrivilegedAction<Annotation>() { public Annotation run() { return (Annotation)Proxy.newProxyInstance(var0.getClassLoader(), new Class[]{var0}, new AnnotationInvocationHandler(var0, var1)); } }); } |
設置
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
可以看到動態生成的類,生成路徑爲工作目錄下的com.sun.proxy.Proxy1, Proxy2…
生成的類如下:
public final class $Proxy1 extends Proxy implements testAnnotation { private static Method m1; private static Method m2; private static Method m3; private static Method m4; private static Method m0;
public $Proxy1(InvocationHandler var1) throws { super(var1); }
public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }
public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
public final int count() throws { try { return (Integer)super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
public final Class annotationType() throws { try { return (Class)super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("testAnnotation ").getMethod("value"); m4 = Class.forName("testAnnotation ").getMethod("annotationType"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } } |
調用AnnotationInvocationHandler的invoke方法來獲取值,
在AnnotationInvocationHandler 裏保留了
private final Map<String, Object> memberValues; Key是方法名,Value就是在你的類裏定義的annotation的值
1.2 soot實現annotation
Soot裏對annotation裏並沒有和JVM一樣生成的動態類,而是在SootClass, SootMethod, SootField裏使用了Tag來封裝annotation。
VisibilityAnnotationTag 代表AnnotationTag的可見屬性,但實際上不可見的AnnotationTag soot依然使用VisibilityAnnotationTag來表示,而對函數的參數Annotation,soot 裏使用VisibilityParameterAnnotationTag來表示,裏面是方法的所有參數的Annotation,而每一個Annotation的值使用AnnotationElem來表示,不同的值的類型對應不同的AnnotationElem,比較特例的是boolean,byte,short, char, int 都是使用int來表示這和JVM比較類似,我們看到在前面JVM曾今定義boolean 定義成int 1或者0。
在class裏定義的annotation會被加到SootClass裏使用Tag的list來表示,在field定義的annotation會被加到SootField裏的Tag的list
Method裏面有Method, Parameter, Local Variable 三種類型的Annotation, 但實際SootMethod裏只是封裝了Method, Parameter這兩種,Local Variable並沒有封裝,但語法樹是能分析成Annotation,可能認爲Local Variable會有很少的使用場景。
因爲Soot使用objectweb 來遍歷語法樹,objectweb可以自定義Visit 的鉤子函數,在分析過程語法樹的過程種可以調用用戶自定義的Visit,這樣不需要在構建完語法樹後再次遍歷語法樹來生成用戶自己的分析邏輯,而是在分析過程就可以構建用戶自己的邏輯提高效率,比如Soot的構建自己的SootClass, SootMethod, SootField,而Annotation在遍歷語法樹的時候就可以開始構建了。
結論:
1. Annotation並沒有和JVM一樣的方式通過構建動態代理類的方式來構建,這裏實現和Lambda不同,只是在SootClass, SootMethod, SootField裏構建了構建了AnnotationTag
2. Annotation 不會生成Jimple 的IR語言,因爲Annotation的值在寫代碼的時候就已經生成了,直接被保存在Tag裏就可以了,只要標識一下聲明就好了。