Как проверить, что операция байт-кода PUTFIELD переназначает поле, принадлежащее этому объекту, используя ObjectWeb ASM? - PullRequest
2 голосов
/ 28 августа 2010

Я использую инфраструктуру манипулирования байт-кодом ASM для выполнения статического анализа кода Java. Я хочу определить, когда поля объекта переназначаются, т. Е. Когда происходит такой код:

class MyObject {
    private int value;
    void setValue(int newValue) { this.value = newValue; }
}

Использование следующего кода (в классе, реализующем ClassVisitor) может обнаружить вышеуказанную ситуацию:

@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
    if(opcode == Opcodes.PUTFIELD) {
        // do whatever here
    }
}

Однако этот код вызывается независимо от объекта, которому принадлежит поле. Я хотел бы найти более конкретный случай, когда операция PUTFIELD выполняется на объекте this. Например, я хочу различать первый фрагмент кода и такой код:

public MyObject createNewObjectWithDifferentField() {
    MyObject newObject = new MyObject();
    newObject.value = 43;
    return newObject;
}

В приведенном выше случае операция PUTFIELD все еще выполняется, но здесь она выполняется с локальной переменной (newObject), а не с объектом this. Это будет зависеть от состояния стека во время назначения, но я столкнулся с несколькими различными сценариями, в которых байт-код полностью отличается, и я ищу способы справиться с этой сложностью.

Как проверить, что PUTFIELD переназначает поле, принадлежащее this объекту?


Редактировать

Я использую ASM только для анализа, а не для инструментирования существующего байт-кода. Предпочтительно я хотел бы найти способ обнаружить это, не изменяя байт-код, если это возможно.

Ответы [ 3 ]

2 голосов
/ 28 августа 2010

Я думаю, что в общем случае это невозможно. Рассмотрим:

class MyObject {
  private int value;
  void mymethod1() {
    mymethod2(Math.random() > 0.5 ? this : new MyObject());
  }

  void mymethod2(MyObject that) {
    that.value = 1;
  }
}

В более простых случаях вы можете отследить стек до ALOAD 0, который в методе экземпляра ссылается на this.

0 голосов
/ 29 августа 2010

Альтернативный подход (время выполнения):

Вы можете использовать AspectJ и настроить поля set / get pointcuts для вашего класса.См .: http://www.eclipse.org/aspectj/doc/released/progguide/semantics-pointcuts.html и http://www.eclipse.org/aspectj/.

После определения ваших pointcut, вы бы написали несколько советов, которые просто распечатывают текущее местоположение выполнения с помощью переменной thisJoinPoint.Затем при запуске вашей программы у вас будет хороший журнал везде, где поля были получены / установлены.

Это потребует ткачества во время выполнения или компиляции, что в любом случае означает манипулирование байт-кодом.Надеюсь, это поможет ...

0 голосов
/ 28 августа 2010

Я никогда не использовал ASM, однако у меня есть опыт работы с байт-кодом.

Прямо перед инструкцией PUTFIELD стек выглядит так:

|...,object_ref,value

или

|...,object_ref,value1,value2 (если тип поля двойной или длинный)

Принимая первый случай, вы можете вставить следующие инструкции перед PUTFIELD:

1: DUP2
2: POP
3: ALOAD_0
4: IF_ACMPNE X
5: put your code here
...
...
X: PUTFIELD

Инструкция (1) дублирует object_ref и значение в стеке.(2) удаляет значение.(3) загружает ссылку «это».(4) Если 'this' равно object_ref, выполнить ваш код, иначе ничего не делать и перейти к PUTFIELD.

Для второго случая (длинное или двойное поле) вы можете использовать эту серию инструкций байт-кода

1: DUP2_X1
2: POP2
3: DUP
4: ALOAD_0
5: IF_ACMPNE 7
6: put your code here
...
...
7: DUP_X2
8: POP
9: PUTFIELD
...