ASM объединяет два объекта-значения в интерпретаторе
Я продолжаю исследовать эмуляцию метода и получать реальное значение при прохождении инструкции ILOAD. После помощи Хольгера с Интерпретатором и после добавления новых операций с локальной переменной в main()
метод, который я застрял с merge(V a, V b)
метод, который должен быть переопределен при расширении Interpreter
,
@Override
public LocalValue merge(LocalValue valueA, LocalValue valueB) {
if (Objects.equals(valueA, valueB)) return valueA;
else return new LocalValue(basicInterpreter.merge(valueA.type, valueB.type), null);
}
Но похоже это не правильно написано. Я могу попробовать разные логические переменные, что возвращать, но не понимая, в каких случаях значения могут сливаться, я не могу этого найти. Там нет полезной информации, которую я пытался найти в учебнике по javadocs и asm-4. Итак, что мне нужно вернуть, когда:
- Одно значение равно нулю, а другое - нет
- Оба значения не нулевые, одного типа, но разные объекты (например, 0 и 5)
- Оба значения не являются нулевыми, разные типы
basicInterpreter:
private BasicInterpreter basicInterpreter = new BasicInterpreter();
LocalValue:
public static class LocalValue implements Value {
Object value;
BasicValue type;
public LocalValue(BasicValue type, Object value) {
this.value = value;
this.type = type;
}
@Override public int getSize() {return type.getSize();}
@Override public String toString() {return value == null ? "null" : value.toString();}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof LocalValue)) return false;
LocalValue otherV = (LocalValue) obj;
return Objects.equals(otherV.type, type) && Objects.equals(otherV.value, value);
}
}
1 ответ
Значения необходимо объединять, когда инструкция может быть достигнута через разные пути кода, например, когда у вас есть условные выражения, циклы или обработчики исключений.
Поэтому, когда значение одинаково, независимо от того, какой путь к коду был выбран, вы можете сохранить его, иначе значение больше не является предсказуемой константой. Так в моем коде, где null
был использован для обозначения неизвестных значений, он всегда возвращает null
когда значения отличаются.
Так что, когда у вас есть код вроде
void foo(int arg) {
int i = 1;
int j = arg%2==0? i: arg;
}
Значения для arg
, i
и значение в стеке операндов объединяется непосредственно перед присваиванием j
, arg
уже имеет непредсказуемое значение, i
имеет значение 1
в каждом пути кода, но значение в стеке операндов, которое должно быть назначено j
имеет разные значения, 1
или "неизвестно", в зависимости от того, какой путь к коду был взят.
Вы можете решить сохранить набор возможных значений, если хотите, но когда одно из возможных значений "неизвестно", результатом объединения будет любое значение, следовательно, "неизвестно".