Skip to content

Commit 841389b

Browse files
committed
Merge pull request scala#4139 from retronym/ticket/7965
SI-7965 Support calls to MethodHandle.{invoke,invokeExact}
2 parents a0b11b3 + 4678729 commit 841389b

File tree

6 files changed

+90
-0
lines changed

6 files changed

+90
-0
lines changed

spec/06-expressions.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,19 @@ The final result of the transformation is a block of the form
410410
}
411411
```
412412

413+
### Signature Polymorphic Methods
414+
415+
For invocations of signature polymorphic methods of the target platform `$f$($e_1 , \ldots , e_m$)`,
416+
the invoked function has a different method type `($p_1$:$T_1 , \ldots , p_n$:$T_n$)$U$` at each call
417+
site. The parameter types `$T_ , \ldots , T_n$` are the types of the argument expressions
418+
`$e_1 , \ldots , e_m$` and `$U$` is the expected type at the call site. If the expected type is
419+
undefined then `$U$` is `scala.AnyRef`. The parameter names `$p_1 , \ldots , p_n$` are fresh.
420+
421+
###### Note
422+
423+
On the Java platform version 7 and later, the methods `invoke` and `invokeExact` in class
424+
`java.lang.invoke.MethodHandle` are signature polymorphic.
425+
413426
## Method Values
414427

415428
```ebnf

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3281,6 +3281,22 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
32813281
}
32823282
handleOverloaded
32833283

3284+
case _ if isPolymorphicSignature(fun.symbol) =>
3285+
// Mimic's Java's treatment of polymorphic signatures as described in
3286+
// https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.3
3287+
//
3288+
// One can think of these methods as being infinitely overloaded. We create
3289+
// a ficticious new cloned method symbol for each call site that takes on a signature
3290+
// governed by a) the argument types and b) the expected type
3291+
val args1 = typedArgs(args, forArgMode(fun, mode))
3292+
val pts = args1.map(_.tpe.deconst)
3293+
val clone = fun.symbol.cloneSymbol
3294+
val cloneParams = pts map (pt => clone.newValueParameter(currentUnit.freshTermName()).setInfo(pt))
3295+
val resultType = if (isFullyDefined(pt)) pt else ObjectTpe
3296+
clone.modifyInfo(mt => copyMethodType(mt, cloneParams, resultType))
3297+
val fun1 = fun.setSymbol(clone).setType(clone.info)
3298+
doTypedApply(tree, fun1, args1, mode, resultType).setType(resultType)
3299+
32843300
case mt @ MethodType(params, _) =>
32853301
val paramTypes = mt.paramTypes
32863302
// repeat vararg as often as needed, remove by-name

src/reflect/scala/reflect/internal/Definitions.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,8 @@ trait Definitions extends api.StandardDefinitions {
514514
lazy val ScalaSignatureAnnotation = requiredClass[scala.reflect.ScalaSignature]
515515
lazy val ScalaLongSignatureAnnotation = requiredClass[scala.reflect.ScalaLongSignature]
516516

517+
lazy val MethodHandle = getClassIfDefined("java.lang.invoke.MethodHandle")
518+
517519
// Option classes
518520
lazy val OptionClass: ClassSymbol = requiredClass[Option[_]]
519521
lazy val OptionModule: ModuleSymbol = requiredModule[scala.Option.type]
@@ -1508,6 +1510,9 @@ trait Definitions extends api.StandardDefinitions {
15081510

15091511
lazy val PartialManifestClass = getTypeMember(ReflectPackage, tpnme.ClassManifest)
15101512
lazy val ManifestSymbols = Set[Symbol](PartialManifestClass, FullManifestClass, OptManifestClass)
1513+
1514+
def isPolymorphicSignature(sym: Symbol) = PolySigMethods(sym)
1515+
private lazy val PolySigMethods: Set[Symbol] = Set[Symbol](MethodHandle.info.decl(sn.Invoke), MethodHandle.info.decl(sn.InvokeExact)).filter(_.exists)
15111516
}
15121517
}
15131518
}

src/reflect/scala/reflect/internal/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,7 @@ trait StdNames {
11471147
final val GetClassLoader: TermName = newTermName("getClassLoader")
11481148
final val GetMethod: TermName = newTermName("getMethod")
11491149
final val Invoke: TermName = newTermName("invoke")
1150+
final val InvokeExact: TermName = newTermName("invokeExact")
11501151

11511152
val Boxed = immutable.Map[TypeName, TypeName](
11521153
tpnme.Boolean -> BoxedBoolean,

src/reflect/scala/reflect/runtime/JavaUniverseForce.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
310310
definitions.QuasiquoteClass_api_unapply
311311
definitions.ScalaSignatureAnnotation
312312
definitions.ScalaLongSignatureAnnotation
313+
definitions.MethodHandle
313314
definitions.OptionClass
314315
definitions.OptionModule
315316
definitions.SomeClass

test/files/run/t7965.scala

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Test that scala doesn't apply boxing or varargs conversions to the
2+
// @PolymorphicSignature magical methods, MethodHandle#{invoke, invokeExact}
3+
object Test {
4+
val code = """
5+
6+
object O {
7+
private def foo = "foo"
8+
private def bar(x: Int): Int = -x
9+
private def baz(x: Box): Unit = x.a = "present"
10+
val lookup = java.lang.invoke.MethodHandles.lookup
11+
}
12+
13+
import java.lang.invoke._
14+
class Box(var a: Any)
15+
16+
object Test {
17+
def main(args: Array[String]): Unit = {
18+
def lookup(name: String, params: Array[Class[_]], ret: Class[_]) = {
19+
val mt = MethodType.methodType(ret, params)
20+
O.lookup.findVirtual(O.getClass, name, mt)
21+
}
22+
val fooResult = (lookup("foo", Array(), classOf[String]).invokeExact(O): Int)
23+
assert(fooResult == "foo")
24+
25+
val barResult = (lookup("bar", Array(classOf[Int]), classOf[Int]).invokeExact(O, 42): Int)
26+
assert(barResult == -42)
27+
28+
val box = new Box(null)
29+
(lookup("baz", Array(classOf[Box]), Void.TYPE).invokeExact(O, box) : Unit)
30+
assert(box.a == "present")
31+
32+
// Note: Application in statement position in a block in Java also infers return type of Unit,
33+
// but we don't support that, ascribe the type to Unit as above.
34+
// as done in Java.
35+
// lookup("baz", Array(classOf[Box]), Void.TYPE).invokeExact(O, box)
36+
()
37+
}
38+
}
39+
40+
"""
41+
def main(args: Array[String]): Unit = {
42+
if (util.Properties.isJavaAtLeast("1.7")) test()
43+
}
44+
45+
def test() {
46+
import scala.reflect.runtime._
47+
import scala.tools.reflect.ToolBox
48+
49+
val m = currentMirror
50+
val tb = m.mkToolBox()
51+
import tb._
52+
eval(parse(code))
53+
}
54+
}

0 commit comments

Comments
 (0)