Different behaviors of Ref_InvokeSpecial
Mandy Chung
mandy.chung at oracle.com
Wed Oct 6 16:19:35 UTC 2021
Thanks for reporting.
I have created https://bugs.openjdk.java.net/browse/JDK-8274848 to
follow up this issue.
Mandy
On 10/5/21 6:43 AM, Zeker Zhayard wrote:
> 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
> <https://urldefense.com/v3/__https://github.com/openjdk/jdk/blob/0828273b898cca5368344e75f1c3f4c3a29dde80/src/java.base/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java*L157__;Iw!!ACWV5N9M2RV99hQ!f8mgBVrh6npGfZZryP5IRMvyk0GRQKNDfWCG1SLWi1I3WELqEodBYSAtr60b8cW6Dw$>
>
> Yours sincerely
>
> On Tue, Oct 5, 2021 at 5:00 AM Mandy Chung <mandy.chung at oracle.com
> <mailto: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