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