RFR: 8147755: ASM should create correct constant tag for invokestatic on handle point to interface static method

Yumin Qi yumin.qi at oracle.com
Fri Jan 22 20:22:06 UTC 2016


Hi, All

    Can I have a review for
    bug: https://bugs.openjdk.java.net/browse/JDK-8147755
    Webrev:  http://cr.openjdk.java.net/~minqi/8147755/webrev-01/

     Summary: When fix bug 8145148 to follow:

JVMS-5.4.3.3 Method Resolution:
  " If C is an interface, method resolution throws an 
IncompatibleClassChangeError."
JVMS-5.4.3.4 Interface Method Resolution:
  "If C is not an interface, interface method resolution throws an 
IncompatibleClassChangeError"

       In defmeth(default method) tests, a MethodHandle pointing to 
interface static method will violate  the spec. Since such a handle in 
asm will generate a tag of Methodref in constantpool for the call site, 
with the fix of 8145148, it requires a tag of InterfaceMethodref.

        Fix by create a new constructor of Handle with extra boolean to 
indicate if the handle points to interface static method.

         Tests:  A test case and resulted constantpool data attached.  
The fix will not affect existing code.
                     Also tested with fixed 8145148 and revised defmeth 
with the new version Handle and passed.

Thanks
Yumin


-------------- next part --------------
import jdk.internal.org.objectweb.asm.*;
import java.io.FileOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import static jdk.internal.org.objectweb.asm.Opcodes.*;

public class TestStaticInterfaceHandle {

    // interface I {
    //   static void m() {
    //     System.out.println("Hello from interface I, static m()!");
    //   }
    // }
    final static int version = 52;
    static byte[] dumpI() {
        ClassWriter cw = new ClassWriter(0);
        cw.visit(version, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, "I", null, "java/lang/Object", null);
        {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "m", "()V", null, null);
            mv.visitCode();
            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitLdcInsn("Hello from interface I, m()!");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
            mv.visitInsn(RETURN);
            mv.visitMaxs(3, 1);
            mv.visitEnd();
        }
        cw.visitEnd();
        return cw.toByteArray();
    }

    static byte[] dumpTestIm() {
        ClassWriter cw = new ClassWriter(0);
        cw.visit(version, ACC_PUBLIC | ACC_SUPER, "TestIm", null, "java/lang/Object", null);
        Handle handle =
            new Handle(Opcodes.H_INVOKESTATIC, "I", "m", "()V", true/* interface static version*/);

        {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
            mv.visitInsn(RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }

        {
            MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "staticCallIm", "()V", null, null);
            mv.visitCode();
            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitLdcInsn("Calling static I.m():");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false/*intf*/);
            mv.visitLdcInsn(handle);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invoke", "()V", false);
            mv.visitInsn(RETURN);
            mv.visitMaxs(3, 1);
            mv.visitEnd();
        }

        cw.visitEnd();
        return cw.toByteArray();
    }

    static class CL extends ClassLoader {
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] classBytes = null;
            switch (name) {
            case "TestIm"               : classBytes = dumpTestIm(); break;
            case "I"                    : classBytes = dumpI(); break;
            default                     : throw new ClassNotFoundException(name);
            }
            return defineClass(name, classBytes, 0, classBytes.length);
        }
    }


    public static void main(String[] args) throws Throwable {
        try (FileOutputStream fos = new FileOutputStream("I.class")) {
          fos.write(dumpI());
        }
        try (FileOutputStream fos = new FileOutputStream("TestIm.class")) {
          fos.write(dumpTestIm());
        }
        String cName = "TestIm";
        String mName = "staticCallIm";

        Class<?> cls = (new CL()).loadClass(cName);
        System.out.println("Test " + cName + "." + mName + ":");
        try {
            cls.getMethod(mName).invoke(cls.newInstance());
        } catch (Throwable e) {
            System.out.println("FAILED");
            throw e;
        }
    }
}


Classfile /scratch/yqi/ws/test/java/8147419/TestIm.class
  Last modified Jan 20, 2016; size 413 bytes
  MD5 checksum cafde171a1e44138b00e389d8bb944a1
public class TestIm
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Utf8               TestIm
   #2 = Class              #1             // TestIm
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             // java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = NameAndType        #5:#6          // "<init>":()V
   #8 = Methodref          #4.#7          // java/lang/Object."<init>":()V
   #9 = Utf8               staticCallIm
  #10 = Utf8               java/lang/System
  #11 = Class              #10            // java/lang/System
  #12 = Utf8               out
  #13 = Utf8               Ljava/io/PrintStream;
  #14 = NameAndType        #12:#13        // out:Ljava/io/PrintStream;
  #15 = Fieldref           #11.#14        // java/lang/System.out:Ljava/io/PrintStream;
  #16 = Utf8               Calling static I.m():
  #17 = String             #16            // Calling static I.m():
  #18 = Utf8               java/io/PrintStream
  #19 = Class              #18            // java/io/PrintStream
  #20 = Utf8               println
  #21 = Utf8               (Ljava/lang/String;)V
  #22 = NameAndType        #20:#21        // println:(Ljava/lang/String;)V
  #23 = Methodref          #19.#22        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #24 = Utf8               I
  #25 = Class              #24            // I
  #26 = Utf8               m
  #27 = NameAndType        #26:#6         // m:()V
  #28 = InterfaceMethodref #25.#27        // I.m:()V
  #29 = MethodHandle       #6:#28         // invokestatic I.m:()V
  #30 = Utf8               java/lang/invoke/MethodHandle
  #31 = Class              #30            // java/lang/invoke/MethodHandle
  #32 = Utf8               invoke
  #33 = NameAndType        #32:#6         // invoke:()V
  #34 = Methodref          #31.#33        // java/lang/invoke/MethodHandle.invoke:()V
  #35 = Utf8               Code
{
  public TestIm();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return

  public static void staticCallIm();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=1, args_size=0
         0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #17                 // String Calling static I.m():
         5: invokevirtual #23                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: ldc           #29                 // MethodHandle invokestatic I.m:()V
        10: invokevirtual #34                 // Method java/lang/invoke/MethodHandle.invoke:()V
        13: return
}



More information about the core-libs-dev mailing list