Почему AspectJ генерирует пустую проверку Annotation? - PullRequest
0 голосов
/ 09 февраля 2019

Я использую AspectJ 1.8.8 ткачество во время компиляции, и у меня есть блок, подобный этому

@SomeAnnotation(value="someValue")
public List doSomething(String someArg) {
    ...
}

, где @SomeAnnotation реализован с советом "Around".

Глядя на байт-код с JD-GUI, я вижу следующий сгенерированный код (слегка отформатированный):

public class SomeClass {
  private static Annotation ajc$anno$5;

  ...

  @SomeAnnotation(value="someValue")
  public List doSomething(String someArg)
  {
    String str = someArg;
    JoinPoint localJoinPoint = Factory.makeJP(ajc$tjp_5, this, this, str);
    Object[] arrayOfObject = new Object[3];
    arrayOfObject[0] = this;
    arrayOfObject[1] = str;
    arrayOfObject[2] = localJoinPoint;
    Annotation tmp56_53 = ajc$anno$5;
    if (tmp56_53 == null) {
      tmp56_53;
    }
    return (List)new SomeClass.AjcClosure11(arrayOfObject).linkClosureAndJoinPoint(69648).around(tmp56_53, (SomeAnnotation)(ajc$anno$5 = SomeClass.class.getDeclaredMethod("doSomething", new Class[] { String.class }).getAnnotation(SomeAnnotation.class)));
  }
}

Мне было интересно, почему это условие (if (tmp56_53...)) даже существует, поскольку кажется, что оно ничего не делает (И также синтаксически неправильная Java? Может быть, потому что это было сгенерировано ajc?).Мне любопытно, потому что это вызывает "пропуски ветвей" в инструменте покрытия (JaCoCo).


Edit 1

Вот необработанный машинный код Java из javap:

       0: aload_1
       1: astore_2
       2: getstatic     #480                // Field ajc$tjp_10:Lorg/aspectj/lang/JoinPoint$StaticPart;
       5: aload_0
       6: aload_0
       7: aload_2
       8: invokestatic  #312                // Method org/aspectj/runtime/reflect/Factory.makeJP:(Lorg/aspectj/lang/JoinPoint$StaticPart;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Lorg/aspectj/lang/JoinPoint;
      11: astore_3
      12: invokestatic  #339                // Method com/foo/SomeAspect.aspectOf:()Lcom/foo/SomeAspect;
      15: iconst_3
      16: anewarray     #2                  // class java/lang/Object
      19: astore        4
      21: aload         4
      23: iconst_0
      24: aload_0
      25: aastore
      26: aload         4
      28: iconst_1
      29: aload_2
      30: aastore
      31: aload         4
      33: iconst_2
      34: aload_3
      35: aastore
      36: new           #484                // class com/foo/SomeClass$AjcClosure21
      39: dup
      40: aload         4
      42: invokespecial #485                // Method com/foo/SomeClass$AjcClosure21."<init>":([Ljava/lang/Object;)V
      45: ldc_w         #327                // int 69648
      48: invokevirtual #333                // Method org/aspectj/runtime/internal/AroundClosure.linkClosureAndJoinPoint:(I)Lorg/aspectj/lang/ProceedingJoinPoint;
      51: getstatic     #488                // Field ajc$anno$10:Ljava/lang/annotation/Annotation;
      54: dup
      55: ifnonnull     86
      58: pop
      59: ldc           #75                 // class com/foo/SomeClass
      61: ldc_w         #489                // String someArg
      64: iconst_1
      65: anewarray     #348                // class java/lang/Class
      68: dup
      69: iconst_0
      70: ldc           #171                // class java/lang/String
      72: aastore
      73: invokevirtual #352                // Method java/lang/Class.getDeclaredMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
      76: ldc_w         #341                // class com/foo/SomeAnnotation
      79: invokevirtual #358                // Method java/lang/reflect/Method.getAnnotation:(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;
      82: dup
      83: putstatic     #488                // Field ajc$anno$10:Ljava/lang/annotation/Annotation;
      86: nop
      87: checkcast     #341                // class com/foo/SomeAnnotation
      90: invokevirtual #362                // Method com/foo/SomeAspect.around:(Lorg/aspectj/lang/ProceedingJoinPoint;Lcom/foo/SomeAnnotation;)Ljava/lang/Object;
      93: pop
      94: return

Похоже, ifnonnull может быть условным вопросом, но я совсем не знаком с инструкциями JVM, и я до сих пор не знаю, почему AspectJ генерирует логику, подобную этой.

1 Ответ

0 голосов
/ 09 февраля 2019

tl; dr: это обычная ленивая инициализация, и jd просто сбит с толку.

Байт 16, где он создает это new Object[3]:

16: anewarray     #2                  // class java/lang/Object

Вы можете видетьпосле этого, в 19-35, просто копируют локальные переменные в стек (iconst для индекса, aload для ссылки) и затем записывают их в массив (aastore). немедленный следующий байт равен 36, что является оператором new (только выделение, затем сразу следует invokespecial для запуска конструктора).

Это приводит нас к байту48, который вызывает linkClosureAndJoinPoint.Вы не включили свою таблицу констант, но в 45 ldc_w #327 загружает постоянное значение 69648, так что это подводит нас к точке из .around.

Теперь происходит нечто интересноев байте 51. Один цепной вызов, который jd реконструировал, теперь прерван.Байт-код загружает поле статической аннотации ajc$anno$10 (а не 5, как говорит jd) в стек.Если это поле аннотации не равно нулю (55), то выполнение переходит к 86 (нет операции, используемой в качестве «точки посадки» для прыжка), которая выполняет эту проверку приведения ((SomeAnnotation)), а затем фактически вызываетсовет.

Код, который пропущен (58-82), говорит об этом, что вы узнаете по декомпиляции:

SomeClass.class
    .getDeclaredMethod("doSomething", new Class[] { String.class })
    .getAnnotation(SomeAnnotation.class)

Байт 83 затем сохраняет результат в статическом поле,и оттуда продолжается выполнение.

С точки зрения Java, это именно то, что происходит:

if (cachedAnnotation == null) {
    cachedAnnotation = getAnnotationOnMethodUsingReflection();
}

Байт-код AspectJ очень точный и чистый здесь (вероятно, оптимизированный вручную, потому что это, вероятно, будеточень горячий код).Либо из-за этого, либо из-за того, что эта логика прерывает цепной вызов метода, jd запутывается и разделяет нулевую проверку и присваивание.

...