From 532147c333a8dab79fc77c20162a4d752c6f6780 Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Mon, 30 Mar 2009 08:45:39 +0000 Subject: Code to support invoke-dynamic for structural t... Code to support invoke-dynamic for structural types. Not yet complete, built around the JSR 292 spec of December 2008. --- lib/fjbg.jar.desired.sha1 | 2 +- src/compiler/scala/tools/nsc/Settings.scala | 2 +- .../scala/tools/nsc/backend/icode/GenICode.scala | 1 + .../scala/tools/nsc/backend/jvm/GenJVM.scala | 26 ++++++++++++-- .../scala/tools/nsc/symtab/Definitions.scala | 7 ++++ .../scala/tools/nsc/transform/CleanUp.scala | 21 ++++++++++- .../scala/tools/nsc/typechecker/Typers.scala | 5 +-- src/library/scala/runtime/DynamicDispatch.java | 42 ++++++++++++++++++++++ 8 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 src/library/scala/runtime/DynamicDispatch.java diff --git a/lib/fjbg.jar.desired.sha1 b/lib/fjbg.jar.desired.sha1 index 55b4cb29b0..d6bf323a06 100644 --- a/lib/fjbg.jar.desired.sha1 +++ b/lib/fjbg.jar.desired.sha1 @@ -1 +1 @@ -a22d30fd058e068c86526b4a4cb16f7529583324 ?fjbg.jar +7f3fa70c5792a945bea5fd1fbad53be0a75541b6 ?fjbg.jar diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala index 8f8ddec2d9..f13e1cfa21 100644 --- a/src/compiler/scala/tools/nsc/Settings.scala +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -673,7 +673,7 @@ trait ScalacSettings val stop = PhasesSetting ("-Ystop", "Stop after phase") val refinementMethodDispatch = ChoiceSetting ("-Ystruct-dispatch", "Selects dispatch method for structural refinement method calls", - List("no-cache", "mono-cache", "poly-cache"), "poly-cache") . + List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache") . withHelpSyntax("-Ystruct-dispatch:") val Xwarndeadcode = BooleanSetting ("-Ywarn-dead-code", "Emit warnings for dead code") diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 69f0d1f826..471a7cc6f7 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -846,6 +846,7 @@ abstract class GenICode extends SubComponent { } case ApplyDynamic(qual, args) => + ctx.clazz.bootstrapClass = Some("scala.runtime.DynamicDispatch") val ctx1 = genLoad(qual, ctx, ANY_REF_CLASS) genLoadArguments(args, tree.symbol.info.paramTypes, ctx1) ctx1.bb.emit(CALL_METHOD(tree.symbol, InvokeDynamic), tree.pos) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index c2096fe161..8f17c000ee 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -72,7 +72,10 @@ abstract class GenJVM extends SubComponent { val BoxesRunTime = "scala.runtime.BoxesRunTime" val StringBuilderType = new JObjectType(StringBuilderClass) - val toStringType = new JMethodType(JObjectType.JAVA_LANG_STRING, JType.EMPTY_ARRAY) + val toStringType = new JMethodType(JObjectType.JAVA_LANG_STRING, JType.EMPTY_ARRAY) + val MethodTypeType = new JObjectType("java.dyn.MethodType") + val JavaLangClassType = new JObjectType("java.lang.Class") + val MethodHandleType = new JObjectType("java.dyn.MethodHandle") // Scala attributes val SerializableAttr = definitions.SerializableAttr @@ -191,7 +194,7 @@ abstract class GenJVM extends SubComponent { if (jclass.getName.endsWith("$")) jclass.addAttribute(getMarkerAttr(jclass)) - if (isStaticModule(c.symbol) || serialVUID != None) { + if (isStaticModule(c.symbol) || serialVUID != None || clasz.bootstrapClass.isDefined) { if (isStaticModule(c.symbol)) addModuleInstanceField; addStaticInit(jclass) @@ -212,6 +215,7 @@ abstract class GenJVM extends SubComponent { } } + if (clasz.bootstrapClass.isDefined) jclass.setBootstrapClass(clasz.bootstrapClass.get) clasz.fields foreach genField clasz.methods foreach genMethod @@ -675,9 +679,27 @@ abstract class GenJVM extends SubComponent { case None => () } + if (clasz.bootstrapClass.isDefined) emitBootstrapMethodInstall(clinit) + clinit.emitRETURN() } + /** Emit code that installs a boostrap method for invoke dynamic. It installs the default + * method, found in scala.runtime.DynamicDispatch. + */ + def emitBootstrapMethodInstall(jcode: JExtendedCode) { + jcode.emitPUSH(jclass.getType.asInstanceOf[JReferenceType]) + jcode.emitPUSH(new JObjectType("scala.runtime.DynamicDispatch")) + jcode.emitPUSH("bootstrapInvokeDynamic") + jcode.emitGETSTATIC("java.dyn.Linkage", "BOOTSTRAP_METHOD_TYPE", MethodTypeType) + jcode.emitDUP + jcode.emitINVOKESTATIC("scala.Console", "println", new JMethodType(JType.VOID, Array(JObjectType.JAVA_LANG_OBJECT))) + jcode.emitINVOKESTATIC("java.dyn.MethodHandles", "findStatic", + new JMethodType(MethodHandleType, Array(JavaLangClassType, JObjectType.JAVA_LANG_STRING, MethodTypeType))) + jcode.emitINVOKESTATIC("java.dyn.Linkage", "registerBootstrapMethod", + new JMethodType(JType.VOID, Array(JavaLangClassType, MethodHandleType))) + } + /** Add a forwarder for method m */ def addForwarder(jclass: JClass, module: Symbol, m: Symbol) { import JAccessFlags._ diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 0cecbc03c4..6623a41992 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -127,6 +127,13 @@ trait Definitions { def methodCache_find = getMember(MethodCacheClass, nme.find_) def methodCache_add = getMember(MethodCacheClass, nme.add_) lazy val EmptyMethodCacheClass: Symbol = getClass("scala.runtime.EmptyMethodCache") + + // invoke dynamic support + lazy val LinkageModule: Symbol = getModule("java.dyn.Linkage") + lazy val Linkage_invalidateCallerClass = getMember(LinkageModule, "invalidateCallerClass") + lazy val DynamicDispatchClass: Symbol = getModule("scala.runtime.DynamicDispatch") + lazy val DynamicDispatch_DontSetTarget: Symbol = getMember(DynamicDispatchClass, "DontSetTarget") + lazy val PredefModule: Symbol = getModule("scala.Predef") def Predef_classOf = getMember(PredefModule, nme.classOf) def Predef_classOfType(classType: Type): Type = diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index db88df5dc5..23496807d8 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -592,6 +592,25 @@ abstract class CleanUp extends Transform { ) } + def getClass(q: Tree): Tree = + Apply(Select(q, nme.getClass_), List()) + + if (settings.refinementMethodDispatch.value == "invoke-dynamic") { +/* val guardCallSite: Tree = { + val cachedClass = addStaticVariableToClass("cachedClass", definitions.ClassClass.tpe, EmptyTree) + val tmpVar = currentOwner.newVariable(ad.pos, unit.fresh.newName(ad.pos, "x")).setInfo(definitions.AnyRefClass.tpe) + atPos(ad.pos)(Block(List( + ValDef(tmpVar, transform(qual))), + If(Apply(Select(gen.mkAttributedRef(cachedClass), nme.EQ), List(getClass(Ident(tmpVar)))), + Block(List(Assign(gen.mkAttributedRef(cachedClass), getClass(Ident(tmpVar)))), + copy.ApplyDynamic(ad, Ident(tmpVar), transformTrees(params))), + EmptyTree))) + } + //println(guardCallSite) +*/ + localTyper.typed(copy.ApplyDynamic(ad, transform(qual), transformTrees(params))) + } else { + /* ### BODY OF THE TRANSFORMATION -> remember we're in case ad@ApplyDynamic(qual, params) ### */ /* This creates the tree that does the reflective call (see general comment @@ -659,7 +678,7 @@ abstract class CleanUp extends Transform { /* We return the dynamic call tree, after making sure no other * clean-up transformation are to be applied on it. */ transform(t) - + } /* ### END OF DYNAMIC APPLY TRANSFORM ### */ /* Some cleanup transformations add members to templates (classes, traits, etc). diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index b14f6724e2..dec6d58b45 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3373,9 +3373,10 @@ trait Typers { self: Analyzer => typedApply(fun, args) case ApplyDynamic(qual, args) => + val reflectiveCalls = !(settings.refinementMethodDispatch.value == "invoke-dynamic") val qual1 = typed(qual, AnyRefClass.tpe) - val args1 = List.mapConserve(args)(arg => typed(arg, AnyRefClass.tpe)) - copy.ApplyDynamic(tree, qual1, args1) setType AnyRefClass.tpe + val args1 = List.mapConserve(args)(arg => if (reflectiveCalls) typed(arg, AnyRefClass.tpe) else typed(arg)) + copy.ApplyDynamic(tree, qual1, args1) setType (if (reflectiveCalls) AnyRefClass.tpe else tree.symbol.info.resultType) case Super(qual, mix) => typedSuper(qual, mix) diff --git a/src/library/scala/runtime/DynamicDispatch.java b/src/library/scala/runtime/DynamicDispatch.java new file mode 100644 index 0000000000..2868298211 --- /dev/null +++ b/src/library/scala/runtime/DynamicDispatch.java @@ -0,0 +1,42 @@ +package scala.runtime; + +import java.dyn.CallSite; +import java.dyn.MethodHandle; + +/** + * This class resolves calls through refinement types. The + * bootstrap method is called when an invokedynamic is found + * by the Java VM. + * + * Note: Requires Java 7 with invoke dynamic support (see JSR 292) + * + * @author Iulian Dragos + * @see JSR292 + */ +public class DynamicDispatch { + + /** + * Resolve an invoke dynamic in Scala code. invokedynamic calls appear + * when a method defined by a refinement type is called. It is resolved + * by looking up a method with the same name and types in the receiver + * object. It is guaranteed by the type checker that such a method + * exists. + * + * The current implementation is not correct, a call site being + * always bootstrapped to a method handle. A bound call site should be + * guarded by a test on the receiver type. Such code should either + * be generated by the compiler, or by this bootstrap method using + * one of the code combinators provided in java.dyn.*. + * + * ATM, they are not yet available in the JVM. + */ + public static bootstrapInvokeDynamic(CallSite cs, Object... args) { + println(cs); + + MethodHandle mh = MethodHandles.findVirtual(cs.callerClass(), + cs.name(), + cs.type()); + cs.setTarget(mh); + return mh(args) + } +} -- cgit v1.2.3