From c09f6173e96ec741c9b38edfee969ae8c6b74d4e Mon Sep 17 00:00:00 2001 From: michelou Date: Wed, 17 Nov 2010 12:26:13 +0000 Subject: updates Scala examples, added detach plugin --- src/detach/library/scala/remoting/Channel.scala | 192 ++++ src/detach/library/scala/remoting/Debug.scala | 27 + .../library/scala/remoting/ServerChannel.scala | 68 ++ src/detach/library/scala/remoting/detach.scala | 49 + src/detach/library/scala/runtime/RemoteRef.scala | 182 +++ .../library/scala/runtime/remoting/Debug.scala | 85 ++ .../scala/runtime/remoting/RegistryDelegate.scala | 192 ++++ .../scala/runtime/remoting/RemoteBooleanRef.scala | 51 + .../scala/runtime/remoting/RemoteByteRef.scala | 51 + .../scala/runtime/remoting/RemoteCharRef.scala | 51 + .../scala/runtime/remoting/RemoteDoubleRef.scala | 50 + .../scala/runtime/remoting/RemoteFloatRef.scala | 50 + .../library/scala/runtime/remoting/RemoteGC.scala | 67 ++ .../scala/runtime/remoting/RemoteIntRef.scala | 51 + .../scala/runtime/remoting/RemoteLongRef.scala | 51 + .../scala/runtime/remoting/RemoteObjectRef.scala | 51 + .../scala/runtime/remoting/RemoteShortRef.scala | 50 + src/detach/plugin/scala/tools/detach/Detach.scala | 1193 ++++++++++++++++++++ .../plugin/scala/tools/detach/DetachPlugin.scala | 41 + src/detach/plugin/scalac-plugin.xml | 4 + 20 files changed, 2556 insertions(+) create mode 100644 src/detach/library/scala/remoting/Channel.scala create mode 100644 src/detach/library/scala/remoting/Debug.scala create mode 100644 src/detach/library/scala/remoting/ServerChannel.scala create mode 100644 src/detach/library/scala/remoting/detach.scala create mode 100644 src/detach/library/scala/runtime/RemoteRef.scala create mode 100644 src/detach/library/scala/runtime/remoting/Debug.scala create mode 100644 src/detach/library/scala/runtime/remoting/RegistryDelegate.scala create mode 100644 src/detach/library/scala/runtime/remoting/RemoteBooleanRef.scala create mode 100644 src/detach/library/scala/runtime/remoting/RemoteByteRef.scala create mode 100644 src/detach/library/scala/runtime/remoting/RemoteCharRef.scala create mode 100644 src/detach/library/scala/runtime/remoting/RemoteDoubleRef.scala create mode 100644 src/detach/library/scala/runtime/remoting/RemoteFloatRef.scala create mode 100644 src/detach/library/scala/runtime/remoting/RemoteGC.scala create mode 100644 src/detach/library/scala/runtime/remoting/RemoteIntRef.scala create mode 100644 src/detach/library/scala/runtime/remoting/RemoteLongRef.scala create mode 100644 src/detach/library/scala/runtime/remoting/RemoteObjectRef.scala create mode 100644 src/detach/library/scala/runtime/remoting/RemoteShortRef.scala create mode 100644 src/detach/plugin/scala/tools/detach/Detach.scala create mode 100644 src/detach/plugin/scala/tools/detach/DetachPlugin.scala create mode 100644 src/detach/plugin/scalac-plugin.xml (limited to 'src/detach') diff --git a/src/detach/library/scala/remoting/Channel.scala b/src/detach/library/scala/remoting/Channel.scala new file mode 100644 index 0000000000..6b1fbcee4e --- /dev/null +++ b/src/detach/library/scala/remoting/Channel.scala @@ -0,0 +1,192 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Channel.scala 18365 2009-07-21 11:00:42Z michelou $ + +package scala.remoting + +import java.io._ +import java.net._ +import java.rmi.server.RMIClassLoader + +/**

+ * The class Channel implements (basic) typed channels + * which use Java socket communication and Scala type manifests to + * provide type-safe send/receive operations between a localhost and another + * remote machine by specifying some host and port. + *

+ * + * @author Stephane Micheloud + * @version 1.1 + */ +class Channel protected (socket: Socket) { + + // Create a socket without a timeout + def this(host: String, port: Int) = this(new Socket(host, port)) + + // // Create a socket with a timeout + // val sockaddr: SocketAddress = new InetSocketAddress(addr, port) + // val socket = new Socket() + // // If the timeout occurs, SocketTimeoutException is thrown. + // socket.connect(sockaddr, 2000) // 2 seconds + + /** Returns the local address of this channel. */ + val host = socket.getInetAddress.getHostAddress + + /** Returns the port on which this channel is listening. */ + val port = socket.getLocalPort + + private var cl: ClassLoader = + try { + // requires permission in Java policy file + val codebase = System.getProperty("java.rmi.server.codebase") + if (codebase != null) info("codebase="+codebase) + RMIClassLoader.getClassLoader(codebase) + } + catch { + case e: Exception => + error("Class loader undefined: " + e.getMessage) + null + } + def classLoader: ClassLoader = cl + def classLoader_=(x: ClassLoader) { cl = x } + + info(""+this) + + private class CustomObjectInputStream(in: InputStream) + extends ObjectInputStream(in) { + override def resolveClass(desc: ObjectStreamClass): Class[_] = + if (cl eq null) + super.resolveClass(desc) + else + try { + info("resolve class "+desc.getName) + cl loadClass desc.getName + } + catch { + case e: ClassNotFoundException => + super.resolveClass(desc) + } + } +/* + // lazy modifier is required! + private lazy val in = + try { + new CustomObjectInputStream(socket.getInputStream) + } + catch { + case e: IOException => + error("Input stream undefined: "+e.getMessage+" ("+this+")") + null + } + private lazy val out = + try { + new ObjectOutputStream(socket.getOutputStream) + } + catch { + case e: IOException => + error("Output stream undefined: "+e.getMessage+" ("+this+")") + null + } +*/ + /** receive<primtype> methods may throw an + * IOException. + */ + def receiveUnit = receive[Unit] + def receiveBoolean = receive[Boolean] + def receiveByte = receive[Byte] + def receiveChar = receive[Char] + def receiveShort = receive[Short] + def receiveInt = receive[Int] + def receiveLong = receive[Long] + def receiveFloat = receive[Float] + def receiveDouble = receive[Double] + def receiveString = receive[String] + + /** receive method may throw either an + * ClassNotFoundException or an IOException. + * + * @throw ChannelException if received value has not + * the expected type. + */ + @throws(classOf[ChannelException]) + def receive[T](implicit expected: reflect.Manifest[T]): T = { + val in = new CustomObjectInputStream(socket.getInputStream) + val found = in.readObject().asInstanceOf[reflect.Manifest[_]] + info("receive: found="+found+", expected="+expected) + import scala.reflect.Manifest + val x = found match { + case Manifest.Unit => () + case Manifest.Boolean => in.readBoolean() + case Manifest.Byte => in.readByte() + case Manifest.Char => in.readChar() + case Manifest.Short => in.readShort() + case Manifest.Int => in.readInt() + case Manifest.Long => in.readLong() + case Manifest.Float => in.readFloat() + case Manifest.Double => in.readDouble() + case _ => in.readObject() + } + val res = if (found <:< expected) + x.asInstanceOf[T] + else + throw new ChannelException( + "\n\tfound \""+found+"\"\n\texpected \""+expected+"\"") + info("received "+res+" (available="+in.available+")") + res + } + + /** ? method may throw either an + * ClassNotFoundException or an IOException. + */ + def ?[T](implicit m: reflect.Manifest[T]): T = receive[T](m) + + /** send method may throw an IOException. + */ + def send[T](x: T)(implicit m: reflect.Manifest[T]) { + val out = new ObjectOutputStream(socket.getOutputStream) + out writeObject m + x match { + case x: Unit => // nop + case x: Boolean => out writeBoolean x + case x: Byte => out writeByte x + case x: Char => out writeChar x + case x: Short => out writeShort x + case x: Int => out writeInt x + case x: Long => out writeLong x + case x: Float => out writeFloat x + case x: Double => out writeDouble x + case x => out writeObject x + } + out.flush() + info("sent "+x) + } + + /** ! method may throw an IOException. + */ + def ![T](x: T)(implicit m: reflect.Manifest[T]) { send(x)(m) } + + def close() { + try { socket.close() } + catch { case e: IOException => } + info(this+" closed") + } + + override def toString: String = socket.toString + + private def info(msg: String) { + runtime.remoting.Debug.info("[Channel] "+msg) + } +} + +/** ChannelException may be thrown by the operation + * receive when the received data has not the expected type. + */ +case class ChannelException(msg: String) extends IOException(msg) + diff --git a/src/detach/library/scala/remoting/Debug.scala b/src/detach/library/scala/remoting/Debug.scala new file mode 100644 index 0000000000..6a63b8a25d --- /dev/null +++ b/src/detach/library/scala/remoting/Debug.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2007-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Debug.scala 17412 2009-03-31 10:08:25Z michelou $ + +package scala.remoting + +/** + * @author Stephane Micheloud + * @version 1.0 + */ +object Debug extends runtime.remoting.Debug { + private val f = new java.text.SimpleDateFormat("HH:mm:ss") + private val c = new java.util.GregorianCalendar + + def getTime: String = f format c.getTime + + def getLocation(obj: AnyRef): String = { + val s = obj.getClass().getClassLoader().toString() + s.substring(s.indexOf('[')) + } +} diff --git a/src/detach/library/scala/remoting/ServerChannel.scala b/src/detach/library/scala/remoting/ServerChannel.scala new file mode 100644 index 0000000000..6f9fe0a594 --- /dev/null +++ b/src/detach/library/scala/remoting/ServerChannel.scala @@ -0,0 +1,68 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2007-2010, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: ServerChannel.scala 18365 2009-07-21 11:00:42Z michelou $ + +package scala.remoting + +import java.net.{ServerSocket, Socket} + +/**

+ * Creates a server channel and binds its associated socket to the + * specified port number.
+ * Example: + *

+ *  class ComputeChannel(s: Socket) extends Channel(s) {
+ *    def receiveFunc = receive[Int => Int]
+ *  }
+ *  class ComputeServer(p: Int)
+ *  extends AbstractServerChannel[ComputeChannel](p) {
+ *     def newChannel(s: Socket) = new ComputeChannel(s)
+ *  }
+ * + * @author Stephane Micheloud + * @version 1.0 + */ +class ServerChannel(p: Int) extends AbstractServerChannel[Channel](p) { + def newChannel(s: Socket) = new Channel(s) +} + +abstract class AbstractServerChannel[T <: Channel](_port: Int) { + + /** Creates an input channel and binds its associated socket to any + * free port. + */ + def this() = this(0) + + // The maximum queue length for incoming requests to connect is set to 50. + private val serverSocket = new ServerSocket(_port) + + /** Returns the local address of this channel. */ + val host = serverSocket.getInetAddress.getHostAddress + + /** Returns the port on which this channel is listening. */ + val port = serverSocket.getLocalPort + info("Listening on port "+port) + + protected def newChannel(socket: Socket): T + + def accept: T = { + System.gc() // required! + newChannel(serverSocket.accept) + } + + def close() { + try { serverSocket.close() } + catch { case e: java.io.IOException => } + info("Server socket "+host+":"+port+" closed") + } + + protected def info(msg: String) { + runtime.remoting.Debug.info("[ServerChannel] "+msg) + } +} diff --git a/src/detach/library/scala/remoting/detach.scala b/src/detach/library/scala/remoting/detach.scala new file mode 100644 index 0000000000..d0fb694af5 --- /dev/null +++ b/src/detach/library/scala/remoting/detach.scala @@ -0,0 +1,49 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://www.scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: detach.scala 16901 2009-01-13 15:37:05Z michelou $ + +package scala.remoting + + +/** The detach object is a marker object which informs + * the Scala compiler that arguments whose type is a function type are + * eligible for remote closure generation. + * + * @author Stephane Micheloud + * @version 1.0, 13/07/2005 + */ +object detach { + + def apply[R](f: Function0[R]): Function0[R] = f + def apply[T0, R](f: Function1[T0, R]): Function1[T0, R] = f + def apply[T0, T1, R](f: Function2[T0, T1, R]): Function2[T0, T1, R] = f + def apply[T0, T1, T2, R](f: Function3[T0, T1, T2, R]): Function3[T0, T1, T2, R] = f + def apply[T0, T1, T2, T3, R](f: Function4[T0, T1, T2, T3, R]): Function4[T0, T1, T2, T3, R] = f + def apply[T0, T1, T2, T3, T4, R](f: Function5[T0, T1, T2, T3, T4, R]): Function5[T0, T1, T2, T3, T4, R] = f + def apply[T0, T1, T2, T3, T4, T5, R](f: Function6[T0, T1, T2, T3, T4, T5, R]): Function6[T0, T1, T2, T3, T4, T5, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, R](f: Function7[T0, T1, T2, T3, T4, T5, T6, R]): Function7[T0, T1, T2, T3, T4, T5, T6, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, R](f: Function8[T0, T1, T2, T3, T4, T5, T6, T7, R]): Function8[T0, T1, T2, T3, T4, T5, T6, T7, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, R](f: Function9[T0, T1, T2, T3, T4, T5, T6, T7, T8, R]): Function9[T0, T1, T2, T3, T4, T5, T6, T7, T8, R] = f + + // since 2.7.0 + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R](f: Function10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R]): Function10[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R](f: Function11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R]): Function11[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R](f: Function12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R]): Function12[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R](f: Function13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R]): Function13[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R](f: Function14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R]): Function14[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R](f: Function15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R]): Function15[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R](f: Function16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R]): Function16[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R](f: Function17[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R]): Function17[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R](f: Function18[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R]): Function18[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R](f: Function19[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R]): Function19[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R](f: Function20[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R]): Function20[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R](f: Function21[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R]): Function21[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R] = f + def apply[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R](f: Function22[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R]): Function22[T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R] = f +} + diff --git a/src/detach/library/scala/runtime/RemoteRef.scala b/src/detach/library/scala/runtime/RemoteRef.scala new file mode 100644 index 0000000000..cd3d2045e2 --- /dev/null +++ b/src/detach/library/scala/runtime/RemoteRef.scala @@ -0,0 +1,182 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RemoteRef.scala 18365 2009-07-21 11:00:42Z michelou $ + +package scala.runtime + +import java.net.{InetAddress, MalformedURLException} +import java.rmi.{NoSuchObjectException, NotBoundException, Remote} +import java.rmi.registry.{LocateRegistry, Registry} +import java.rmi.server.{ExportException, RemoteObject, UnicastRemoteObject} + +import scala.runtime.remoting.{Debug, RemoteGC} + +/** + * + * @author Stephane Micheloud + * @version 1.0 + */ +object RemoteRef { /*extends Thread { + start() + + private class QuitException extends Exception + private var isTerminated = false + + // keeps track of live remote objects + val remoteGC = new RemoteGC + + override def run() { + info("started thread") + try { + while (!isTerminated) { + this.synchronized { + try { + wait(200) + } catch { + case _: InterruptedException => + if (isTerminated) throw new QuitException + } + remoteGC.gc() + if (remoteGC.allClosed) + throw new QuitException + } // synchronized + + } + } catch { + case _: QuitException => + // allow thread to exit + } + } +*/ + try { + val prop = System.getProperty("sun.rmi.dgc.server.gcInterval") + if (prop eq null) + System.setProperty("sun.rmi.dgc.server.gcInterval", "10000") + } + catch { + case e => + error(e.getMessage) + } + + private val host = + try { + val prop = System.getProperty("java.rmi.server.hostname") + if (prop ne null) prop else InetAddress.getLocalHost.getHostAddress + } + catch { + case e => + warning(e.getMessage) + InetAddress.getLocalHost.getHostAddress + } + + private val port = + try { + val prop = System.getProperty("scala.remoting.port") + if (prop ne null) prop.toInt else Registry.REGISTRY_PORT + } + catch { + case e => + warning(e.getMessage) + Registry.REGISTRY_PORT // default port + } + + private val registry = + try { + LocateRegistry.createRegistry(port) + } + catch { + case e => + warning(e.getMessage) + LocateRegistry.getRegistry(host, port) + } + + private val prefix = "//"+host+":"+port+"/" + printDebugInfos + + // Variant 1: rebind/unbind + def bind(name: String, x: Remote): Remote = + try { + registry.rebind(prefix+name, x) + info("\""+prefix+name+"\" bound") + val stub = RemoteObject.toStub(x) + //remoteGC.newRef(stub) + stub + } catch { + case e: MalformedURLException => + error(e.getMessage); null + case e: ExportException => + info(""+e); null + case e: Exception => // AlreadyBoundException, etc.. + throw e + } + + def unbind(name: String) = + try { + registry.unbind(prefix+name) + info("\""+name+"\" unbound") + } catch { + case e: java.io.EOFException => + warning(e.getMessage) + case e: NotBoundException => + warning(e.getMessage+" already unbound") + case e: MalformedURLException => + error(e.getMessage) + case e: Exception => + throw e + } +/* + // Variant 2: un-/exportObject + def bind(name: String, x: Remote): Remote = + try { + val ex = UnicastRemoteObject.exportObject(x) + registry.rebind(prefix+name, ex) + info("\""+prefix+name+"\" bound") + //val stub = RemoteObject.toStub(ex) + //remoteGC.newRef(ex) + ex //stub + } catch { + case e: MalformedURLException => + error(e.getMessage); null + case e: ExportException => + info(""+e); null + case e: Exception => // AlreadyBoundException, etc.. + throw e + } + + def unbind(x: Remote) { + try { + UnicastRemoteObject.unexportObject(x, false) + info("\""+x+"\" unbound") + } catch { + case e: java.io.EOFException => + warning(e.getMessage) + case e: NotBoundException => + warning(e.getMessage+" already unbound") + case e: MalformedURLException => + error(e.getMessage) + case e: Exception => + throw e + } + } +*/ + private def info(msg: String) { Debug.info("[RemoteRef] "+msg) } + private def warning(msg: String) { Debug.warning("[RemoteRef] "+msg) } + private def error(msg: String) { Debug.error("[RemoteRef] "+msg) } + + private def printDebugInfos { + def property(name: String): String = + name+"="+( + try { System.getProperty(name, "") } + catch { case e => warning(e.getMessage); "?" }) + info(property("java.rmi.server.hostname")) + info(property("sun.rmi.dgc.server.gcInterval")) + info("registry="+registry) + info("prefix="+prefix) + } +} diff --git a/src/detach/library/scala/runtime/remoting/Debug.scala b/src/detach/library/scala/runtime/remoting/Debug.scala new file mode 100644 index 0000000000..f5e50eede4 --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/Debug.scala @@ -0,0 +1,85 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2007-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Debug.scala 17777 2009-05-19 18:16:25Z michelou $ + +package scala.runtime.remoting + +/** + * @author Stephane Micheloud + * @version 1.0 + */ +object Debug extends Debug { + override def info (msg: String) { if (lib) super.info(msg) } + override def verbose(msg: String) { if (lib) super.verbose(msg) } + override def warning(msg: String) { if (lib) super.warning(msg) } + override def error (msg: String) { if (lib) super.error(msg) } +} + +/** + * @author Stephane Micheloud + * @version 1.0 + */ +class Debug(tag: String) { + + def this() = this("") + + object Level extends Enumeration { + type Level = Value + val SILENT, ERROR, WARNING, VERBOSE, INFO = Value + } + + private val level0 = + try { + val prop = System.getProperty("scala.remoting.logLevel") + if (prop ne null) prop.toLowerCase else "" + } + catch { + case e => + Console.err.println(e.getMessage) + "" + } + + import Level._ + protected var (lev, lib) = { + val p = java.util.regex.Pattern.compile("(error|warning|verbose|info)(\\,lib)?(.*)") + val m = p matcher level0 + val (s, b) = + if (m.matches) (m.group(1), m.group(2) ne null) + else ("", false) + s match { + case "error" => (ERROR , b) + case "warning" => (WARNING, b) + case "verbose" => (VERBOSE, b) + case "info" => (INFO , b) + case _ => (SILENT , false) + } + } + + def level = lev + def level_= (lev: Level) = { this.lev = lev } + + private val tag0: String = + if (tag != null & tag.length > 0) tag+" " else "" + + def info(msg: String) { + if (lev >= INFO) Console.println(tag0 + "(info): " + msg) + } + + def verbose(msg: String) { + if (lev >= VERBOSE) Console.println(tag0 + "(verb): " + msg) + } + + def warning(msg: String) { + if (lev >= WARNING) Console.err.println(tag0 + "(warn): " + msg) + } + + def error(msg: String) { + if (lev >= ERROR) Console.err.println(tag0 + "(erro): " + msg) + } +} diff --git a/src/detach/library/scala/runtime/remoting/RegistryDelegate.scala b/src/detach/library/scala/runtime/remoting/RegistryDelegate.scala new file mode 100644 index 0000000000..ba6f6c042d --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/RegistryDelegate.scala @@ -0,0 +1,192 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RegistryDelegate.scala 18234 2009-07-07 13:21:57Z michelou $ + +package scala.runtime.remoting + +import java.rmi.{RMISecurityManager, Remote, RemoteException} +import java.rmi.registry.{LocateRegistry, Registry} +import java.rmi.server.UnicastRemoteObject + +/** + *

+ * This class implements the registry delegate concept + * (see http://www.genady.net/rmi/v20/docs/delegate/RegistryDelegate.html) + *

+ *

+ * In order to enforce some level of security, the standard RMI registry + * implementation (e.g. rmiregistry.exe) only allows processes + * on the same host to register objects in the registry (think of a bank + * running a registry on one of its servers, and doesn't want anybody + * modifying it). So, by design, if a process tries to + * bind(String, Remote) an object to a remote registry, + * an exception will be thrown. + *

+ *

+ * However, the design of a distributed system may require remote clients to + * register themselves in a central registry. If such system is deployed in a + * controlled and trusted environment (e.g., a firewalled intranet with tight + * access control), the security risk may be acceptable. + *

+ *

+ * The simplest technical solution to the remote registration problem is to + * have a registry delegate. A registry delegate is an object that serves as + * a proxy for the real registry. The delegate itself usually appears in the + * registry under a well known name. It implements the Registry interface and + * simply delegates all method calls to the appropriate methods of the real + * registry. The delegate is allowed to perform bind and unbind operations + * because it is running on the same host as the registry. + *

+ *

+ * The common scenario for starting a registry and creating the delegate is + * starting a class with the following main(Array[String]) method: + *

+ *
+ *   @throws(classOf[AccessException], classOf[RemoteException], classOf[AlreadyBoundException])
+ *   object namingService {
+ *     def main(args: Array[String]) {
+ *       if (System.getSecurityManager() == null)
+ *         System.setSecurityManager(new RMISecurityManager())
+ *
+ *       val registry = LocateRegistry.createRegistry(REGISTRY_PORT)
+ *       registry.bind(DELEGATE_NAME, new RegistryDelegate());
+ *
+ *       do {
+ *         try {
+ *           Thread.sleep(Long.MAX_VALUE)
+ *         } catch {
+ *           case e: InterruptedException => // do nothing
+ *           case e: Throwable => e.printStackTrace(); System.exit(1)
+ *         }
+ *       } while (true)
+ *     }
+ *  }
+ *

+ * The common usage scenario looks something like: + *

+ *   Registry remoteRegistry = LocateRegistry.getRegistry("remotehost.mycompany.com");
+ *   Registry delegate = (Registry) remoteRegistry.lookup(DELEGATE_NAME);
+ *   delegate.bind("someName", new SomeRemoteObject());
+ *

+ * The getRegistryDelegate(String) method is a helper method + * that fetches the registry delegate for you. + *

+ *

+ * The main(Array[String]) method of this class will create a + * local registry on the default port, create a registry delegate and bind + * it under the well known name that you chose in the wizard + * (DELEGATE_NAME). + *

+ * + * @author Genady Beryozkin, rmi-info@genady.net + */ + +object RMIDelegate { + /** The name under which the delegate appears in the registry. */ + val DELEGATE_NAME = "foo" + + /** This method retrieves the registry delegate from a registry that is + * running on a remote host. + */ + @throws(classOf[RemoteException]) + def getRegistryDelegate(remoteHost: String): Registry = + getRegistryDelegate(remoteHost, Registry.REGISTRY_PORT) + + /** This method retrieves the registry delegate from a registry that is + * running on a remote host. + */ + @throws(classOf[RemoteException]) + def getRegistryDelegate(remoteHost: String, remotePort: Int): Registry = { + val registry = LocateRegistry.getRegistry(remoteHost, remotePort) + (registry lookup DELEGATE_NAME).asInstanceOf[Registry] + } + + /** A simple way to run a registry and bind a registry delegate. */ + @throws(classOf[RemoteException]) + def main(args: Array[String]) { + var port = Registry.REGISTRY_PORT + + if (args.length > 0) { + if (args(0) equals "-help") { + println("Usage: rmidelegate ") + exit(0) + } + try { + port = args(0).toInt + } catch { + case e: NumberFormatException => + println("Usage: rmidelegate ") + exit(1) + } + val opts = args filter (_ startsWith "-J-D") + for (opt <- opts) { + val x = opt.substring(4) split "=" + if (x.length == 2) System.setProperty(x(0), x(1)) + else System.setProperty(x(0), "") + } + } + + if (System.getSecurityManager() == null) + System.setSecurityManager(new RMISecurityManager() { + override def checkPermission(p: java.security.Permission) {} + }) + + + val registry = LocateRegistry.createRegistry(port) + registry.bind(DELEGATE_NAME, new RegistryDelegate()) + + do { + try { + Thread.sleep(Long.MaxValue) + } catch { + case e: InterruptedException => + // do nothing + case e: Throwable => + e.printStackTrace() + exit(1) + } + } while (true) + } + +} + +/** Create a delegate for a user provided registry instance. The registry is + * assumed to be a local registry, as there is no point in creating a delegate + * for a remote registry. + */ +class RegistryDelegate(reg: Registry) extends UnicastRemoteObject with Registry { + /** The local registry */ + private val localRegistry: Registry = reg + + /** Create a delegate for a local registry that is bound to the default + * local port (1099). + */ + def this() = this(LocateRegistry.getRegistry()) + + /** Create a delegate for a local registry that is bound to a user + * specified port. + */ + def this(port: Int) = this(LocateRegistry.getRegistry(port)) + + @throws(classOf[RemoteException]) + def bind(name: String, obj: Remote) { localRegistry.bind(name, obj) } + + @throws(classOf[RemoteException]) + def list(): Array[String] = localRegistry.list() + + @throws(classOf[RemoteException]) + def lookup(name: String): Remote = localRegistry.lookup(name) + + @throws(classOf[RemoteException]) + def rebind(name: String, obj: Remote) { localRegistry.rebind(name, obj) } + + @throws(classOf[RemoteException]) + def unbind(name: String) { localRegistry.unbind(name) } + +} diff --git a/src/detach/library/scala/runtime/remoting/RemoteBooleanRef.scala b/src/detach/library/scala/runtime/remoting/RemoteBooleanRef.scala new file mode 100644 index 0000000000..be5597fa23 --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/RemoteBooleanRef.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RemoteBooleanRef.scala 18398 2009-07-28 14:26:36Z michelou $ + +package scala.runtime.remoting + +import java.rmi.server.{UnicastRemoteObject, Unreferenced} +import scala.runtime.{BooleanRef, RemoteRef} + +/** + * The trait RemoteRemoteBooleanRef provides a remote interface + * for manipulating boolean references. + * + * @author Stephane Micheloud + * @version 1.0 + */ +@remote +trait RemoteBooleanRef { + def elem_=(value: Boolean) + def elem: Boolean +} + +/** + * The class RemoteBooleanRefImpl implements a remote (global) + * boolean reference by inheriting from the class + * UnicastRemoteObject. + * + * In particular, it forwards method invocations to the elem + * accessors of class runtime.BooleanRef and implements the + * java.rmi.server.Unreferenced interface to automatically + * remove the no more referenced binding from the registry. + * + * @author Stephane Micheloud + * @version 1.0 + */ +class RemoteBooleanRefImpl(name: String, x: BooleanRef) +extends UnicastRemoteObject with RemoteBooleanRef with Unreferenced { + def elem_=(value: Boolean) { x.elem = value } + def elem: Boolean = x.elem + override def toString() = x.elem.toString + def unreferenced() { + Debug.info("[RemoteBooleanRefImpl] unreferenced: "+this) + RemoteRef.unbind(name) + } +} diff --git a/src/detach/library/scala/runtime/remoting/RemoteByteRef.scala b/src/detach/library/scala/runtime/remoting/RemoteByteRef.scala new file mode 100644 index 0000000000..cac0ceb260 --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/RemoteByteRef.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RemoteByteRef.scala 18398 2009-07-28 14:26:36Z michelou $ + +package scala.runtime.remoting + +import java.rmi.server.{UnicastRemoteObject, Unreferenced} +import scala.runtime.{ByteRef, RemoteRef} + +/** + * The trait RemoteRemoteByteRef provides a remote interface + * for manipulating byte references. + * + * @author Stephane Micheloud + * @version 1.0 + */ +@remote +trait RemoteByteRef { + def elem_=(value: Byte) + def elem: Byte +} + +/** + * The class RemoteByteRefImpl implements a remote (global) + * byte reference by inheriting from the class + * UnicastRemoteObject. + * + * In particular, it forwards method invocations to the elem + * accessors of class runtime.ByteRef and implements the + * java.rmi.server.Unreferenced interface to automatically + * remove the no more referenced binding from the registry. + * + * @author Stephane Micheloud + * @version 1.0 + */ +class RemoteByteRefImpl(name: String, x: ByteRef) +extends UnicastRemoteObject with RemoteByteRef with Unreferenced { + def elem_=(value: Byte) { x.elem = value } + def elem: Byte = x.elem + override def toString() = x.elem.toString + def unreferenced() { + Debug.info("[RemoteByteRefImpl] unreferenced: "+this) + RemoteRef.unbind(name) + } +} diff --git a/src/detach/library/scala/runtime/remoting/RemoteCharRef.scala b/src/detach/library/scala/runtime/remoting/RemoteCharRef.scala new file mode 100644 index 0000000000..9b00c8c1db --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/RemoteCharRef.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RemoteCharRef.scala 18398 2009-07-28 14:26:36Z michelou $ + +package scala.runtime.remoting + +import java.rmi.server.{UnicastRemoteObject, Unreferenced} +import scala.runtime.{CharRef, RemoteRef} + +/** + * The trait RemoteRemoteCharRef provides a remote interface + * for manipulating character references. + * + * @author Stephane Micheloud + * @version 1.0 + */ +@remote +trait RemoteCharRef { + def elem_=(value: Char) + def elem: Char +} + +/** + * The class RemoteCharRefImpl implements a remote (global) + * character reference by inheriting from the class + * UnicastRemoteObject. + * + * In particular, it forwards method invocations to the elem + * accessors of class runtime.CharRef and implements the + * java.rmi.server.Unreferenced interface to automatically + * remove the no more referenced binding from the registry. + * + * @author Stephane Micheloud + * @version 1.0 + */ +class RemoteCharRefImpl(name: String, x: CharRef) +extends UnicastRemoteObject with RemoteCharRef with Unreferenced { + def elem_=(value: Char) { x.elem = value } + def elem: Char = x.elem + override def toString() = x.elem.toString + def unreferenced() { + Debug.info("[RemoteCharRefImpl] unreferenced: "+this) + RemoteRef.unbind(name) + } +} diff --git a/src/detach/library/scala/runtime/remoting/RemoteDoubleRef.scala b/src/detach/library/scala/runtime/remoting/RemoteDoubleRef.scala new file mode 100644 index 0000000000..bdbcd398bd --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/RemoteDoubleRef.scala @@ -0,0 +1,50 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RemoteDoubleRef.scala 18398 2009-07-28 14:26:36Z michelou $ + +package scala.runtime.remoting + +import java.rmi.server.{UnicastRemoteObject, Unreferenced} +import scala.runtime.{DoubleRef, RemoteRef} + +/** + * The trait RemoteRemoteDoubleRef provides.. + * + * @author Stephane Micheloud + * @version 1.0 + */ +@remote +trait RemoteDoubleRef { + def elem_=(value: Double) + def elem: Double +} + +/** + * The class RemoteDoubleRefImpl implements a remote (global) + * double reference by inheriting from the class + * UnicastRemoteObject. + * + * In particular, it forwards method invocations to the elem + * accessors of class runtime.DoubleRef and implements the + * java.rmi.server.Unreferenced interface to automatically + * remove the no more referenced binding from the registry. + * + * @author Stephane Micheloud + * @version 1.0 + */ +class RemoteDoubleRefImpl(name: String, x: DoubleRef) +extends UnicastRemoteObject with RemoteDoubleRef with Unreferenced { + def elem_=(value: Double) { x.elem = value } + def elem: Double = x.elem + override def toString() = x.elem.toString + def unreferenced() { + Debug.info("[RemoteDoubleRefImpl] unreferenced: "+this) + RemoteRef.unbind(name) + } +} diff --git a/src/detach/library/scala/runtime/remoting/RemoteFloatRef.scala b/src/detach/library/scala/runtime/remoting/RemoteFloatRef.scala new file mode 100644 index 0000000000..158894bc18 --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/RemoteFloatRef.scala @@ -0,0 +1,50 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RemoteFloatRef.scala 18398 2009-07-28 14:26:36Z michelou $ + +package scala.runtime.remoting + +import java.rmi.server.{UnicastRemoteObject, Unreferenced} +import scala.runtime.{FloatRef, RemoteRef} + +/** + * The trait RemoteRemoteFloatRef provides a remote interface + * for manipulating float references. + * + * @author Stephane Micheloud + * @version 1.0 + */ +@remote +trait RemoteFloatRef { + def elem_=(value: Float) + def elem: Float +} + +/** + * The class RemoteFloatRefImpl implements a remote (global) + * float reference by inheriting from the class + * UnicastRemoteObject. + * + * In particular, it forwards method invocations to the elem + * accessors of class runtime.FloatRef and implements the + * java.rmi.server.Unreferenced interface. + * + * @author Stephane Micheloud + * @version 1.0 + */ +class RemoteFloatRefImpl(name: String, x: FloatRef) +extends UnicastRemoteObject with RemoteFloatRef with Unreferenced { + def elem_=(value: Float) { x.elem = value } + def elem: Float = x.elem + override def toString() = x.elem.toString + def unreferenced() { + Debug.info("[RemoteIntFloatImpl] unreferenced: "+this) + RemoteRef.unbind(name) + } +} diff --git a/src/detach/library/scala/runtime/remoting/RemoteGC.scala b/src/detach/library/scala/runtime/remoting/RemoteGC.scala new file mode 100644 index 0000000000..d4a5108cb8 --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/RemoteGC.scala @@ -0,0 +1,67 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RemoteGC.scala 17547 2009-04-21 13:56:28Z michelou $ + +package scala.runtime.remoting + +import java.lang.ref.{Reference, WeakReference, ReferenceQueue} +import java.rmi.{NoSuchObjectException, Remote} +import java.rmi.server.UnicastRemoteObject + +import scala.collection.mutable.HashSet + +/** + * + * @author Stephane Micheloud + * @version 1.0 + */ +// Adapted from scala.actors.ActorGC +private [runtime] class RemoteGC { + + private val refQueue = new ReferenceQueue[Remote] + private val refSet = new HashSet[Reference[T] forSome { type T <: Remote }] + + private var liveRefs = 0 + + def newRef(a: Remote) = synchronized { + refSet += new WeakReference(a, refQueue) + liveRefs += 1 + info("added object reference \""+a+"\" ("+liveRefs+")") + } + + def gc() = synchronized { + info("GC called ("+liveRefs+")") + // check for unreachable object references + def drain() { + val wr = refQueue.poll + if (wr != null) { + val msg = try { + UnicastRemoteObject.unexportObject(wr.get, true/*force*/) + "removed object reference" + } + catch { + case e: NoSuchObjectException => + "object already unbound" + } + info(msg+" ("+liveRefs+")") + liveRefs -= 1 + refSet -= wr + // continue draining + drain() + } + } + drain() + } + + def allClosed: Boolean = synchronized { + liveRefs <= 0 + } + + private def info(msg: String) { Debug.info("[RemoteGC] "+msg) } +} diff --git a/src/detach/library/scala/runtime/remoting/RemoteIntRef.scala b/src/detach/library/scala/runtime/remoting/RemoteIntRef.scala new file mode 100644 index 0000000000..211a7db971 --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/RemoteIntRef.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RemoteIntRef.scala 18398 2009-07-28 14:26:36Z michelou $ + +package scala.runtime.remoting + +import java.rmi.server.{UnicastRemoteObject, Unreferenced} +import scala.runtime.{IntRef, RemoteRef} + +/** + * The trait RemoteRemoteIntRef provides a remote interface + * for manipulating integer references. + * + * @author Stephane Micheloud + * @version 1.0 + */ +@remote +trait RemoteIntRef { + def elem_=(value: Int) + def elem: Int +} + +/** + * The class RemoteIntRefImpl implements a remote (global) + * integer reference by inheriting from the class + * UnicastRemoteObject. + * + * In particular, it forwards method invocations to the elem + * accessors of class runtime.IntRef and implements the + * java.rmi.server.Unreferenced interface to automatically + * remove the no more referenced binding from the registry. + * + * @author Stephane Micheloud + * @version 1.0 + */ +class RemoteIntRefImpl(name: String, x: IntRef) +extends UnicastRemoteObject with RemoteIntRef with Unreferenced { + def elem_=(value: Int) { x.elem = value } + def elem: Int = x.elem + override def toString() = x.elem.toString + def unreferenced() { + Debug.info("[RemoteIntRefImpl] unreferenced: "+this) + RemoteRef.unbind(name) + } +} diff --git a/src/detach/library/scala/runtime/remoting/RemoteLongRef.scala b/src/detach/library/scala/runtime/remoting/RemoteLongRef.scala new file mode 100644 index 0000000000..469bb534ed --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/RemoteLongRef.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RemoteLongRef.scala 18398 2009-07-28 14:26:36Z michelou $ + +package scala.runtime.remoting + +import java.rmi.server.{UnicastRemoteObject, Unreferenced} +import scala.runtime.{LongRef, RemoteRef} + +/** + * The trait RemoteRemoteLongRef provides a remote interface + * for manipulating long integer references. + * + * @author Stephane Micheloud + * @version 1.0 + */ +@remote +trait RemoteLongRef { + def elem_=(value: Long) + def elem: Long +} + +/** + * The class RemoteLongRefImpl implements a remote (global) + * long integer reference by inheriting from the class + * UnicastRemoteObject. + * + * In particular, it forwards method invocations to the elem + * accessors of class runtime.LongRef and implements the + * java.rmi.server.Unreferenced interface to automatically + * remove the no more referenced binding from the registry. + * + * @author Stephane Micheloud + * @version 1.0 + */ +class RemoteLongRefImpl(name: String, x: LongRef) +extends UnicastRemoteObject with RemoteLongRef with Unreferenced { + def elem_=(value: Long) { x.elem = value } + def elem: Long = x.elem + override def toString() = x.elem.toString + def unreferenced() { + Debug.info("[RemoteLongRefImpl] unreferenced: "+this) + RemoteRef.unbind(name) + } +} diff --git a/src/detach/library/scala/runtime/remoting/RemoteObjectRef.scala b/src/detach/library/scala/runtime/remoting/RemoteObjectRef.scala new file mode 100644 index 0000000000..c5c6989618 --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/RemoteObjectRef.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RemoteObjectRef.scala 18398 2009-07-28 14:26:36Z michelou $ + +package scala.runtime.remoting + +import java.rmi.server.{UnicastRemoteObject, Unreferenced} +import scala.runtime.{ObjectRef, RemoteRef} + +/** + * The trait RemoteRemoteObjectRef provides a remote interface + * for manipulating object references. + * + * @author Stephane Micheloud + * @version 1.0 + */ +@remote +trait RemoteObjectRef { + def elem_=(value: AnyRef) + def elem: AnyRef +} + +/** + * The class RemoteObjectRefImpl implements a remote (global) + * object reference by inheriting from the class + * UnicastRemoteObject. + * + * In particular, it forwards method invocations to the elem + * accessors of class runtime.ObjectRef and implements the + * java.rmi.server.Unreferenced interface to automatically + * remove the no more referenced binding from the registry. + * + * @author Stephane Micheloud + * @version 1.0 + */ +class RemoteObjectRefImpl(name: String, x: ObjectRef) +extends UnicastRemoteObject with RemoteObjectRef with Unreferenced { + def elem_=(value: AnyRef) { x.elem = value } + def elem: AnyRef = x.elem + override def toString() = x.elem.toString + def unreferenced() { + Debug.info("[RemoteObjectRefImpl] unreferenced: "+this) + RemoteRef.unbind(name) + } +} diff --git a/src/detach/library/scala/runtime/remoting/RemoteShortRef.scala b/src/detach/library/scala/runtime/remoting/RemoteShortRef.scala new file mode 100644 index 0000000000..bc94d79b9c --- /dev/null +++ b/src/detach/library/scala/runtime/remoting/RemoteShortRef.scala @@ -0,0 +1,50 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: RemoteShortRef.scala 18398 2009-07-28 14:26:36Z michelou $ + +package scala.runtime.remoting + +import java.rmi.server.{UnicastRemoteObject, Unreferenced} +import scala.runtime.{ShortRef, RemoteRef} + +/** + * The trait RemoteRemoteShortRef provides a remote interface + * for manipulating short integer references. + * + * @author Stephane Micheloud + * @version 1.0 + */ +@remote +trait RemoteShortRef { + def elem_=(value: Short) + def elem: Short +} + +/** + * The class RemoteShortRefImpl implements a remote (global) + * short integer reference by inheriting from the class + * UnicastRemoteObject. + * + * In particular, it forwards method invocations to the elem + * accessors of class runtime.ShortRef and implements the + * java.rmi.server.Unreferenced interface. + * + * @author Stephane Micheloud + * @version 1.0 + */ +class RemoteShortRefImpl(name: String, x: ShortRef) +extends UnicastRemoteObject with RemoteShortRef with Unreferenced { + def elem_=(value: Short) { x.elem = value } + def elem: Short = x.elem + override def toString() = x.elem.toString + def unreferenced() { + Debug.info("[RemoteShortRefImpl] unreferenced: "+this) + RemoteRef.unbind(name) + } +} 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] + + /**

+ * The method freeObjTraverser.traverse is invoked + * in the method DetachPlugin.transformUnit in order to + * gather information about objects referenced inside a detached + * closure and which will be accessed remotely through object proxies. + *

+ *

+ * Object proxies are generated in method mkClosureApply + * and their definitions are generated in method genProxy. + *

+ */ + 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="" 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] + } +*/ + /**

+ * Given the closure definition (generated by previous phases) + *

+     *    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
+     *    }
+ *

+ * the method mkClosureDef transforms the above code + * to the following: + *

+     *    @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
+     *    }
+ *

+ * In particular, it performs the following operations: + * 1) add constructor parameter proxy_n to access + * proxy of the enclosing class + * 2) change reference types in constructor arguments to type + * ' + * 3) change occurences of this identifier to + * proxy_n in template code + * 4) change reference types of local value definitions associated + * to updated constructor arguments to type Remote_type_Ref + *

+ * + * @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.==) + + /**

+ * Method updateConstructorParams 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". + *

+ *

+ * 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. + *

+ */ + 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 + + /**

+ * Given a class C with member x + * which is (remotely) referenced from inside a detached closure: + *

+     *    class C extends .. {
+     *      var x: Int
+     *    }
+ *

+ * the method addProxy generates the following two + * proxy definitions (used later in method mkClosureApply + * to generate object proxies): + *

+     *    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)
+     *    }
+ */ + 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) + + /**

+ * Given a detached closure applied in some environment consisting + * of an enclosing class C and some local variables + * x$1 (immutable) and y$1 (mutable): + *

+     *    scala.remoting.detach.apply({
+     *      (new $anonfun$1(C.this, x$1, y$1): Function1)
+     *    })
+ *

+ * the above code is transformed to the following block: + *

+     *    {
+     *      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)
+     *    }
+     *  
+ */ + 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 + + /**

+ * Method mkClosureInstance updates the list of actual + * parameters passed to the closure instance. + *

+ */ + 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 + + /**

+ * Method transformUnit performs three successive operations: + *

+ *
    + *
  1. it first gathers infos about free objects and detached + * closures;
  2. + *
  3. it then adds proxies for free objects;
  4. + *
  5. finally, if transforms detached closures (both definition and + * instantation).
  6. + *
+ */ + 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 @@ + + detach + scala.tools.detach.DetachPlugin + -- cgit v1.2.3