Извлечь дескриптор метода Array Clone через общий поиск - PullRequest
0 голосов
/ 09 мая 2018

Недавно я попытался получить доступ к методу clone типа массива через библиотеку java.lang.invoke.Это оказалось неудачным.В примере используется метод клонирования, например:

int[] a = new int[]{1, 2, 3, 4};
int[] b = a.clone();

Я хочу создать MethodHandle для вызова a.clone().Таким образом, результирующий код похож на:

int[] a = new int[]{1, 2, 3, 4};
int[] b = findCloneMethod().invoke(a);

Я настроил эту систему для каждого вызова другого метода.Однако система терпит неудачу только с этим методом.

Этот вопрос относится к библиотеке java.lang.invoke, not * java.lang.reflect.

Описание проблемы

Следующий пример кода был введен, чтобы показать это поведение.

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class Main {

    void test() {
        final MethodHandles.Lookup caller = MethodHandles.lookup();
        final Class<?> refc = int[].class;
        final String name = "clone";

        final MethodType type = MethodType.methodType(Object.class);

        final MethodHandle mh = findVirtual(caller, refc, name, type);

        System.out.println("Lookup: " + caller);
        System.out.println("mh: " + mh);
    }

    public static void main(String[] args) {
        new Main().test();
    }

    private MethodHandle findVirtual(final MethodHandles.Lookup caller, final Class<?> refc, final String name, final MethodType type) {
        try {
            return caller.findVirtual(refc, name, type);
        } catch (final Throwable t) {
            t.printStackTrace();
            return null;
        }
    }
}

Пример вывода:

Поиск: Главный

mh: MethodHandle (Main) Object


Вывод имеет смысл.Вызывающий объект из области Main видит только метод clone в суперклассе Main: Object.Даже если int[] используется в качестве ссылочного класса, этот метод невидим.Это становится проблемой, поскольку, если используется этот сайт вызова, JVM пытается привести int[] к Main.

Если ввести ссылочный тип как int[], можно ожидать, что метод clone в типе int[] общедоступен.Поэтому, когда документация предлагает:

Тип дескриптора метода будет типом метода, с добавленным типом получателя (обычно refc). 1

Что тип mh будет (int[])Object.


Попытка решения:

Поскольку Lookup caller создает помехи для правильного clone метода, была сделана попытка с использованием открытого поиска:

final MethodHandles.Lookup caller = MethodHandles.publicLookup();

Однако это не удалось.Простая проверка подписи Object#clone() показывает, почему:

protected Object clone() throws CloneNotSupportedException { ...

Метод protected.Это не относится к типам массивов, так как реализация делает этот метод общедоступным.

Примером этого может быть попытка доступа к методу Object#equals.Это показано с тремя отдельными ссылочными классами:

final MethodHandles.Lookup caller = MethodHandles.publicLookup();
final Class<?> refc = Object.class;
final String name = "equals";

final MethodType type = MethodType.methodType(boolean.class, Object.class);

Lookup: java.lang.Object / public

mh: MethodHandle (Object, Object) логический

А затем с использованием Main.class в качестве базового класса:

final Class<?> refc = Main.class;

Lookup: java.lang.Object / public

mh: MethodHandle (Main, Object)логическое

И затем использование int[].class в качестве базового класса:

final Class<?> refc = int[].class

Lookup: Main

mh: MethodHandle (int [],Объект) логическое

Это предполагаемое поведение, очевидно, работает бегло.В первом случае ссылочный класс был Object и возвращал дескриптор метода Object#equals.Во втором ссылочный класс был Main и возвращал дескриптор метода Main#equals.И наконец, эталонный класс был int[].class и возвращал дескриптор метода int[]#equals.

Этот паритет не поддерживается с помощью метода int[]#clone.Поскольку метод clone защищен, но общедоступен для массивов, его невозможно найти.Мне это кажется ошибкой.Метод массива clone должен быть общедоступным через publicLookup.Возвращаемый тип дескриптора метода может быть: MethodHandle(Object)Object.

После получения MethodHandle#asType можно затем использовать для исправления типа.В этом случае он будет приведен к MethodHandle(int[])Object.

Без общедоступного метода Object#clone, по крайней мере для типов массивов, это кажется невозможным.

Более того, это кажется единственным методом, где это может произойти, так кактипы массивов расширяют / реализуют только 3 класса: Object, Cloneable и Serializable.Единственным другим методом будет Object#finalize, который также защищен;однако типы массивов не делают этот метод общедоступным.Поэтому clone - единственный метод, для которого это не удается.


tl; др:

Метод массива clone, такой как Object[]#clone(), не доступен для общего доступа через открытый поиск. Это потому что Object#clone это protected; однако типы массивов делают этот метод общедоступным. Попытка получить доступ к массиву clone через ссылочный класс не удалась из-за этой проблемы видимости. В результате для этого метода не может быть сгенерировано MethodHandle. Тем не менее, использование тех же методов для открытых методов, таких как Object#equals, прекрасно работает для типов массивов.


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

Есть ли способ сгенерировать MethodHandle для метода массива clone?


Примечание: я понимаю правильное использование java.lang.invoke и не собираюсь использовать его в качестве замены java.lang.reflect.

1 Ответ

0 голосов
/ 10 мая 2018

В ранних реализациях JSR 292 была ошибка JDK-8001105 , где findVirtual не работал для метода clone массива. Однако эта проблема была исправлена ​​давно.

Итак, в недавнем JDK 8 findVirtual отлично работает для clone, если вызывается publicLookup().
Вы можете найти специальный случай для этого в JDK исходном коде :

        if (Modifier.isProtected(mods) &&
                refKind == REF_invokeVirtual &&
                m.getDeclaringClass() == Object.class &&
                m.getName().equals("clone") &&
                refc.isArray()) {
            // The JVM does this hack also.
            // (See ClassVerifier::verify_invoke_instructions
            // and LinkResolver::check_method_accessability.)
            // Because the JVM does not allow separate methods on array types,
            // there is no separate method for int[].clone.
            // All arrays simply inherit Object.clone.
            // But for access checking logic, we make Object.clone
            // (normally protected) appear to be public.
            // Later on, when the DirectMethodHandle is created,
            // its leading argument will be restricted to the
            // requested array type.
            // N.B. The return type is not adjusted, because
            // that is *not* the bytecode behavior.
            mods ^= Modifier.PROTECTED | Modifier.PUBLIC;
        }
...