Я играл с ASM , и я считаю, что мне удалось добавить модификатор final в поле экземпляра класса; однако затем я приступил к созданию экземпляра указанного класса и вызвал для него установщик, который успешно изменил значение поля now-final. Я делаю что-то не так с моими изменениями байт-кода, или в конечном итоге применяется только компилятор Java?
Обновление: (31 июля) Вот код для вас. Основные части
- простой POJO с
private int x
и private final int y
,
- MakeFieldsFinalClassAdapter, который делает каждое поле, которое он посещает, окончательным, если это не так,
- и AddSetYMethodVisitor, который заставляет метод setX () POJO также устанавливать y в то же значение, которое он установил для x.
Другими словами, мы начинаем с класса с одним финальным (x) и одним не финальным (y) полем. Мы делаем х финал. Мы делаем setX () set y в дополнение к настройке x. Мы бегаем. И x, и y устанавливаются без ошибок. Код находится на github . Вы можете клонировать его с помощью:
git clone git://github.com/zzantozz/testbed.git tmp
cd tmp/asm-playground
Две вещи, на которые следует обратить внимание: Причина, по которой я задал этот вопрос в первую очередь: и поле, которое я сделал финальным, и поле, которое уже было финальным, могут быть установлены с тем, что я считаю обычные инструкции байт-кода.
Еще одно обновление: (1 августа) Протестировано с 1.6.0_26-b03 и 1.7.0-b147 с одинаковыми результатами. То есть JVM успешно изменяет конечные поля во время выполнения.
Окончательное (?) Обновление: (19 сентября)
Я удаляю полный источник из этого поста, потому что он был довольно длинным, но все еще доступен на github (см. Выше).
Полагаю, я убедительно доказал , что JDK7 JVM нарушает спецификацию . (См. Отрывок в ответе Стивена .) После использования ASM для изменения байт-кода, как описано ранее, я записал его обратно в файл класса. Используя отличный JD-GUI , этот файл класса декомпилируется в следующий код:
package rds.asm;
import java.io.PrintStream;
public class TestPojo
{
private final int x;
private final int y;
public TestPojo(int x)
{
this.x = x;
this.y = 1;
}
public int getX() {
return this.x;
}
public void setX(int x) {
System.out.println("Inside setX()");
this.x = x; this.y = x;
}
public String toString()
{
return "TestPojo{x=" +
this.x +
", y=" + this.y +
'}';
}
public static void main(String[] args) {
TestPojo pojo = new TestPojo(10);
System.out.println(pojo);
pojo.setX(42);
System.out.println(pojo);
}
}
Краткий взгляд на это должен сказать, что класс никогда не скомпилируется из-за переназначения конечного поля, и, тем не менее, запуск этого класса в обычном ванильном JDK 6 или 7 выглядит следующим образом:
$ java rds.asm.TestPojo
TestPojo{x=10, y=1}
Inside setX()
TestPojo{x=42, y=42}
- Кто-нибудь еще имеет информацию, прежде чем я сообщу об ошибке на этом?
- Кто-нибудь может подтвердить, является ли это ошибкой в JDK 6 или только в 7?