Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8233920

MethodHandles::tryFinally generates illegal bytecode for long/double return types

    XMLWordPrintable

Details

    Backports

      Description

        Code to reproduce:

        ```
        package main;

        import java.lang.invoke.MethodHandle;
        import java.lang.invoke.MethodHandles;

        public class Main {
            public static void main(String[] args) {
                MethodHandle doubleIdentity = MethodHandles.identity(double.class);
                MethodHandles.tryFinally(doubleIdentity,
                        MethodHandles.dropArguments(doubleIdentity, 0, Throwable.class));
            }
        }
        ```

        Run with either `-XX:+UnlockDiagnosticVMOptions -XX:+VerifyMethodHandles` to produce:

        =============== DEBUG MESSAGE: receiver not on stack ================

        #
        # A fatal error has been detected by the Java Runtime Environment:
        #
        # EXCEPTION_PRIV_INSTRUCTION (0xc0000096) at pc=0x00000212a403d6e5, pid=26184, tid=20820
        #
        # JRE version: OpenJDK Runtime Environment (12.0+33) (build 12+33)
        # Java VM: OpenJDK 64-Bit Server VM (12+33, mixed mode, sharing, tiered, compressed oops, g1 gc, windows-amd64)
        # Problematic frame:
        # v ~BufferBlob::MethodHandles adapters
        #
        # No core dump will be written. Minidumps are not enabled by default on client versions of Windows
        #
        # An error report file with more information is saved as:
        # J:\WS\Oxygen-Stable-New\test\hs_err_pid26184.log
        Could not load hsdis-amd64.dll; library not loadable; PrintAssembly is disabled
        #
        # If you would like to submit a bug report, please visit:
        # http://bugreport.java.com/bugreport/crash.jsp
        #

        Or with `-Xverify:all` to get:

        Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
        Exception Details:
          Location:
            java/lang/invoke/LambdaForm$MH.tryFinally(Ljava/lang/Object;D)D @41: swap
          Reason:
            Type double_2nd (current frame, stack[1]) is not assignable to category1 type
          Current Frame:
            bci: @41
            flags: { }
            locals: { 'java/lang/invoke/BoundMethodHandle$Species_LLLL', double, double_2nd, 'java/lang/Object', 'java/lang/Object', 'java/lang/Object', 'java/lang/Object' }
            stack: { double, double_2nd, 'java/lang/invoke/MethodHandle' }
          Bytecode:
            0000000: 2ac0 000e 594b b400 124e 2ab4 0015 3a04
            0000010: 2ab4 0018 3a05 2ab4 001b 3a06 2dc0 001f
            0000020: 27b6 0023 1904 c000 1f5f 015f 27b6 0026
            0000030: a700 1159 1904 c000 1f5f 0e27 b600 2657
            0000040: bfaf
          Exception Handler Table:
            bci [28, 36] => handler: 51
          Stackmap Table:
            full_frame(@51,{Object[#14],Double,Object[#4],Object[#4],Object[#4],Object[#4]},{Object[#29]})
            same_locals_1_stack_item_frame(@65,Double)

        at java.base/jdk.internal.misc.Unsafe.defineAnonymousClass0(Native Method)
        at java.base/jdk.internal.misc.Unsafe.defineAnonymousClass(Unsafe.java:1223)
        at java.base/java.lang.invoke.InvokerBytecodeGenerator.loadAndInitializeInvokerClass(InvokerBytecodeGenerator.java:295)
        at java.base/java.lang.invoke.InvokerBytecodeGenerator.loadMethod(InvokerBytecodeGenerator.java:287)
        at java.base/java.lang.invoke.InvokerBytecodeGenerator.generateCustomizedCode(InvokerBytecodeGenerator.java:693)
        at java.base/java.lang.invoke.LambdaForm.compileToBytecode(LambdaForm.java:871)
        at java.base/java.lang.invoke.LambdaForm.prepare(LambdaForm.java:829)
        at java.base/java.lang.invoke.MethodHandle.<init>(MethodHandle.java:468)
        at java.base/java.lang.invoke.BoundMethodHandle.<init>(BoundMethodHandle.java:54)
        at java.base/java.lang.invoke.BoundMethodHandle$Species_LLLL.<init>(java/lang/invoke/BoundMethodHandle$Species_LLLL)
        at java.base/java.lang.invoke.BoundMethodHandle$Species_LLLL.make(java/lang/invoke/BoundMethodHandle$Species_LLLL)
        at java.base/java.lang.invoke.MethodHandleImpl.makeTryFinally(MethodHandleImpl.java:2140)
        at java.base/java.lang.invoke.MethodHandles.tryFinally(MethodHandles.java:5950)
        at test/main.Main.main(Main.java:9)

        See the spec for swap: https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-4.html#jvms-4.10.1.9.swap "A swap instruction is type safe iff one can validly replace two category 1 types, Type1 and Type2, on the incoming operand stack with the types Type2 and Type1 yielding the outgoing type state."

        Where: "Category 1 types occupy a single stack entry" (so, it is not possible to use `swap` with a double or long operand)

        Relevant bytecode does this however:

        ```
              28: aload_3
              29: checkcast #31 // class java/lang/invoke/MethodHandle
              32: dload_1
              33: invokevirtual #35 // Method java/lang/invoke/MethodHandle.invokeBasic:(D)D
              36: aload 4
              38: checkcast #31 // class java/lang/invoke/MethodHandle
              41: swap
        ```

        Attachments

          Issue Links

            Activity

              People

                jvernee Jorn Vernee
                jvernee Jorn Vernee
                Votes:
                0 Vote for this issue
                Watchers:
                3 Start watching this issue

                Dates

                  Created:
                  Updated:
                  Resolved: