summaryrefslogtreecommitdiff
path: root/src/detach
diff options
context:
space:
mode:
authormichelou <michelou@epfl.ch>2010-11-17 12:26:13 +0000
committermichelou <michelou@epfl.ch>2010-11-17 12:26:13 +0000
commitc09f6173e96ec741c9b38edfee969ae8c6b74d4e (patch)
tree1f06579f72afa12092acd0b5bbb7c678291cf619 /src/detach
parent363a1456f671323b35dcacf2c8b8eb39180b8a53 (diff)
downloadscala-c09f6173e96ec741c9b38edfee969ae8c6b74d4e.tar.gz
scala-c09f6173e96ec741c9b38edfee969ae8c6b74d4e.tar.bz2
scala-c09f6173e96ec741c9b38edfee969ae8c6b74d4e.zip
updates Scala examples, added detach plugin
Diffstat (limited to 'src/detach')
-rw-r--r--src/detach/library/scala/remoting/Channel.scala192
-rw-r--r--src/detach/library/scala/remoting/Debug.scala27
-rw-r--r--src/detach/library/scala/remoting/ServerChannel.scala68
-rw-r--r--src/detach/library/scala/remoting/detach.scala49
-rw-r--r--src/detach/library/scala/runtime/RemoteRef.scala182
-rw-r--r--src/detach/library/scala/runtime/remoting/Debug.scala85
-rw-r--r--src/detach/library/scala/runtime/remoting/RegistryDelegate.scala192
-rw-r--r--src/detach/library/scala/runtime/remoting/RemoteBooleanRef.scala51
-rw-r--r--src/detach/library/scala/runtime/remoting/RemoteByteRef.scala51
-rw-r--r--src/detach/library/scala/runtime/remoting/RemoteCharRef.scala51
-rw-r--r--src/detach/library/scala/runtime/remoting/RemoteDoubleRef.scala50
-rw-r--r--src/detach/library/scala/runtime/remoting/RemoteFloatRef.scala50
-rw-r--r--src/detach/library/scala/runtime/remoting/RemoteGC.scala67
-rw-r--r--src/detach/library/scala/runtime/remoting/RemoteIntRef.scala51
-rw-r--r--src/detach/library/scala/runtime/remoting/RemoteLongRef.scala51
-rw-r--r--src/detach/library/scala/runtime/remoting/RemoteObjectRef.scala51
-rw-r--r--src/detach/library/scala/runtime/remoting/RemoteShortRef.scala50
-rw-r--r--src/detach/plugin/scala/tools/detach/Detach.scala1193
-rw-r--r--src/detach/plugin/scala/tools/detach/DetachPlugin.scala41
-rw-r--r--src/detach/plugin/scalac-plugin.xml4
20 files changed, 2556 insertions, 0 deletions
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
+
+/** <p>
+ * The class <code>Channel</code> implements (basic) typed channels
+ * which use <a href="http://java.sun.com/docs/books/tutorial/networking/sockets/"
+ * target="_top"/>Java socket</a> communication and Scala type manifests to
+ * provide type-safe send/receive operations between a localhost and another
+ * remote machine by specifying some <code>host</code> and <code>port</code>.
+ * </p>
+ *
+ * @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
+ }
+*/
+ /** <code>receive&lt;primtype&gt;</code> methods may throw an
+ * <code>IOException</code>.
+ */
+ 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]
+
+ /** <code>receive</code> method may throw either an
+ * <code>ClassNotFoundException</code> or an <code>IOException</code>.
+ *
+ * @throw <code>ChannelException</code> 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
+ }
+
+ /** <code>?</code> method may throw either an
+ * <code>ClassNotFoundException</code> or an <code>IOException</code>.
+ */
+ def ?[T](implicit m: reflect.Manifest[T]): T = receive[T](m)
+
+ /** <code>send</code> method may throw an <code>IOException</code>.
+ */
+ 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)
+ }
+
+ /** <code>!</code> method may throw an <code>IOException</code>.
+ */
+ 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)
+ }
+}
+
+/** <code>ChannelException</code> may be thrown by the operation
+ * <code>receive</code> 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}
+
+/** <p>
+ * Creates a server channel and binds its associated socket to the
+ * specified port number.<br/>
+ * Example:
+ * </p><pre>
+ * <b>class</b> ComputeChannel(s: Socket) <b>extends</b> Channel(s) {
+ * <b>def</b> receiveFunc = receive[Int => Int]
+ * }
+ * <b>class</b> ComputeServer(p: Int)
+ * <b>extends</b> AbstractServerChannel[ComputeChannel](p) {
+ * <b>def</b> newChannel(s: Socket) = <b>new</b> ComputeChannel(s)
+ * }</pre>
+ *
+ * @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 <code>detach</code> object is a <em>marker object</em> 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
+
+/**
+ * <p>
+ * This class implements the registry delegate concept
+ * (see http://www.genady.net/rmi/v20/docs/delegate/RegistryDelegate.html)
+ * </p>
+ * <p>
+ * In order to enforce some level of security, the standard RMI registry
+ * implementation (e.g. <code>rmiregistry.exe</code>) 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
+ * <code>bind(String, Remote)</code> an object to a remote registry,
+ * an exception will be thrown.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * The common scenario for starting a registry and creating the delegate is
+ * starting a class with the following <code>main(Array[String])</code> method:
+ * </p>
+ * <pre>
+ * @throws(classOf[AccessException], classOf[RemoteException], classOf[AlreadyBoundException])
+ * <b>object</b> namingService {
+ * <b>def</b> main(args: Array[String]) {
+ * <b>if</b> (System.getSecurityManager() == <b>null</b>)
+ * System.setSecurityManager(<b>new</b> RMISecurityManager())
+ *
+ * <b>val</b> registry = LocateRegistry.createRegistry(REGISTRY_PORT)
+ * registry.bind(DELEGATE_NAME, <b>new</b> RegistryDelegate());
+ *
+ * do {
+ * <b>try</b> {
+ * Thread.sleep(Long.MAX_VALUE)
+ * } <b>catch</b> {
+ * <b>case</b> e: InterruptedException => // do nothing
+ * <b>case</b> e: Throwable => e.printStackTrace(); System.exit(1)
+ * }
+ * } while (<b>true</b>)
+ * }
+ * }</pre>
+ * <p>
+ * The common usage scenario looks something like:
+ * </p><pre>
+ * Registry remoteRegistry = LocateRegistry.getRegistry("remotehost.mycompany.com");
+ * Registry delegate = (Registry) remoteRegistry.lookup(DELEGATE_NAME);
+ * delegate.bind("someName", <b>new</b> SomeRemoteObject());</pre>
+ * <p>
+ * The <code>getRegistryDelegate(String)</code> method is a helper method
+ * that fetches the registry delegate for you.
+ * </p>
+ * <p>
+ * The <code>main(Array[String])</code> 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
+ * (<code>DELEGATE_NAME</code>).
+ * </p>
+ *
+ * @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 <options> <port>")
+ exit(0)
+ }
+ try {
+ port = args(0).toInt
+ } catch {
+ case e: NumberFormatException =>
+ println("Usage: rmidelegate <options> <port>")
+ 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 Remote<code>RemoteBooleanRef</code> 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 <code>RemoteBooleanRefImpl</code> implements a remote (global)
+ * boolean reference by inheriting from the class
+ * <code>UnicastRemoteObject</code>.
+ *
+ * In particular, it forwards method invocations to the <code>elem</code>
+ * accessors of class <code>runtime.BooleanRef</code> and implements the
+ * <code>java.rmi.server.Unreferenced</code> 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 Remote<code>RemoteByteRef</code> 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 <code>RemoteByteRefImpl</code> implements a remote (global)
+ * byte reference by inheriting from the class
+ * <code>UnicastRemoteObject</code>.
+ *
+ * In particular, it forwards method invocations to the <code>elem</code>
+ * accessors of class <code>runtime.ByteRef</code> and implements the
+ * <code>java.rmi.server.Unreferenced</code> 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 Remote<code>RemoteCharRef</code> 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 <code>RemoteCharRefImpl</code> implements a remote (global)
+ * character reference by inheriting from the class
+ * <code>UnicastRemoteObject</code>.
+ *
+ * In particular, it forwards method invocations to the <code>elem</code>
+ * accessors of class <code>runtime.CharRef</code> and implements the
+ * <code>java.rmi.server.Unreferenced</code> 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 Remote<code>RemoteDoubleRef</code> provides..
+ *
+ * @author Stephane Micheloud
+ * @version 1.0
+ */
+@remote
+trait RemoteDoubleRef {
+ def elem_=(value: Double)
+ def elem: Double
+}
+
+/**
+ * The class <code>RemoteDoubleRefImpl</code> implements a remote (global)
+ * double reference by inheriting from the class
+ * <code>UnicastRemoteObject</code>.
+ *
+ * In particular, it forwards method invocations to the <code>elem</code>
+ * accessors of class <code>runtime.DoubleRef</code> and implements the
+ * <code>java.rmi.server.Unreferenced</code> 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 Remote<code>RemoteFloatRef</code> 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 <code>RemoteFloatRefImpl</code> implements a remote (global)
+ * float reference by inheriting from the class
+ * <code>UnicastRemoteObject</code>.
+ *
+ * In particular, it forwards method invocations to the <code>elem</code>
+ * accessors of class <code>runtime.FloatRef</code> and implements the
+ * <code>java.rmi.server.Unreferenced</code> 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 Remote<code>RemoteIntRef</code> 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 <code>RemoteIntRefImpl</code> implements a remote (global)
+ * integer reference by inheriting from the class
+ * <code>UnicastRemoteObject</code>.
+ *
+ * In particular, it forwards method invocations to the <code>elem</code>
+ * accessors of class <code>runtime.IntRef</code> and implements the
+ * <code>java.rmi.server.Unreferenced</code> 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 Remote<code>RemoteLongRef</code> 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 <code>RemoteLongRefImpl</code> implements a remote (global)
+ * long integer reference by inheriting from the class
+ * <code>UnicastRemoteObject</code>.
+ *
+ * In particular, it forwards method invocations to the <code>elem</code>
+ * accessors of class <code>runtime.LongRef</code> and implements the
+ * <code>java.rmi.server.Unreferenced</code> 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 Remote<code>RemoteObjectRef</code> 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 <code>RemoteObjectRefImpl</code> implements a remote (global)
+ * object reference by inheriting from the class
+ * <code>UnicastRemoteObject</code>.
+ *
+ * In particular, it forwards method invocations to the <code>elem</code>
+ * accessors of class <code>runtime.ObjectRef</code> and implements the
+ * <code>java.rmi.server.Unreferenced</code> 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 Remote<code>RemoteShortRef</code> 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 <code>RemoteShortRefImpl</code> implements a remote (global)
+ * short integer reference by inheriting from the class
+ * <code>UnicastRemoteObject</code>.
+ *
+ * In particular, it forwards method invocations to the <code>elem</code>
+ * accessors of class <code>runtime.ShortRef</code> and implements the
+ * <code>java.rmi.server.Unreferenced</code> 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]
+
+ /** <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>