Причина, по которой это больше не работает в Java 12, связана с JDK-8210522 . Этот CSR говорит:
Основная информация
Базовое отражение имеет механизм фильтрации, позволяющий скрыть чувствительные к безопасности и целостности поля и методы от классов getXXXField (s) и getXXXMethod (s). Механизм фильтрации был использован для нескольких выпусков, чтобы скрыть чувствительные к безопасности поля, такие как System.security и Class.classLoader.
Этот CSR предлагает расширить фильтры, чтобы скрыть поля от ряда классов с высокой степенью безопасности в java.lang.reflect и java.lang.invoke.
Задача
Многие из классов в пакетах java.lang.reflect и java.lang.invoke имеют закрытые поля, которые при прямом доступе могут поставить под угрозу среду выполнения или привести к сбою виртуальной машины. В идеале все непубличные / незащищенные поля классов в java.base должны быть отфильтрованы по отражению ядра и недоступны для чтения / записи через небезопасный API, но в данный момент мы не находимся поблизости от этого. В то же время механизм фильтрации используется в качестве помощи полосы.
Решение
Расширить фильтр на все поля в следующих классах:
java.lang.ClassLoader
java.lang.reflect.AccessibleObject
java.lang.reflect.Constructor
java.lang.reflect.Field
java.lang.reflect.Method
и приватные поля в java.lang.invoke.MethodHandles.Lookup, которые используются для класса поиска и режима доступа.
Спецификация
Никаких изменений в спецификации нет, это фильтрация непубличных / незащищенных полей, на которые не следует полагаться ничего за пределами java.base. Ни один из классов не является сериализуемым.
По сути, они отфильтровывают поля java.lang.reflect.Field
, поэтому вы не можете злоупотреблять ими - как вы сейчас пытаетесь это сделать. Вы должны найти другой способ сделать то, что вам нужно; ответ Евгения , по-видимому, предоставляет хотя бы один вариант.
Примечание : Приведенный выше CSR указывает, что конечной целью является предотвращение любого отражающего доступа к внутреннему коду в модуле java.base
. Этот механизм фильтрации, по-видимому, влияет только на Core Reflection API, и его можно обойти, используя Invoke API. Я не совсем уверен, как связаны эти два API, поэтому, если это нежелательное поведение - за исключением сомнительного изменения статического конечного поля - кто-то должен отправить отчет об ошибке (проверить существующий первый). Другими словами, используйте приведенный ниже взлом на свой страх и риск ; попробуйте найти другой способ сделать то, что вам нужно сначала.
Тем не менее, похоже, что вы все еще можете взломать поле modifiers
, по крайней мере, в OpenJDK 12.0.1, используя java.lang.invoke.VarHandle
.
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public final class FieldHelper {
private static final VarHandle MODIFIERS;
static {
try {
var lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
MODIFIERS = lookup.findVarHandle(Field.class, "modifiers", int.class);
} catch (IllegalAccessException | NoSuchFieldException ex) {
throw new RuntimeException(ex);
}
}
public static void makeNonFinal(Field field) {
int mods = field.getModifiers();
if (Modifier.isFinal(mods)) {
MODIFIERS.set(field, mods & ~Modifier.FINAL);
}
}
}
Следующее использует вышеприведенное для изменения статического конечного поля EMPTY_ELEMENTDATA
внутри ArrayList
. Это поле используется, когда ArrayList
инициализируется с емкостью 0
. Конечным результатом является то, что созданный ArrayList
содержит элементы без добавления каких-либо элементов.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) throws Exception {
var newEmptyElementData = new Object[]{"Hello", "World!"};
updateEmptyElementDataField(newEmptyElementData);
var list = new ArrayList<>(0);
// toString() relies on iterator() which relies on size
var sizeField = list.getClass().getDeclaredField("size");
sizeField.setAccessible(true);
sizeField.set(list, newEmptyElementData.length);
System.out.println(list);
}
private static void updateEmptyElementDataField(Object[] array) throws Exception {
var field = ArrayList.class.getDeclaredField("EMPTY_ELEMENTDATA");
FieldHelper.makeNonFinal(field);
field.setAccessible(true);
field.set(null, array);
}
}
Выход:
[Hello, World!]
Используйте --add-opens
при необходимости.