diff options
Diffstat (limited to 'src/detach/plugin')
-rw-r--r-- | src/detach/plugin/scala/tools/detach/Detach.scala | 1193 | ||||
-rw-r--r-- | src/detach/plugin/scala/tools/detach/DetachPlugin.scala | 41 | ||||
-rw-r--r-- | src/detach/plugin/scalac-plugin.xml | 4 |
3 files changed, 1238 insertions, 0 deletions
diff --git a/src/detach/plugin/scala/tools/detach/Detach.scala b/src/detach/plugin/scala/tools/detach/Detach.scala new file mode 100644 index 0000000000..f0b0a2a18d --- /dev/null +++ b/src/detach/plugin/scala/tools/detach/Detach.scala @@ -0,0 +1,1193 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2010 LAMP/EPFL + * @author Stephane Micheloud + */ + +package scala.tools.detach + +import scala.collection.immutable +import scala.collection.mutable.{HashMap, HashSet, ListBuffer} + +import scala.tools.nsc._ +import scala.tools.nsc.plugins.PluginComponent +import scala.tools.nsc.symtab.Flags._ +import scala.tools.nsc.transform._ + +abstract class Detach extends PluginComponent + with Transform with TypingTransformers { + import global._ + import definitions._ + + /** the following two members override abstract members in Transform */ + val phaseName: String = "detach" + + protected def newTransformer(unit: CompilationUnit): Transformer = + new DetachTransformer(unit) + + // set with the `-P:detach:enable` plugin option (see DetachPlugin) */ + var isEnabled = false + + private class DetachTransformer(unit: CompilationUnit) + extends TypingTransformer(unit) { + private val DEBUG = settings.debug.value + private val PROXY_PREFIX = "proxy$" // local proxy objects + private val PROXY_SUFFIX = "$proxy" // top-level proxy classes + private val DETACH_SUFFIX = "$detach" // detached closures + private val IMPL_SUFFIX = "Impl" // follows Java convention + + private val nme_bind = newTermName("bind") + private val nme_unbind = newTermName("unbind") + private val nme_unreferenced = newTermName("unreferenced") + + private val Functions = FunctionClass.toList // see method isFuncType + + private val RemoteClass = + definitions.getClass("java.rmi.Remote") + + private val UIDClass = + definitions.getClass("java.rmi.server.UID") + + private val UnicastRemoteObjectClass = + definitions.getClass("java.rmi.server.UnicastRemoteObject") + + private val UnreferencedClass = + definitions.getClass("java.rmi.server.Unreferenced") + + private val DetachModule = + definitions.getModule("scala.remoting.detach") + + private val DebugModule = + definitions.getModule("scala.remoting.Debug") + + private val RemoteRefModule = + definitions.getModule("scala.runtime.RemoteRef") + + private val ThreadModule = + definitions.getModule("java.lang.Thread") + + private val UnicastRemoteObjectModule = + definitions.getModule("java.rmi.server.UnicastRemoteObject") + + private val remoteAnnotationInfo = { + val RemoteAttr: Symbol = definitions.getClass("scala.remote") + AnnotationInfo(RemoteAttr.tpe, List(), List()) + } + + private val serializableAnnotationInfo = + AnnotationInfo(SerializableAttr.tpe, List(), List()) +/* + private val throwsAnnotationInfo = { + val RemoteExceptionClass = definitions.getClass("java.rmi.RemoteException") + val ThrowsAttr = definitions.getClass("scala.throws") + AnnotationInfo( + ThrowsAttr.tpe, + List(Literal(Constant(RemoteExceptionClass.tpe))), + List() + ) + } +*/ + // todo: see generation of Java version UID + private def serialVersionUIDAnnotationInfo(clazz: Symbol) = { + def genHash(sym: Symbol): Long = { + val sym1 = if (sym.isConstructor) sym.owner else sym + val ts = sym.tpe match { + case MethodType(params, rt) => (params map (_.tpe)) ::: List(rt) + case t => List(t) + } + val hashes = sym1.nameString.hashCode :: + (ts map (_.typeSymbol.nameString.hashCode)) + (0L /: hashes)((acc, h) => acc ^ h) + } + val hashes = for (sym <- clazz.info.decls.toList) yield genHash(sym) + val uid: Long = (0L /: hashes) ((acc, h) => acc * 41 + h) + val serialVersionUIDAttr = definitions.getClass("scala.SerialVersionUID") + AnnotationInfo( + serialVersionUIDAttr.tpe, + List(Literal(Constant(uid))), + List() + ) + } + + private def elems(suffix: String): List[(Symbol, Symbol)] = + for (clazz <- ObjectRefClass :: refClass.valuesIterator.toList) yield { + val name = "scala.runtime.remoting.Remote" + clazz.name + suffix + (clazz, definitions.getClass(name)) + } + private val remoteRefClass = immutable.HashMap(elems(""): _*) + private val remoteRefImpl = immutable.HashMap(elems("Impl"): _*) + + private val proxyInterfaceDefs = new HashMap[Symbol/*owner*/, ListBuffer[Tree]] + private val detachedClosureApply = new HashMap[Tree, Apply] + + private type SymSet = HashSet[Symbol] + private val capturedObjects = new HashMap[Symbol/*clazz*/, SymSet] + private val capturedFuncs = new HashMap[Symbol/*clazz*/, SymSet] + private val capturedCallers = new HashMap[Symbol/*clazz*/, SymSet] + private val capturedThisClass = new HashMap[Symbol, Symbol] + + private val proxies = new HashMap[ + Symbol, //clazz + (Symbol, Symbol, HashMap[Symbol, Symbol]) //iface, impl, accessor map + ] + def toInterface(clazz: Symbol) = proxies(clazz)._1 + private val classdefs = new HashMap[Symbol/*clazz*/, ClassDef] + // detachedClosure gathers class definitions containing a "detach" apply + private val detachedClosure = new HashMap[Symbol/*clazz*/, ClassDef] + + /** <p> + * The method <code>freeObjTraverser.traverse</code> is invoked + * in the method <code>DetachPlugin.transformUnit</code> in order to + * gather information about objects referenced inside a detached + * closure and which will be accessed remotely through object proxies. + * </p> + * <p> + * Object proxies are generated in method <code>mkClosureApply</code> + * and their definitions are generated in method <code>genProxy</code>. + * </p> + */ + private val freeObjTraverser = new Traverser { + def symSet(f: HashMap[Symbol, SymSet], sym: Symbol): SymSet = f.get(sym) match { + case Some(ss) => ss + case None => val ss = new HashSet[Symbol]; f(sym) = ss; ss + } + def getClosureApply(tree: Tree): Apply = tree match { + case Block(_, expr) => getClosureApply(expr) + case Typed(expr, _) => getClosureApply(expr) + case apply @ Apply(Select(_, _), _) => apply // sel="<init>" or some "f$0" + case Apply(fun, _) => getClosureApply(fun) + case _ => + throw new Error("getClosureApply: unhandled case " + tree) + } + def isFuncType(tp: Type): Boolean = tp match { + case TypeRef(pre, sym, args) => + Functions contains sym.tpe.typeSymbol + case _ => + false + } + def isOuterMember(sym: Symbol): Boolean = + sym.isOuterAccessor || + sym.name.endsWith(nme.OUTER/*, nme.OUTER.length*/) + override def traverse(tree: Tree) { + val sym = tree.symbol + val owner = + if (currentOwner.isModule) currentOwner + else currentOwner.enclClass + tree match { + case cdef @ ClassDef(_, _, _, impl) => + classdefs(sym) = cdef + super.traverse(impl) + if (detachedClosure contains sym) { + detachedClosure(sym) = cdef + symSet(capturedObjects, sym) += capturedThisClass(sym) + } + + case Apply(Select(qual, _), List(arg)) + if (qual.tpe <:< DetachModule.tpe) => + assert(isFuncType(arg.tpe))//debug + val t = getClosureApply(arg) + if (!t.fun.symbol.isConstructor) + unit.error(t.pos, "detach inapplicable for " +t.fun.symbol) + val sym = t.fun.symbol.owner + capturedThisClass(sym) = owner + symSet(capturedFuncs, sym) + detachedClosureApply(tree) = t + classdefs get sym match { + case None => + detachedClosure(sym) = null // set later in case ClassDef + case Some(cdef) => + detachedClosure(sym) = cdef + symSet(capturedObjects, sym) += capturedThisClass(sym) + } + super.traverse(arg) + + case Select(qual @ This(_), name) + if qual.symbol.isModuleClass && !qual.symbol.isPackageClass => + val qsym = qual.symbol + symSet(capturedFuncs, owner) += sym + symSet(capturedObjects, owner) += qsym + + case Select(qual, name) + if (qual.hasSymbol && + (sym.owner != owner) && + !(sym.ownerChain contains ScalaPackageClass) && + !(sym.owner hasFlag JAVA)) => + val qsym = qual.symbol + symSet(capturedFuncs, owner) += sym + if (qsym.isStaticModule && !qsym.isPackage) { + //println("*****1******* capturedObjects("+owner+") += "+qsym) + symSet(capturedObjects, owner) += qsym + } + else if (!isOuterMember(qsym) && !(qsym isNestedIn owner)) { + //println("*****3******* capturedCallers("+sym+") += "+qsym) + symSet(capturedCallers, sym) += qsym + } + + case _ => + super.traverse(tree) + } + } + } //freeObjTraverser + + private val valueClass = immutable.HashMap( + (for ((sym, ref) <- refClass.toList) yield (ref, sym)): _* + ) + (ObjectRefClass -> ObjectClass) + + private def toValueClass(tp: Type): Type = + if (isRefClass(tp)) valueClass(tp.typeSymbol).tpe + else if (proxies contains tp.typeSymbol) toInterface(tp.typeSymbol).tpe + else tp + + private def isRefClass(tp: Type): Boolean = + (tp ne null) && + ((refClass.valuesIterator contains tp.typeSymbol) || (ObjectRefClass eq tp.typeSymbol)) + + private def isRemoteRefClass(tp: Type): Boolean = + (tp ne null) && (remoteRefClass.valuesIterator contains tp.typeSymbol) + + private def mkRemoteRefClass(tp: Type): Type = { + assert(isRefClass(tp)) + val tp1 = remoteRefClass(tp.typeSymbol) + typeRef(tp1.typeConstructor.prefix, tp1, Nil) // after erasure, no type anymore! + } + + class TreeOuterSubstituter(from: List[Symbol], to: List[Symbol]) extends Traverser { + if (DEBUG) + println("\nTreeOuterSubstituter:"+ + "\n\tfrom="+from.mkString(",")+ + "\n\tto="+to.mkString(",")) + val substMap = new HashMap[Symbol, Symbol] + override def traverse(tree: Tree) { + def subst(from: List[Symbol], to: List[Symbol]) { + if (!from.isEmpty) + if (tree.symbol.tpe == from.head.tpe) { + if (DEBUG) + println("\nTreeOuterSubstituter\n\tsym="+tree.symbol+ + ", tpe="+tree.symbol.tpe+ + "\n\towner="+tree.symbol.owner) + tree.symbol updateInfo to.head.tpe + } + else tree.symbol.tpe match { + case MethodType(params, restp) => + for (p <- params if p.tpe == from.head.tpe) { + p updateInfo to.head.tpe + } + if (restp == from.head.tpe) { + if (DEBUG) + println("\nTreeOuterSubstituter(2)\n\tsym="+tree.symbol+ + ", tpe="+tree.symbol.tpe+ + ", owner="+tree.symbol.owner) + tree.symbol updateInfo MethodType(params, to.head.tpe) + } + case _ => + subst(from.tail, to.tail) + } + } + def isOuter(sym: Symbol): Boolean = + sym.isOuterAccessor || + sym.name.endsWith(nme.OUTER/*, nme.OUTER.length*/) + if (tree.hasSymbol && isOuter(tree.symbol)) subst(from, to) + super.traverse(tree) + } + } + + // based on class Trees.TreeTypeSubstituter + private class TreeTypeRefSubstituter(clazz: Symbol) extends Traverser { + override def traverse(tree: Tree) { + val sym = tree.symbol + if (tree.hasSymbol && isRefClass(sym.tpe) && + (sym.owner.enclClass == clazz) && + (sym.isValueParameter || sym.hasFlag(PARAMACCESSOR))) { + sym setInfo mkRemoteRefClass(sym.tpe) + tree.tpe = sym.tpe + } + if (isRefClass(tree.tpe)) + tree.tpe = mkRemoteRefClass(tree.tpe) + super.traverse(tree) + } + override def apply[T <: Tree](tree: T): T = super.apply(tree) + } + + private class TreeOwnerSubstituter(from: Symbol, to: Symbol) extends Traverser { + def substType(sym: Symbol): Type = { + def subst(tpe: Type): Type = tpe match { + case MethodType(params, restp) => + println("TreeOwnerSubstituter[1]: tpe="+tpe+ + ", tpe.typeSymbol="+tpe.typeSymbol+", sym="+sym)//debug + for (p <- params if p.tpe == from.tpe) { + println("TreeOwnerSubstituter[2]: sym="+sym+ + ", sym.owner="+sym.owner+", p.tpe="+p.tpe)//debug + p updateInfo to.tpe + } + MethodType(params, subst(restp)) + case _ => + if (sym.owner == from && tpe == from.tpe) { + println("TreeOwnerSubstituter[3]: sym="+sym+ + ", owner="+sym.owner+", tpe="+tpe)//debug + to.tpe + } else tpe + } + subst(sym.tpe) + } + val map = new HashMap[Symbol, Symbol] + override def traverse(tree: Tree) { + if (tree.hasSymbol && tree.symbol != NoSymbol) { + val sym = tree.symbol + if (sym.owner == from) { + val sym1 = map get sym match { + case Some(s) => s + case None => val s = sym.cloneSymbol(to); map(sym) = s; s + } + tree setSymbol sym1 + } + val sym1 = tree.symbol + val tp = substType(sym1) + if (tp != sym1.tpe) { + if (sym1.owner == to) + println("\n%%%%%1%%%%%%% TreeOwnerSubst: tree="+tree+", sym1="+sym1+", sym1.owner="+sym1.owner)//debug + sym1 setInfo tp + tree setSymbol sym1 + } + } + super.traverse(tree) + } + //override def apply[T <: Tree](tree: T): T = super.apply(tree/*.duplicate*/) + } + + private var inConstructorFlag = 0L + + private def isCaptured(clazz: Symbol, sym: Symbol): Boolean = + if (capturedFuncs contains clazz) { + //log("**1** isCaptured: clazz="+clazz+", sym="+sym+", ") + capturedFuncs(clazz) contains sym + } + else { + //log("**2** isCaptured: clazz="+clazz+", sym="+sym) + sym.isMethod && !sym.isConstructor + } + + private class TreeAccessorSubstituter(clazz: Symbol, objs: List[Symbol], proxySyms: List[Symbol]) + extends Transformer { + def removeAccessors(tree: Tree): Tree = tree match { + case Apply(fun, _) => + removeAccessors(fun) + case Select(qual, _) if tree.hasSymbol && tree.symbol.isOuterAccessor => + removeAccessors(qual) + case _ => + tree + } + if (DEBUG) + println("\nTreeAccessorSubstituter: "+ + "\n\tobjs="+objs.mkString(",")+ + "\n\tproxies="+proxySyms.mkString(",")) + override def transform(tree: Tree): Tree = tree match { + // transforms field assignment $outer.i$1.elem=.. + // into setter $outer.i$1_=(..) + case Assign(lhs @ Select(qual1 @ Select(qual, name), name1), rhs) + if qual1.hasSymbol && !qual1.symbol.isPrivateLocal && + isRemoteRefClass(qual1.tpe) => + if (DEBUG) + println("\nTreeAccessorSubstituter: Assign1\n\tqual1="+qual1+", sel.tpe="+lhs.tpe+ + "\n\tqual1.tpe="+qual1.tpe+", name1="+name1+ + "\n\tqual.tpe="+qual.tpe+", tree.tpe="+tree.tpe)//debug + val iface = toInterface(qual.tpe.typeSymbol) + val sym = iface.tpe.decls lookup nme.getterToSetter(name) + atPos(tree.pos)(Apply( + Select(super.transform(qual), sym) setType lhs.tpe, + List(super.transform(rhs)) + ) setType tree.tpe) + + // transforms local assignment this.x$1.elem=.. + // into setter method this.x$1_=(..) + case Assign(lhs @ Select(qual, name), rhs) + if qual.hasSymbol && qual.symbol.isPrivateLocal && + isRemoteRefClass(qual.tpe) => + if (DEBUG) + println("\nTreeAccessorSubstituter: Assign2"+ + "\n\tqual="+qual+", qual.tpe="+qual.tpe+ + "\n\tname="+name) + // substitute the 'elem' member of the reference class with + // the corresponding setter method of the remote reference class. + val qual1 = super.transform(qual) + val sym = qual1.tpe.decls lookup nme.getterToSetter(name) + val fun = gen.mkAttributedSelect(qual1, sym) + Apply(fun, List(super.transform(rhs))) setType lhs.tpe + + case Assign(Select(qual, name), rhs) + if qual.hasSymbol && (objs contains qual.symbol) => + val sym = qual.symbol + val proxy = proxySyms(objs indexOf sym) + if (DEBUG) + println("\nTreeAccessorSubstituter: Assign3"+ + "\n\tqual="+qual+", qual.tpe="+qual.tpe+ + "\n\tproxy="+proxy+", proxy.tpe="+proxy.tpe+ + "\n\tname="+name)//debug + // substitute the member accessor of the enclosing class with + // the corresponding setter method of the detached interface. + val iface = toInterface(sym) + val substSymbols = new TreeSymSubstituter( + sym.info.decls.toList filter { isCaptured(sym, _) }, + iface.info.decls.toList) + substSymbols(Apply( + Select(Ident(proxy), nme.getterToSetter(name)), + List(super.transform(rhs)))) + + // transforms setter invocation this.i$1_=(..) + // into setter invocation $outer.i$1_=(..) + case Apply(Select(qual @ This(_), name), args) + if (objs contains qual.symbol) && nme.isSetterName(name) => + val proxy = proxySyms(objs indexOf qual.symbol) + if (DEBUG) + println("\nTreeAccessorSubstituter: Apply"+ + "\n\tqual="+qual+", qual.tpe="+qual.tpe+ + "\n\tproxy="+proxy+", proxy.tpe="+proxy.tpe+ + "\n\tname="+name+", decoded="+name.decode) + val qual1 = gen.mkAttributedSelect(gen.mkAttributedThis(proxy.owner), proxy) + val sym1 = proxy.info.decls lookup name.decode + val fun = gen.mkAttributedSelect(qual1, sym1) + Apply(fun, args map (super.transform(_))) setType tree.tpe + + // transforms access to field this.name$1 + // into invocation of getter method $outer.name$1() + case Select(qual @ This(_), name) + if objs contains qual.symbol => + val proxy = proxySyms(objs indexOf qual.symbol) + if (DEBUG) + println("\nTreeAccessorSubstituter: Select"+ + "\n\tqual="+qual+", qual.tpe="+qual.tpe+ + "\n\tproxy="+proxy+", proxy.tpe="+proxy.tpe+ + "\n\tname="+name+", decoded="+name.decode) + val qual1 = gen.mkAttributedSelect(gen.mkAttributedThis(proxy.owner), proxy) + val sym1 = proxy.info.decls lookup nme.originalName(name) //name + gen.mkAttributedSelect(qual1, sym1) + + // transforms field $outer.name$1 into getter method $outer.name$1() + case Select(qual @ Select(_, name1), name) + if qual.hasSymbol && name1.endsWith(nme.OUTER/*, nme.OUTER.length*/) && + !tree.symbol.isMethod => + if (DEBUG) + println("\nTreeAccessorSubstituter: Select0\n\tqual="+qual+ + ", qual.tpe="+qual.tpe+", name="+name)//debug + val sym = qual.symbol + val qual1 = gen.mkAttributedSelect(gen.mkAttributedThis(sym.owner), sym) + val iface = toInterface(qual.tpe.typeSymbol) + val sym1 = iface.tpe.decls lookup name + val fun = gen.mkAttributedSelect(qual1, sym1) + Apply(fun, List()) setType tree.tpe + + case Select(apply @ Apply(fun @ Select(qual, _), _), name) + if fun.symbol.isOuterAccessor => + val tsym = fun.symbol.tpe.resultType.typeSymbol + val funcs = capturedFuncs(clazz).toList filter (sym => + (tsym.ownerChain contains sym.owner) || (tsym isSubClass sym.owner)) + if (DEBUG) + println("\nTreeAccessorSubstituter: Select1\n\tfun="+fun+ + ",\n\tfun.tpe="+fun.tpe+", name="+name+ + ",\n\tfuncs="+funcs)//debug + funcs find (tree.symbol.==) match { + case Some(sym) => + val qual1 = + if (currentOwner.enclClass isNestedIn clazz) apply + else removeAccessors(qual) + val name1 = + (if (tsym isSubClass qual1.tpe.typeSymbol) "" + else tsym.fullName('$')+"$")+sym.name + val iface = toInterface(qual1.tpe.typeSymbol) + val sym1 = iface.tpe.decls lookup name1 + gen.mkAttributedSelect(qual1, sym1) + case None => + super.transform(tree) + } + + // transforms field access $outer.i$1.elem + // into invocation of getter method $outer.i$1() + case Select(qual @ Select(qual1, name1), name) + if qual.hasSymbol && !qual.symbol.isPrivateLocal && + isRemoteRefClass(qual.tpe) => + if (DEBUG) + println("\nTreeAccessorSubstituter: Select2\n\tqual="+qual+ + "\n\tqual.tpe="+qual.tpe+", tree.tpe="+tree.tpe)//debug + val iface = toInterface(qual.symbol.owner) + val sym1 = iface.tpe.decls lookup name1 + val fun = gen.mkAttributedSelect(qual1, sym1) + Apply(fun, List()) setType tree.tpe + + // transforms local access this.i$1.elem + // into invocation of getter method this.i$1() + case Select(qual, name) + if qual.hasSymbol && qual.symbol.isPrivateLocal && + isRemoteRefClass(qual.tpe) => + if (DEBUG) + println("\nTreeAccessorSubstituter: Select3\n\tqual="+qual+ + "\n\tqual.tpe="+qual.tpe)//debug + val sym = qual.tpe.decls lookup name + val fun = gen.mkAttributedSelect(qual, sym) + Apply(fun, List()) setType tree.tpe + + case Select(qual, name) + if qual.hasSymbol && (objs contains qual.symbol) => + if (DEBUG) + println("\nTreeAccessorSubstituter: Select4\n\tqual="+qual+ + ", qual.tpe="+qual.tpe+", name="+name)//debug + val sym = qual.symbol + val proxy = proxySyms(objs indexOf sym) + // substitute the accessor of a member of the enclosing class + // with the corresponding accessor of the detached interface + val qual1 = gen.mkAttributedSelect(gen.mkAttributedThis(proxy.owner), proxy) + val iface = toInterface(sym) + val sym1 = iface.tpe.decls lookup name.decode + gen.mkAttributedSelect(qual1, sym1) + + case _ => + super.transform(tree) + } + def apply[T <: Tree](tree: T): T = transform(tree).asInstanceOf[T] + } // TreeAccessorSubstituter +/* + private class TreeNameSubstituter(from: Name, to: Symbol) extends Transformer { + override def transform(tree: Tree): Tree = tree match { + case Super(qual, mix) if tree.symbol.name == from => + Super(qual, mix) setSymbol to + case This(name) if name == from => + This(to.name) setSymbol to + case _ => + super.transform(tree) + } + def apply[T <: Tree](tree: T): T = transform(tree).asInstanceOf[T] + } +*/ + /** <p> + * Given the closure definition (generated by previous phases) + * </p><pre> + * class $anonfun$1 extends Object with Function1 { + * def this($outer: C, x$1: Int): $anonfun$1 = .. + * def apply(x: Int): Int = x + this.$outer.x() + this.x$1 + * }</pre> + * <p> + * the method <code>mkClosureDef</code> transforms the above code + * to the following: + * </p><pre> + * @serializable + * class $anonfun$1$detach extends Object with Function1 { + * def this($outer: C$proxy, x$1: Int): $anonfun$1$detach = .. + * def apply(x: Int): Int = x + this.$outer.x() + this.x$1 + * }</pre> + * <p> + * In particular, it performs the following operations: + * 1) add constructor parameter <code>proxy_n</code> to access + * proxy of the enclosing class + * 2) change reference types in constructor arguments to type + * <code<Remote_type_Ref</code>' + * 3) change occurences of <code>this</code> identifier to + * <code>proxy_n</code> in template code + * 4) change reference types of local value definitions associated + * to updated constructor arguments to type <code>Remote_type_Ref</code> + * </p> + * + * @param clazz the symbol of the original closure definition + * @return the typed class definition for the detached closure. + */ + private def mkClosureDef(clazz: Symbol): Tree = { + val cdef = detachedClosure(clazz) + val name = cdef.symbol.name + if (name endsWith DETACH_SUFFIX) + return cdef // closure already detached + + clazz.name = encode(clazz.name.decode + DETACH_SUFFIX) + clazz addAnnotation serialVersionUIDAnnotationInfo(clazz) + clazz addAnnotation serializableAnnotationInfo + + val thiz = capturedThisClass(clazz) + val (List(outer), captured) = + capturedObjects(clazz).toList partition (thiz.==) + + /** <p> + * Method <code>updateConstructorParams</code> updates the class + * symbol of the detached closure as follows: + * 1) it appends the "$detach" suffix to the class name, + * 2) it adds the "@serializable" annotation to class attributes, + * 3) it adds a parameter symbol for each element of "captured". + * </p> + * <p> + * and also updates the signature of the constructor symbol: + * 1) it adds a parameter type for each element of "captured", + * 2) it changes reference types to remote reference types. + * </p> + */ + def updateConstructorParams(vparams: List[ValDef]): List[Symbol] = { + val hasOuter = !vparams.isEmpty && (vparams.head.symbol.tpe == thiz.tpe) + val ctor = clazz.primaryConstructor + val params = (for (sym <- captured) yield { + val iface = toInterface(sym) + val param = ctor.newValueParameter(ctor.pos, freshProxyName) + .setFlag(SYNTHETIC) + .setInfo(iface.tpe) + param.owner = ctor + param + }) ::: ( + if (hasOuter) Nil + else { + val iface = toInterface(thiz) + val param = ctor.newValueParameter(ctor.pos, nme.OUTER) + .setFlag(SYNTHETIC) + .setInfo(iface.tpe) + param.owner = ctor + List(param) + } + ) + val tp = ctor.tpe match { + case mt @ MethodType(params1, restp) => + val params2 = if (hasOuter) { + val iface = toInterface(params1.head.tpe.typeSymbol) + ctor.newSyntheticValueParam(iface.tpe) :: params1.tail + } + else params1 + for (p <- params2 if isRefClass(p.tpe)) { + p updateInfo mkRemoteRefClass(p.tpe) + } + MethodType(params ::: params2, restp) + case tp => + tp + } + ctor updateInfo tp + params + } //updateConstructorParams + + /** + */ + def updateConstructorDef(ctor: DefDef): (List[Tree], List[Symbol]) = { + val DefDef(mods, name, tparams, List(vparams), tpt, rhs) = ctor + val newparams = updateConstructorParams(vparams) + val vparams0 = newparams map (sym => ValDef(sym) setType sym.tpe) + val ctorDef = treeCopy.DefDef(ctor, mods, name, tparams, List(vparams0 ::: vparams), tpt, rhs) + val accessors = for (sym <- newparams) yield { + val acc = clazz.newValue(sym.pos, sym.name) + .setFlag(SYNTHETIC | PARAMACCESSOR | PRIVATE | LOCAL) + .setInfo(sym.tpe) + clazz.info.decls enter acc + acc + } + val accDefs = accessors map (sym => ValDef(sym) setType sym.tpe) + (ctorDef :: accDefs, accessors) + } //updateConstructorDef + + val impl = cdef.impl + val (List(ctor: DefDef), body1) = impl.body partition (t => + t.isDef && t.symbol.isPrimaryConstructor) + val (defs, accessors) = updateConstructorDef(ctor) + val impl1 = treeCopy.Template(impl, impl.parents, impl.self, defs ::: body1) + val (from, to) = /*List.unzip*/( + for (obj <- captured ::: List(outer)) + yield (obj, toInterface(obj)) + ) unzip + //val substNames = new TreeNameSubstituter(name, clazz) + val substTypeRefs = new TreeTypeRefSubstituter(clazz) + val substAccs = new TreeAccessorSubstituter(clazz, from, accessors) + val substTypes = new TreeOuterSubstituter(from, to) + val substSyms = new TreeSymSubstituter(from, to) + val t1 = ClassDef(clazz, substSyms(substTypes(substAccs(substTypeRefs(impl1))))) + //println("mkClosureDef: t(untyped)=\n"+nodeToString(t1)) + val t = localTyper typed t1 + detachedClosure(clazz) = t.asInstanceOf[ClassDef] + //println("mkClosureDef: t(typed)=\n"+nodeToString(t)) + t + } //mkClosureDef + + /** <p> + * Given a class <code>C</code> with member <code>x</code> + * which is (remotely) referenced from inside a detached closure: + * </p><pre> + * class C extends .. { + * var x: Int + * }</pre> + * <p> + * the method <code>addProxy</code> generates the following two + * proxy definitions (used later in method <code>mkClosureApply</code> + * to generate object proxies): + * </p><pre> + * trait C$proxy extends java.rmi.Remote { + * def x(): Int + * def x_=(x$1: Int): Unit + * } + * class C$proxyImpl + * extends java.rmi.server.UnicastRemoteObject + * with C$proxy with java.rmi.server.Unreferenced { + * def this(x$0: String, x$1: C): C$ProxyImpl = .. + * def x(): Int = this.x$1.x() + * def x_=(x$1: Int): Unit = this.x$1.x_=(x$1) + * def unreferenced(): Unit = RemoteRef.unbind(this.x$0) + * }</pre> + */ + private def addProxy(closure: Symbol, clazz: Symbol) { + // the Sun RMI compiler crashes with the error message + // "error: An error has occurred in the compiler; ..." with trace + // "sun.tools.java.CompilerError: getInnerClassField" if the + // generated proxy class does not belong to the top-level scope. + val proxyOwner = clazz.toplevelClass.owner //clazz.owner + + if (DEBUG) + println("\nadd proxy for "+clazz+" in "+proxyOwner)//debug + + val (proxyIntf, proxyImpl, proxyMap) = proxies get clazz match { + case Some(proxy) => + proxy + case None => + val iface = + proxyOwner.newClass(clazz.pos, encode(clazz.name.decode + PROXY_SUFFIX)) + iface.sourceFile = clazz.sourceFile + iface setFlag (ABSTRACT | TRAIT | INTERFACE) // Java interface + val iparents = List(ObjectClass.tpe, RemoteClass.tpe, ScalaObjectClass.tpe) + iface setInfo ClassInfoType(iparents, new Scope, iface) + // methods must throw RemoteException + iface addAnnotation remoteAnnotationInfo + + val iclaz = + proxyOwner.newClass(clazz.pos, encode(iface.name.decode + IMPL_SUFFIX)) + iclaz.sourceFile = clazz.sourceFile + iclaz setFlag (SYNTHETIC | FINAL) + // Variant 1: rebind/unbind + val cparents = List(UnicastRemoteObjectClass.tpe, iface.tpe, + UnreferencedClass.tpe, ScalaObjectClass.tpe) + // Variant 2: un-/exportObject + //val cparents = List(ObjectClass.tpe, iface.tpe, + // UnreferencedClass.tpe, ScalaObjectClass.tpe) + iclaz setInfo ClassInfoType(cparents, new Scope, iclaz) + val proxy = (iface, iclaz, new HashMap[Symbol, Symbol]) + proxies(clazz) = proxy + proxy + } + + def addAccessors { + def mkGetter(sym: Symbol, name: String): Symbol = { + val getter = if (sym.isMethod) { + val meth = sym.cloneSymbol(proxyIntf) + meth.name = name + val tsym = meth.tpe.resultType.typeSymbol + if (proxies contains tsym) + meth updateInfo MethodType(List(), toInterface(tsym).tpe) + meth + } + else { + val meth = proxyIntf.newMethod(sym.pos, nme.getterName(sym.originalName)) + meth setFlag ACCESSOR + meth setInfo MethodType(List(), toValueClass(sym.tpe)) + meth + } + getter setFlag ABSTRACT + getter resetFlag FINAL + getter + } + def mkSetter(sym: Symbol): Symbol = { + val setter = proxyIntf.newMethod(sym.pos, nme.getterToSetter(sym.originalName)) + setter setFlag (sym.flags & ~(PRIVATE | LOCAL) | ACCESSOR | lateDEFERRED) + val param = setter.newSyntheticValueParam(toValueClass(sym.tpe)) + setter setInfo MethodType(List(param), UnitClass.tpe) + setter setFlag ABSTRACT + setter resetFlag FINAL + setter + } + def create(owner: Symbol, clazz: Symbol) { + val funcs = capturedFuncs(owner).toList + funcs find (_.isConstructor) match { + case Some(sym) if capturedFuncs contains sym.owner => + create(sym.owner, clazz) + case _ => + } + val newfuncs = funcs filterNot (proxyMap.valuesIterator.toList contains) + val (members, others) = newfuncs partition (clazz isSubClass _.owner) + val outers = others filter (sym => + (clazz isNestedIn sym.owner) && clazz.isClass) + for (sym <- outers) { + val sym1 = mkGetter(sym, sym.fullName('$')) + proxyIntf.info.decls enter sym1 + proxyMap(sym1) = sym + }/* + for (sym <- outers if capturedCallers contains sym; + caller <- capturedCallers(sym)) { + val sym1 = mkGetter(sym, caller.nameString+'$'+sym.nameString) + if (clazz.isAnonymousClass) + println("[2] clazz="+clazz+", sym1="+sym1) + proxyIntf.info.decls enter sym1 + proxyMap(sym1) = sym + }*/ + for (sym <- members if !sym.isConstructor) { + val sym1 = mkGetter(sym, sym.originalName.decode) + proxyIntf.info.decls enter sym1 + proxyMap(sym1) = sym + } + for (sym <- members if isRefClass(sym.tpe)) { + val sym1 = mkSetter(sym) + proxyIntf.info.decls enter sym1 + proxyMap(sym1) = sym + } + } + create(closure, clazz) + } + + addAccessors + if (DEBUG) { + val xs = proxyMap.keysIterator.toList + println("\tadded "+proxyIntf+ + "\n\twith "+xs.mkString(", ")+" ["+xs.length+"]") + } + } //addProxy + + def genProxy(clazz: Symbol) { + val (proxyIntf, proxyImpl, proxyMap) = proxies(clazz) + + // generate proxy interface + val ifaceBody = proxyMap.keysIterator.toList map { DefDef(_, EmptyTree) } + val ifaceParents = + proxyIntf.info.parents map (t => TypeTree(t) setPos proxyIntf.pos) + val ifaceTmpl = Template(ifaceParents, emptyValDef, ifaceBody) + val ifaceDef = localTyper typed ClassDef(proxyIntf, ifaceTmpl) + + // generated proxy implementation + // Variant 1: rebind/unbind + val param1 = + proxyImpl.newValueParameter(proxyImpl.pos, freshName("x$")) + .setFlag(SYNTHETIC | PARAMACCESSOR | PRIVATE | LOCAL) + .setInfo(StringClass.tpe) + proxyImpl.info.decls enter param1 + + val param2 = + proxyImpl.newValueParameter(proxyImpl.pos, freshName("x$")) + .setFlag(SYNTHETIC | PARAMACCESSOR | PRIVATE | LOCAL) + .setInfo(clazz.tpe) + proxyImpl.info.decls enter param2 + + val unreferenced = + proxyImpl.newMethod(proxyImpl.pos, nme_unreferenced) + .setInfo(MethodType(List(), UnitClass.tpe)) + proxyImpl.info.decls enter unreferenced + + val proxyBody = + DefDef(unreferenced, List(List()), Block( + List(Apply( //stats + Select(gen.mkAttributedRef(DebugModule), "info"), + List(Apply( + Select(Literal(Constant("unreferenced: ")), "$plus"), + // Variant 1: rebind/unbind + List(Select(This(proxyImpl), param1.name)) + // Variant 2: un-/exportObject + //List(This(proxyImpl)) + )) + )), + Apply( //expr + Select(gen.mkAttributedRef(RemoteRefModule), nme_unbind), + // Variant 1: rebind/unbind + List(Select(This(proxyImpl), param1.name)) + // Variant 2: un-/exportObject + //List(This(proxyImpl)) + ) + )) :: ( + for (sym <- proxyIntf.info.decls.toList) yield { + val sym1 = sym.cloneSymbol(proxyImpl) + sym1 resetFlag (ABSTRACT | DEFERRED | lateDEFERRED) + proxyImpl.info.decls enter sym1 + DefDef(sym1, { + val sym2 = proxyMap(sym) + var t = Select(This(proxyImpl), param2) + var outerAcc = + if (sym2.owner isSubClass param2) None + else param2.info.decls.toList find (_.isOuterAccessor) + while (!outerAcc.isEmpty) { + t = Select(t, outerAcc.get) + val outerClass = outerAcc.get.tpe.resultType.typeSymbol + outerAcc = + if (sym2.owner == outerClass) None + else outerClass.info.decls.toList find (_.isOuterAccessor) + } + val sel = Select(t, sym2) + if (sym2.isMethod) { + Apply(sel, sym1.paramss(0) map { Ident(_) }) + } + else if (isRefClass(sym2.tpe)) { + val sel1 = Select(sel, nme.elem) + if (sym1.tpe.paramTypes.length == 0) sel1 + else Assign(sel1, Ident(sym1.paramss(0)(0))) + } + else + sel + }) + }) + val proxyParents = + proxyImpl.info.parents map (t => TypeTree(t) setPos proxyImpl.pos) + val proxyTmpl = Template(proxyParents, + emptyValDef, NoMods, + // Variant 1: rebind/unbind + /*vparamss*/ List(List(ValDef(param1), ValDef(param2))), + // Variant 2: un-/exportObject + ///*vparamss*/ List(List(ValDef(param2))), + /*argss*/ List(List()), proxyBody, NoPosition) + val proxyDef = localTyper typed ClassDef(proxyImpl, proxyTmpl) + + // remember definitions to be added by transformStats + val proxyOwner = proxyIntf.owner + if (! (proxyInterfaceDefs contains proxyOwner)) + proxyInterfaceDefs(proxyOwner) = new ListBuffer + proxyInterfaceDefs(proxyOwner) += ifaceDef + proxyInterfaceDefs(proxyOwner) += proxyDef + } //genProxy + + private def freshName(s: String): Name = + unit.fresh.newName(s) + + private def freshProxyName: Name = + unit.fresh.newName(PROXY_PREFIX) + + /** <p> + * Given a detached closure applied in some environment consisting + * of an enclosing class <code>C</code> and some local variables + * <code>x$1</code> (immutable) and <code>y$1</code> (mutable): + * </p><pre> + * scala.remoting.detach.apply({ + * (new $anonfun$1(C.this, x$1, y$1): Function1) + * })</pre> + * <p> + * the above code is transformed to the following block: + * </p><pre> + * { + * val proxy$1: C$Proxy = + * RemoteRef.bind("C/proxy$1", new C$ProxyImpl(C.this)) + * val proxy$2: RemoteIntRef = + * RemoteRef.bind("C/proxy$2", new RemoteIntRefImpl(y$1)) + * (new $anonfun$1detach(proxy$1, x$1, proxy$2): Function1) + * } + * </pre> + */ + private def mkClosureApply(tree: Tree): Tree = { + val apply @ Apply(fun, args) = detachedClosureApply(tree) + assert(fun.symbol.isConstructor, fun.symbol+" is not a constructor")//debug + val clazz = apply.tpe.typeSymbol + val thiz = capturedThisClass(clazz) + val cdef = mkClosureDef(clazz) + val uid = localTyper typed { + val sym = currentOwner.newValue(tree.pos, freshName("uid$")) + .setFlag(SYNTHETIC) + .setInfo(StringClass.tpe) + val rhs = Apply(Select( + Apply( + Select(New(TypeTree(UIDClass.tpe)), nme.CONSTRUCTOR), + List() + ), + "toString" + ), List()) + ValDef(sym, rhs) + } + def cast(tree: Tree, tpe: Type): Tree = + Apply( + TypeApply( + Select(tree, Object_asInstanceOf), + List(TypeTree(tpe)) + ), + List() + ) + + def mkProxy(csym: Symbol): ValDef = { + val (iface, proxy, _) = proxies(csym) + val sym = currentOwner.newValue(csym.pos, freshProxyName) + .setFlag(SYNTHETIC) + .setInfo(iface.tpe) + val bind = Select(gen.mkAttributedRef(RemoteRefModule), nme_bind) + val name = Apply( + Select(Literal(Constant(sym.fullName('/')+"$")), String_+), + List(Ident(uid.symbol)) + ) + val thiz = + if (csym.isModule) gen.mkAttributedIdent(csym) + else gen.mkAttributedThis(csym) + val args = List(name, + Apply(Select(New(TypeTree(proxy.tpe)), nme.CONSTRUCTOR), + // Variant 1: rebind/unbind + List(name, thiz))) + // Variant 2: un-/exportObject + //List(thiz))) + val rhs = cast(Apply(bind, args), iface.tpe) + ValDef(sym, rhs) + } + + def mkObjProxies: List[ValDef] = { + val (outer, captured) = + capturedObjects(clazz).toList partition (thiz.==) + (captured ::: outer) map mkProxy + } + + def mkArgProxies: Map[Symbol, ValDef] = { + def retRefs(t: Tree): List[Tree] = t match { + case Apply(fun, args) => + args flatMap retRefs + case id @ Ident(_) => + if (isRefClass(id.tpe)) List(id) else Nil + case Template(_, _, body) => + body flatMap retRefs + case New(tpt) => + retRefs(tpt) + case thiz @ This(_) => + if (isRefClass(thiz.tpe)) List(thiz) else Nil + case _ => + throw new Error("Internal error: " + t.getClass) + } + new immutable.HashMap[Symbol, ValDef] ++ ( + for (variable <- retRefs(apply)) yield { + val param = variable.symbol + assert(isRefClass(param.tpe), param) + val proxy = currentOwner.newValue(param.pos, freshProxyName) + .setFlag(SYNTHETIC) + .setInfo(mkRemoteRefClass(param.tpe)) + val bind = Select(gen.mkAttributedRef(RemoteRefModule), nme_bind) + //val name = Literal(Constant(proxy.fullName('/'))) + val name = Apply( + Select(Literal(Constant(proxy.fullName('/')+"$")), String_+), + List(Ident(uid.symbol)) + ) + val ts = param.tpe.typeSymbol + val args = List(name, + Apply( + Select(New(TypeTree(remoteRefImpl(ts).tpe)), nme.CONSTRUCTOR), + // Variant 1: rebind/unbind + List(name, variable))) + // Variant 2: un-/exportObject + //List(variable))) + val rhs = cast(Apply(bind, args), remoteRefClass(ts).tpe) + (param, ValDef(proxy, rhs)) + } + ) + } //mkArgProxies + + /** <p> + * Method <code>mkClosureInstance</code> updates the list of actual + * parameters passed to the closure instance. + * </p> + */ + def mkClosureInstance(objProxies: List[ValDef], + argProxies: Map[Symbol, ValDef]): Tree = { + fun.tpe = fun.symbol.tpe + val args0 = objProxies map (tree => Ident(tree.symbol)) + val hasOuter = !args.isEmpty && (args.head.symbol.tpe == thiz.tpe) + val args1 = (if (hasOuter) args.tail else args) map (arg => + argProxies get arg.symbol match { + case Some(t) => Ident(t.symbol) + case None => arg + } + ) + if (DEBUG) + println("\nmkClosureInstance:\n\targs0="+args0+"\n\targs1="+args1) + val t = Typed( + Apply(fun, args0 ::: args1), + //TypeTree(clazz.info.parents.tail.head) //interface (2.7.x) + TypeTree(clazz.info.parents.head) //interface (2.8.x) + ) + localTyper typed t + } //mkClosureInstance + + val objProxies = mkObjProxies + val argProxies = mkArgProxies + val stats = uid :: objProxies ::: argProxies.valuesIterator.toList + val expr = mkClosureInstance(objProxies, argProxies) + localTyper typed Block(stats, expr) + } //mkClosureApply + + override def transform(tree: Tree): Tree = { + def withInConstructorFlag(inConstructorFlag: Long)(f: => Tree): Tree = { + val savedInConstructorFlag = this.inConstructorFlag + this.inConstructorFlag = inConstructorFlag + val t = f + this.inConstructorFlag = savedInConstructorFlag + t + } + if (!isEnabled) return tree + tree match { + case ClassDef(mods, name, tparams, impl) => + val tree1 = super.transform(tree) + if (!reporter.hasErrors && (capturedThisClass contains tree1.symbol)) + mkClosureDef(tree1.symbol) + else + tree1 + + case Apply(Select(_, _), _) => + val tree1 = super.transform(tree) + if (!reporter.hasErrors && (detachedClosureApply contains tree1)) + atPos(tree1.pos)(mkClosureApply(tree1)) + else + tree1 + + case Template(_, _, _) => + withInConstructorFlag(0) { super.transform(tree) } + + case _ => + super.transform(tree) + } + } + + /** Transform statements and add detached definitions to them. */ + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { + val stats1 = super.transformStats(stats, exprOwner) + val newDefs = { + val buf = new ListBuffer[Tree] + if (proxyInterfaceDefs contains currentOwner) + buf ++= proxyInterfaceDefs(currentOwner).toList + buf.toList + } + if (newDefs.isEmpty) stats1 else stats1 ::: newDefs + } + + private def genProxies { + def printDebugInfo { + println("\ncompilation unit : "+unit) + for ((sym, _) <- detachedClosure) { + println("closure to detach: "+sym+" (owner: "+sym.owner+")") + println("captured this : "+capturedThisClass(sym)) + val objs = capturedObjects get sym match { + case Some(ss) => ss.toList + case None => Nil + } + println("captured objects : "+objs.mkString(", ")+" ["+objs.length+"]") + } + println("\ncalled functions :") + for (sym <- capturedFuncs.keysIterator) { + val xs = capturedFuncs(sym).toList map (s => { + val callers = capturedCallers get s match { + case Some(ss) => "|"+ss.toList.mkString(",") + case None => "" + } + s+"("+s.owner.name+callers+")" + }) + println("\t"+sym+" -> "+xs.mkString(", ")+" ["+xs.length+"]") + } + } + def printDebugInfo2 { + println("\nproxy classes :") + for (sym <- proxies.keysIterator) + println("\t"+sym+"("+sym.tpe+") -> "+proxies(sym)) + } + if (DEBUG) + printDebugInfo + for ((closure, _) <- detachedClosure; + captured <- capturedObjects(closure)) + addProxy(closure, captured) + if (DEBUG) + printDebugInfo2 + for (sym <- proxies.keysIterator) + genProxy(sym) + } //genProxies + + /** <p> + * Method <code>transformUnit</code> performs three successive operations: + * </p> + * <ol> + * <li>it first gathers infos about free objects and detached + * closures;</li> + * <li>it then adds proxies for free objects;</li> + * <li>finally, if transforms detached closures (both definition and + * instantation).</li> + * </ol> + */ + override def transformUnit(unit: CompilationUnit) { + freeObjTraverser.traverse(unit.body) + if (!reporter.hasErrors) genProxies + super.transformUnit(unit) + } + } + +} + diff --git a/src/detach/plugin/scala/tools/detach/DetachPlugin.scala b/src/detach/plugin/scala/tools/detach/DetachPlugin.scala new file mode 100644 index 0000000000..c557a913b5 --- /dev/null +++ b/src/detach/plugin/scala/tools/detach/DetachPlugin.scala @@ -0,0 +1,41 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2010 LAMP/EPFL + * @author Stephane Micheloud + */ + +package scala.tools.detach + +import scala.tools.nsc.{Global, Phase} +import scala.tools.nsc.plugins.{Plugin, PluginComponent} + +class DetachPlugin(val global: Global) extends Plugin { + import global._ + + val name = "detach" + val description = "Perform detaching of remote closures" + + object detach extends { + val global = DetachPlugin.this.global + val runsAfter = List("lambdalift") + override val runsBefore = List("constructors") + } with Detach + + val components = List[PluginComponent](detach) + + def setEnabled(flag: Boolean) { detach.isEnabled = flag } + + override def processOptions(options: List[String], error: String => Unit) = { + var enabled = false + for (option <- options) { + if (option == "enable") { + enabled = true + } else { + error("Option not understood: "+option) + } + } + setEnabled(enabled) + } + + override val optionsHelp: Option[String] = + Some(" -P:detach:enable Enable detaching of remote closures") +} diff --git a/src/detach/plugin/scalac-plugin.xml b/src/detach/plugin/scalac-plugin.xml new file mode 100644 index 0000000000..6c8600e331 --- /dev/null +++ b/src/detach/plugin/scalac-plugin.xml @@ -0,0 +1,4 @@ +<plugin> + <name>detach</name> + <classname>scala.tools.detach.DetachPlugin</classname> +</plugin> |