Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for @PolymorphicSignature, required for using MethodHandles from Scala #7965

Closed
scabug opened this issue Nov 11, 2013 · 6 comments
Closed
Assignees
Labels
Milestone

Comments

@scabug
Copy link

scabug commented Nov 11, 2013

Java 7 has added MethodHandle's which can take any arguments types and their result can be cast to any return type.
The signature polymorphism was introduced that "connects this freedom of invocation directly to the JVM execution stack"

Currently the code

        val mt = MethodType.methodType(classOf[Int]);
        val mh = MethodHandles.lookup().findVirtual(classOf[java.lang.String], "length", mt)
        var  s = mh.invoke("bla").asInstanceOf[Int]

compiles to

        0: getstatic     #17                 // Field java/lang/Integer.TYPE:Ljava/lang/Class;
       3: invokestatic  #23                 // Method java/lang/invoke/MethodType.methodType:(Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
       6: astore_1      
       7: invokestatic  #29                 // Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
      10: ldc           #31                 // class java/lang/String
      12: ldc           #33                 // String length
      14: aload_1       
      15: invokevirtual #39                 // Method java/lang/invoke/MethodHandles$Lookup.findVirtual:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
      18: astore_2      
      19: aload_2       
      20: iconst_1      
      21: anewarray     #5                  // class java/lang/Object
      24: dup           
      25: iconst_0      
      26: ldc           #41                 // String bla
      28: aastore       
      29: invokevirtual #47                 // Method java/lang/invoke/MethodHandle.invoke:([Ljava/lang/Object;)Ljava/lang/Object;
      32: invokestatic  #53                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
      35: istore_3

has the wrong call in the end, as compiler shouldn't have boxed "bla" to array

The equivalent to java code

               MethodType mt = MethodType.methodType(int.class);
                MethodHandle mh = lookup.findVirtual(java.lang.String.class, "length", mt);
                int s = 0;
                s = (int)mh.invoke("bla");

compiles to this

      0: invokestatic  #2                  // Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
       3: astore_1      
       4: getstatic     #13                 // Field java/lang/Integer.TYPE:Ljava/lang/Class;
       7: invokestatic  #4                  // Method java/lang/invoke/MethodType.methodType:(Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
      10: astore_2      
      11: aload_1       
      12: ldc           #3                  // class java/lang/String
      14: ldc           #14                 // String length
      16: aload_2       
      17: invokevirtual #6                  // Method java/lang/invoke/MethodHandles$Lookup.findVirtual:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
      20: astore_3      
      21: iconst_0      
      22: istore        4
      24: aload_3       
      25: ldc           #15                 // String bla
      27: invokevirtual #16                 // Method java/lang/invoke/MethodHandle.invoke:(Ljava/lang/String;)I
      30: istore        4

You can see that "bla" in the last call was just forwarded to invoke, without being boxed, and the return value is being treated AS an int, not being casted to it.

Such behaviour should be used when method is marked with @PolymorphicSignature annotation.

@scabug
Copy link
Author

scabug commented Nov 11, 2013

Imported From: https://issues.scala-lang.org/browse/SI-7965?orig=1
Reporter: @DarkDimius
Affected Versions: 2.10.3

@scabug
Copy link
Author

scabug commented Nov 11, 2013

@DarkDimius said:
How it should work on bytecode-level is defined here: http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandle.html#

{quote}As is usual with virtual methods, source-level calls to invokeExact and invoke compile to an invokevirtual instruction. More unusually, the compiler must record the actual argument types, and may not perform method invocation conversions on the arguments. Instead, it must push them on the stack according to their own unconverted types. The method handle object itself is pushed on the stack before the arguments. The compiler then calls the method handle with a symbolic type descriptor which describes the argument and return types.

To issue a complete symbolic type descriptor, the compiler must also determine the return type. This is based on a cast on the method invocation expression, if there is one, or else Object if the invocation is an expression or else void if the invocation is a statement. The cast may be to a primitive type (but not void).

As a corner case, an uncasted null argument is given a symbolic type descriptor of java.lang.Void. The ambiguity with the type Void is harmless, since there are no references of type Void except the null reference.
{quote}

@scabug
Copy link
Author

scabug commented Nov 12, 2013

@DarkDimius said:
The biggest problem it that you'll compile, but you'll fail in runtime.

@scabug
Copy link
Author

scabug commented Nov 20, 2014

@retronym said:
Here's a fun technique to facilitate experimentation with invokeExact without resorting to writing Java code!

scala> :paste -raw
// Entering paste mode (ctrl-D to finish)

package pack { object O { private def foo = "foo"; val lookup = java.lang.invoke.MethodHandles.lookup } }

// Exiting paste mode, now interpreting.

scala> val mt = java.lang.invoke.MethodType.methodType(classOf[String], Array[Class[_]]())
mt: java.lang.invoke.MethodType = ()String

scala> val mh = pack.O.lookup.findVirtual(pack.O.getClass, "foo", mt)
mh: java.lang.invoke.MethodHandle = MethodHandle(O$)String

scala> mh.invokeExact(pack.O) // won't work as Scala doesn't respect the @PolymorphicSignature of invokeExact
java.lang.invoke.WrongMethodTypeException: expected (O$)String but found (Object[])Object
  at java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:340)
  at java.lang.invoke.Invokers.checkExactType(Invokers.java:351)
  ... 33 elided

scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'.          **
** scala.tools.nsc._ has been imported      **
** global._, definitions._ also imported    **
** Try  :help, :vals, power.<tab>           **

scala> val invokeExact = typeOf[java.lang.invoke.MethodHandle].member(TermName("invokeExact"))
invokeExact: $r.intp.global.Symbol = method invokeExact

scala> val origInfo = invokeExact.info
origInfo: $r.intp.global.Type = (x$1: Object*)Object

scala> val newInfo = JavaMethodType(invokeExact.newValueParameter(TermName("x")).setInfo(typeOf[pack.O.type]) :: Nil, StringTpe)
newInfo: $r.intp.global.JavaMethodType = (x: pack.O.type)String

scala> invokeExact.setInfo(newInfo)
res1: invokeExact.type = method invokeExact

scala> mh.invokeExact(pack.O) // Let's try again!
res2: String = foo

I was messing around with MethodHandles looking for ways to use access private methods without our usual mangling and publicising. Each Scala class that needs to expose its privates could publish a single, static, SYNTHETIC field named '$LOOKUP'. We could use a static cache for the MethodHandle at each call site, much like we do for structural types.

@scabug
Copy link
Author

scabug commented Nov 20, 2014

@retronym said:
I've prototyped compiler support for this, It is actually much less invasive than I feared: https://github.com/retronym/scala/compare/ticket/7965?expand=1

@scabug scabug closed this as completed Dec 23, 2014
@scabug
Copy link
Author

scabug commented Dec 23, 2014

@adriaanm said:
scala/scala#4139

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants