Different behaviors of Ref_InvokeSpecial
Zeker Zhayard
zekerzhayard at gmail.com
Tue Oct 5 13:43:30 UTC 2021
Hi,
I'm using:
$ java -version
openjdk version "1.8.0_302"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_302-b08)
OpenJDK 64-Bit Server VM (Temurin)(build 25.302-b08, mixed mode)
Yes, it is true that there will be no issues when compiling with javac
alone. However, if we change the access of a method from private to public
in .class file and do not change the invokespecial/ref_invokespecial of
this method in the class to invokevirtual/ref_invokevirtual, some different
behaviors will occur.
For invokespecial, it is not surprising that it will invoke the method in
super class no matter what java version is, but for ref_invokespecial,
before java 14, it will invoke super method, but after java 15, the
subclass method will be invoked.
Specific to this use case:
import java.util.function.Supplier;
public class ExampleClass {
public static void main(String[] args) {
System.out.println(new SubClass().test());
System.out.println(new SubClass().testWithLMF());
}
public String test() {
return this.testPrivate();
}
public String testWithLMF() {
Supplier<String> supplier = this::testPrivate;
return supplier.get();
}
private String testPrivate() {
return "123";
}
public static class SubClass extends ExampleClass {
public String testPrivate() {
return "456";
}
}
}
Compile it with java 8 and modify the access of testPrivate from private to
public in .class file (do not modify any invokespecial and
ref_invokespecial).
Java 14:
$ java ExampleClass
123
123
Java 15:
$ java ExampleClass
123
456
I think this result is not as expected. This part of the code may not be
thoughtful [1].
[1]
https://github.com/openjdk/jdk/blob/0828273b898cca5368344e75f1c3f4c3a29dde80/src/java.base/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java#L157
Yours sincerely
On Tue, Oct 5, 2021 at 5:00 AM Mandy Chung <mandy.chung at oracle.com> wrote:
> (valhalla-dev is a proper mailing list to discuss that. I bcc jdk-dev).
>
> Hi Zeker,
>
> Which version of javac are you using? I compiled it with JDK 8u331 and
> can't reproduce the problem.
>
> $ jdk1.8.0_311.jdk/bin/java -version
> java version "1.8.0_311-ea"
> Java(TM) SE Runtime Environment (build 1.8.0_311-ea-b01)
> Java HotSpot(TM) 64-Bit Server VM (build 25.311-b01, mixed mode)
>
> $ jdk1.8.0_311.jdk/bin/javac -version
> javac 1.8.0_311-ea
>
> $ jdk1.8.0_311.jdk/bin/javac ExampleClass.java
> $ jdk1.8.0_311.jdk/bin/java ExampleClass
> 123
>
> $ jdk-15.jdk/bin/java -version
> java version "15" 2020-09-15
> Java(TM) SE Runtime Environment (build 15+36-1562)
> Java HotSpot(TM) 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)
>
> $ jdk-15.jdk/bin/java ExampleClass
> 123
>
>
> Note that the JVMS change related to invokespecial is part of JEP 181
> Nest-based Access Control. The hidden classes generated by
> LambdaMetaFactory is a nestmate of the target class and so the bytecode
> generated was fixed in JEP 371 to use invokevirtual or invokeinterface per
> JVMS.
>
> The bootstrap method javac generated is REF_invokeSpecial in your
> ExampleClass. LMF does special handle it for compatibility for existing
> code to behave as in older releases.
>
> BootstrapMethods:
> 0: #34 REF_invokeStatic
> java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
> Method arguments:
> #35 ()Ljava/lang/Object;
> #36 REF_invokeSpecial ExampleClass.testPrivate:()Ljava/lang/String;
> #37 ()Ljava/lang/String;
>
>
> I don't see any issue from ExampleClass.
>
> Mandy
>
> On 10/4/21 2:50 AM, Zeker Zhayard wrote:
>
> Hi,
> JEP 371 describes that to invoke private nestmate instance methods from
> code in a hidden class, use invokevirtual or invokeinterface instead of
> invokespecial and generated bytecode that uses invokespecial to invoke a
> private nestmate instance method will fail verification. invokespecial
> should only be used to invoke private nestmate constructors.
> However, consider the following use case:
>
> import java.util.function.Supplier;
>
> public class ExampleClass {
> public static void main(String[] args) {
> System.out.println(new SubClass().test());
> }
>
> public String test() {
> Supplier<String> supplier = this::testPrivate;
> return supplier.get();
> }
>
> private String testPrivate() {
> return "123";
> }
>
> public static class SubClass extends ExampleClass {
> public String testPrivate() {
> return "456";
> }
> }
> }
>
> 1. Compile it with Java 8
> 2. Modify the access of ExampleClass#testPrivate to protected in bytecode
> 3. Running in Java 15+ will get "456" rather than "123" in Java 8~14
>
>
>
More information about the jdk-dev
mailing list