java.lang.NoSuchMethodError: VarHandle.compareAndSet (VariableHandlesExample, State, State) void - PullRequest
0 голосов
/ 24 октября 2018

VarHandle показывает ошибку ниже -

Exception in thread "main" java.lang.NoSuchMethodError: VarHandle.compareAndSet(VarHandleExample,int,int)void
    at java.base/java.lang.invoke.MethodHandleNatives.newNoSuchMethodErrorOnVarHandle(MethodHandleNatives.java:492)
    at java.base/java.lang.invoke.MethodHandleNatives.varHandleOperationLinkerMethod(MethodHandleNatives.java:445)
    at java.base/java.lang.invoke.MethodHandleNatives.linkMethodImpl(MethodHandleNatives.java:378)
    at java.base/java.lang.invoke.MethodHandleNatives.linkMethod(MethodHandleNatives.java:366)
    at j9.VarHandleExample.update(VarHandleExample.java:23)
    at j9.VarHandleExample.main(VarHandleExample.java:14)

Моя программа:

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;

public class VarHandleExample {
    public int publicTestVariable = 10;
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        VarHandleExample e= new VarHandleExample();
        e.update();
    }
    public void update() throws NoSuchFieldException, IllegalAccessException {
        VarHandle publicIntHandle = MethodHandles.lookup()
              .in(VariableHandlesTest.class)
              .findVarHandle(VarHandleExample.class, "publicTestVariable", int.class);
        publicIntHandle.compareAndSet(this, 10, 100); // CAS
    }
}

1 Ответ

0 голосов
/ 05 января 2019

Это, похоже, ошибка в JVM / JDK / Spec / Doc, которая зависит от того, как компилятор преобразует сигнатуру полиморфного метода подписи.


compareAndSet отмеченс @MethodHandle.PolymorphicSignature.Это означает семантически (перефразировано), что любая сигнатура, найденная на сайте вызова, будет использоваться для вызова метода.Это в основном предотвращает упаковку аргументов.

Полная подпись compareAndSet в VarHandle:

public final native
@MethodHandle.PolymorphicSignature
@HotSpotIntrinsicCandidate
boolean compareAndSet(Object... args);

Обратите внимание, что возвращается boolean, но ошибка показывает намВМ пытается связать VarHandle.compareAndSet(VarHandleExample,int,int)void, который имеет другой тип возврата.Это можно увидеть и в байт-коде, сгенерированном компилятором Eclipse:

publicIntHandle.compareAndSet(this, 10, 100); // CAS

(частично) переведен как:

25: invokevirtual #55 // Method java/lang/invoke/VarHandle.compareAndSet:(LVarHandleExample;II)V

(обратите внимание на комментарий, который показывает нам подписьссылочной константы метода в пуле констант, который используется для связывания метода)

Поэтому во время выполнения кажется, что виртуальная машина попытается найти метод с V (то есть void) в качестве возвратаТип, который действительно не существует.

javac с другой стороны генерирует эту подпись:

25: invokevirtual #11 // Method java/lang/invoke/VarHandle.compareAndSet:(LVarHandleExample;II)Z

Где тип возвращаемого значения Z (что означает boolean) вместоиз V.


Вы можете обойти эту проблему, явно указав тип возвращаемого значения boolean, либо используя возвращаемое значение:

boolean b = publicIntHandle.compareAndSet(this, 10, 100); // CAS

, либо используя пробелif в случае, если вам не нужно значение:

if(publicIntHandle.compareAndSet(this, 10, 100)); // CAS

Теперь перейдем к части юриста по языку.

Я мог бы найти ограниченную информацию о сигнатурных полиморфных методах (помеченные @PolymorphicSignature) [ 1 ] (ничего на языкеспецификации).Похоже, нет никаких полномочий относительно того, как дескриптор для полиморфного метода подписи должен быть получен и переведен компилятором в спецификации.

Возможно, наиболее интересным является этот отрывок из jvms-5.4.3.3 (выделено мной):

Если C объявляет ровно один метод с именем, указанным в ссылке на метод, а объявление является полиморфным методом подписи ( §2.9.3 ), тогда поиск метода завершается успешно.Все имена классов, упомянутые в дескрипторе, разрешаются (§5.4.3.1).

Разрешенный метод - это объявление метода полиморфной сигнатуры. Для C необязательно объявлять метод с дескриптором, указанным в ссылке на метод.

Где C в этом случае будет VarHandle, при этом метод являетсяпоиск был бы compareAndSet, а дескриптор (LVarHandleExample;II)Z или (LVarHandleExample;II)V в зависимости от компилятора.

Также интересен javadoc о Полиморфизм сигнатур :

Когда JVM обрабатывает байт-код, содержащий полиморфные вызовы подписи, он успешно связывает любой такой вызов независимо от своего дескриптора символьного типа .(Чтобы сохранить безопасность типов, JVM будет защищать такие вызовы с помощью подходящих динамических проверок типов, как описано в другом месте.)

VarHandle имеет только один метод с именем compareAndSet, и онявляется сигнатурным полиморфным, поэтому поиск должен завершиться успешно.Имхо, это проблема с виртуальной машиной, что в этом случае выдается исключение, так как дескриптор и, следовательно, тип возвращаемого значения не должны иметь значения в соответствии со спецификацией.

Кажется, также существует проблема с испусканием javacZ в качестве типа возврата в дескрипторе.В соответствии с тем же разделом javadoc:

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

Однако дескриптор, выдаваемый javac, определенно зависит от объявления метода.

Таким образом, согласно спецификации / doc здесь, похоже, есть 2 ошибки;

  1. в виртуальной машине, которую вы используете, которая неправильно не связывает полиморфный метод подписи.Я также могу воспроизвести его с помощью OpenJDK 64-Bit Server VM (build 13-internal+0-adhoc.Jorn.jdk, mixed mode, sharing), который является последним источником OpenJDK.

  2. в javac, который испускает неправильный тип возврата для дескриптора.

Я предполагаю, что спецификация является основной, но в этом случае она кажется более вероятнойчто спецификация / документация просто не была обновлена ​​после реализации, и это то, что кусает eclipsec.


Я получил ответ на мое письмо на jdk-dev , ответил Дэном Смитом.

За 2.)

Здесь правильный javac.См. jls-15.12.3-400-B

"Если полиморфный метод подписи либо void, либо имеет тип возвращаемого значения, отличный от Object, результат времени компиляции является результатомтип вызова объявления времени компиляции "

Неофициальное описание в javadoc, на которое вы ссылаетесь, является неполным, и я подал ошибку, чтобы исправить это: https://bugs.openjdk.java.net/browse/JDK-8216511

Итакпохоже, что Eclipse генерирует неверный дескриптор для вызова, а не javac.

For 1.)

Вы правы.Время ссылки NoSuchMethodError здесь не указано.Скорее, согласно javadoc VarHandle, мы должны увидеть WrongMethodTypeException во время выполнения.

Отчет об ошибке: https://bugs.openjdk.java.net/browse/JDK-8216520

...