From 5abca7388d32146b5abede47d37864be199c7a3c Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 19 May 2015 15:11:30 +1000 Subject: [PATCH] [indystructural] Prototype structural calls using invokedynamic See also: https://github.com/retronym/indy-structural ``` // sandbox/structural.scala object Test { def main(args: Array[String]): Unit = { println(duckduck(new C)) println(duckduck(new D)) } def duckduck(a: { def quack(a: String): String }): String = { a.quack("A") } } class C { def quack(a: String) = "QUACK! " + a } class D { def quack(a: String) = "QUICK! " + a } ``` ``` qscalac -target:jvm-1.8 -Ybackend:GenBCode sandbox/structural.scala && qscala Test && javap -v 'Test$' | cat -v warning: there was one feature warning; re-run with -feature for details one warning found QUACK! A QUICK! A Classfile /Users/jason/code/scala2/Test$.class { public static final Test$ MODULE$; descriptor: LTest$; flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL public void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC Code: stack=4, locals=2, args_size=2 0: getstatic #19 // Field scala/Predef$.MODULE$:Lscala/Predef$; 3: aload_0 4: new #21 // class C 7: dup 8: invokespecial #22 // Method C."":()V 11: invokevirtual #26 // Method duckduck:(Ljava/lang/Object;)Ljava/lang/String; 14: invokevirtual #30 // Method scala/Predef$.println:(Ljava/lang/Object;)V 17: getstatic #19 // Field scala/Predef$.MODULE$:Lscala/Predef$; 20: aload_0 21: new #32 // class D 24: dup 25: invokespecial #33 // Method D."":()V 28: invokevirtual #26 // Method duckduck:(Ljava/lang/Object;)Ljava/lang/String; 31: invokevirtual #30 // Method scala/Predef$.println:(Ljava/lang/Object;)V 34: return public java.lang.String duckduck(java.lang.Object); descriptor: (Ljava/lang/Object;)Ljava/lang/String; flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_1 1: ldc #38 // String A 3: invokedynamic #49, 0 // InvokeDynamic #0:"dyn:callMethod:quack":(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String; 8: checkcast #51 // class java/lang/String 11: areturn } BootstrapMethods: 0: #45 invokestatic jdk/internal/dynalink/DefaultBootstrapper.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method arguments: ``` --- .../nsc/backend/jvm/BCodeBodyBuilder.scala | 18 ++++++++++++++++++ .../scala/tools/nsc/transform/CleanUp.scala | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 40ba0c010b6f..8db80b14c93f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -311,6 +311,17 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case app : Apply => generatedType = genApply(app, expectedType) + case app @ ApplyDynamic(qual, args) if settings.target == "jvm.1.8" => + genLoad(qual) + val symbol = app.symbol + val paramTypes = symbol.info.paramTypes map toTypeKind + genLoadArguments(args, paramTypes) + val argAsmTypes = symbol.info.params.map(symInfoTK).map(_.toASMType) + val returnAsmType = toTypeKind(symbol.info.resultType).toASMType + val invokedType = asm.Type.getMethodType(returnAsmType, toTypeKind(qual.tpe).toASMType +: argAsmTypes: _*) + val name = "dyn:callMethod:" + symbol.name + mnode.visitInvokeDynamicInsn(name, invokedType.getDescriptor, dynalinkBoostrapHandle) + case ApplyDynamic(qual, args) => sys.error("No invokedynamic support yet.") case This(qual) => @@ -1324,4 +1335,11 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { definitions.LambdaMetaFactory.fullName('/'), sn.AltMetafactory.toString, "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;") + // TODO use custom boostrap method in our runtime that employs ChainedCallSite to cache recently used linkage results + // TODO use "org.dynalang" % "dynalink" rather than relying on Nashorn internals + lazy val dynalinkBoostrapHandle = + new asm.Handle(asm.Opcodes.H_INVOKESTATIC, + "jdk/internal/dynalink/DefaultBootstrapper", "bootstrap", + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;") + } diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index c29826551bdd..96fca4f20061 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -446,7 +446,7 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { * refinement, where the refinement defines a parameter based on a * type variable. */ - case tree: ApplyDynamic => + case tree: ApplyDynamic if !(settings.isBCodeActive && settings.target.value == "jvm-1.8") => transformApplyDynamic(tree) /* Some cleanup transformations add members to templates (classes, traits, etc).