VarHandle is showing below error -
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)
My Program is :
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
}
}
This seems to be a bug in the JVM/JDK/Spec/Doc, which depends on how the signature of a signature polymorphic method is translated by the compiler.
compareAndSet
is marked with @MethodHandle.PolymorphicSignature
. This means semantically (paraphrased) that whatever signature is found at the call site will be used to invoke the method. This mainly prevents the boxing of the arguments.
The full signature of compareAndSet
in VarHandle is:
public final native
@MethodHandle.PolymorphicSignature
@HotSpotIntrinsicCandidate
boolean compareAndSet(Object... args);
Note that it returns a boolean
, but the error shows us the VM is trying to link VarHandle.compareAndSet(VarHandleExample,int,int)void
, which has a different return type. This can be seen in the bytecode generated by the Eclipse compiler as well:
publicIntHandle.compareAndSet(this, 10, 100); // CAS
Is (partly) translated as:
25: invokevirtual #55 // Method java/lang/invoke/VarHandle.compareAndSet:(LVarHandleExample;II)V
(Note the comment there which shows us the signature of the method reference constant in the constant pool which is used to link the method)
So at runtime it seems that the VM will try and find a method with V
(i.e. void
) as a return type, which indeed doesn't exist.
javac
on the other hand generates this signature:
25: invokevirtual #11 // Method java/lang/invoke/VarHandle.compareAndSet:(LVarHandleExample;II)Z
Where the return type is Z
(meaning boolean
) instead of V
.
You can work around this by explicitly making the return type boolean
, either by using the return value:
boolean b = publicIntHandle.compareAndSet(this, 10, 100); // CAS
Or by using a blank if
in case you don't need the value:
if(publicIntHandle.compareAndSet(this, 10, 100)); // CAS
Now on to the language lawyer part.
I could find limited info about signature polymorphic methods (the ones marked with @PolymorphicSignature) [1] (nothing in the language spec). There doesn't seem to be any mandate about how a descriptor for a signature polymorphic method should be derived and translated by the compiler in the specification.
Perhaps most interesting is this passage from jvms-5.4.3.3 (emphasis mine):
If C declares exactly one method with the name specified by the method reference, and the declaration is a signature polymorphic method (§2.9.3), then method lookup succeeds. All the class names mentioned in the descriptor are resolved (§5.4.3.1).
The resolved method is the signature polymorphic method declaration. It is not necessary for C to declare a method with the descriptor specified by the method reference.
Where C
in this case would be VarHandle
, the method being looked up would be compareAndSet
, and the descriptor either (LVarHandleExample;II)Z
or (LVarHandleExample;II)V
depending on the compiler.
Also interesting is the javadoc about Signature Polymorphism:
When the JVM processes bytecode containing signature polymorphic calls, it will successfully link any such call, regardless of its symbolic type descriptor. (In order to retain type safety, the JVM will guard such calls with suitable dynamic type checks, as described elsewhere.)
VarHandle
does have exactly one method with the name compareAndSet
and it is signature polymorphic, so the lookup should succeed. Imho it is a problem with the VM that an exception is thrown in this case, since the descriptor, and therefore the return type should not matter according to the specification.
There also seems to be a problem with javac emitting Z
as a return type in the descriptor. According to that same javadoc section:
The unusual part is that the symbolic type descriptor is derived from the actual argument and return types, not from the method declaration.
However, the descriptor emitted by javac definitely depends on the method declaration.
So there seem to be 2 bugs here, according to the spec/doc;
in the VM you're using, which incorrectly fails to link a signature polymorphic method. I can also reproduce it with OpenJDK 64-Bit Server VM (build 13-internal+0-adhoc.Jorn.jdk, mixed mode, sharing)
, which is the latest OpenJDK source.
in javac
which emits the wrong return type for the descriptor.
I'm assuming the specification as the primary authority, but it seems more likely in this case that the specification/documentation just wasn't updated after the implementation was, and that's what's biting eclipsec.
I got a response to my email to jdk-dev, answered by Dan Smith.
For 2.)
javac is correct here. See jls-15.12.3-400-B
"If the signature polymorphic method is either void or has a return type other than Object, the compile-time result is the result of the invocation type of the compile-time declaration"
The informal description in javadoc that you reference is incomplete, and I've filed a bug to fix that: https://bugs.openjdk.java.net/browse/JDK-8216511
So it appears that Eclipse is generating the wrong descriptor for the call, not javac.
For 1.)
You are correct. A link-time NoSuchMethodError is not specified here. Rather, per the VarHandle javadoc, we should see a run-time WrongMethodTypeException.
Bug report: https://bugs.openjdk.java.net/browse/JDK-8216520
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With