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