summaryrefslogtreecommitdiff
path: root/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env')
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala200
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala67
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala306
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala126
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala466
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala63
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala10
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala10
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala96
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala303
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala173
-rw-r--r--examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala42
12 files changed, 0 insertions, 1862 deletions
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala
deleted file mode 100644
index e0aa557..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/ExternalJSEnv.scala
+++ /dev/null
@@ -1,200 +0,0 @@
-package scala.scalajs.sbtplugin.env
-
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.classpath._
-import scala.scalajs.tools.env._
-import scala.scalajs.tools.logging._
-
-import scala.scalajs.sbtplugin.JSUtils._
-
-import java.io.{ Console => _, _ }
-import scala.io.Source
-
-import scala.concurrent.{Future, Promise}
-import scala.util.Try
-
-abstract class ExternalJSEnv(
- final protected val additionalArgs: Seq[String],
- final protected val additionalEnv: Map[String, String]) extends AsyncJSEnv {
-
- /** Printable name of this VM */
- protected def vmName: String
-
- /** Command to execute (on shell) for this VM */
- protected def executable: String
-
- protected class AbstractExtRunner(protected val classpath: CompleteClasspath,
- protected val code: VirtualJSFile, protected val logger: Logger,
- protected val console: JSConsole) {
-
- /** JS files used to setup VM */
- protected def initFiles(): Seq[VirtualJSFile] = Nil
-
- /** Sends required data to VM Stdin (can throw) */
- protected def sendVMStdin(out: OutputStream): Unit = {}
-
- /** VM arguments excluding executable. Override to adapt.
- * Overrider is responsible to add additionalArgs.
- */
- protected def getVMArgs(): Seq[String] = additionalArgs
-
- /** VM environment. Override to adapt.
- *
- * Default is `sys.env` and [[additionalEnv]]
- */
- protected def getVMEnv(): Map[String, String] =
- sys.env ++ additionalEnv
-
- /** Get files that are a library (i.e. that do not run anything) */
- protected def getLibJSFiles(): Seq[VirtualJSFile] =
- initFiles() ++ classpath.allCode
-
- /** Get all files that are passed to VM (libraries and code) */
- protected def getJSFiles(): Seq[VirtualJSFile] =
- getLibJSFiles() :+ code
-
- /** write a single JS file to a writer using an include fct if appropriate */
- protected def writeJSFile(file: VirtualJSFile, writer: Writer): Unit = {
- // The only platform-independent way to do this in JS is to dump the file.
- writer.write(file.content)
- writer.write('\n')
- }
-
- /** Pipe stdin and stdout from/to VM */
- final protected def pipeVMData(vmInst: Process): Unit = {
- // Send stdin to VM.
- val out = vmInst.getOutputStream()
- try { sendVMStdin(out) }
- finally { out.close() }
-
- // Pipe stdout to console
- pipeToConsole(vmInst.getInputStream(), console)
-
- // We are probably done (stdin is closed). Report any errors
- val errSrc = Source.fromInputStream(vmInst.getErrorStream(), "UTF-8")
- try { errSrc.getLines.foreach(err => logger.error(err)) }
- finally { errSrc.close }
- }
-
- /** Wait for the VM to terminate, verify exit code */
- final protected def waitForVM(vmInst: Process): Unit = {
- // Make sure we are done.
- vmInst.waitFor()
-
- // Get return value and return
- val retVal = vmInst.exitValue
- if (retVal != 0)
- sys.error(s"$vmName exited with code $retVal")
- }
-
- protected def startVM(): Process = {
- val vmArgs = getVMArgs()
- val vmEnv = getVMEnv()
-
- val allArgs = executable +: vmArgs
- val pBuilder = new ProcessBuilder(allArgs: _*)
-
- pBuilder.environment().clear()
- for ((name, value) <- vmEnv)
- pBuilder.environment().put(name, value)
-
- pBuilder.start()
- }
-
- /** send a bunch of JS files to an output stream */
- final protected def sendJS(files: Seq[VirtualJSFile],
- out: OutputStream): Unit = {
- val writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"))
- try sendJS(files, writer)
- finally writer.close()
- }
-
- /** send a bunch of JS files to a writer */
- final protected def sendJS(files: Seq[VirtualJSFile], out: Writer): Unit =
- files.foreach { writeJSFile(_, out) }
-
- /** pipe lines from input stream to JSConsole */
- final protected def pipeToConsole(in: InputStream, console: JSConsole) = {
- val source = Source.fromInputStream(in, "UTF-8")
- try { source.getLines.foreach(console.log _) }
- finally { source.close() }
- }
-
- }
-
- protected class ExtRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole
- ) extends AbstractExtRunner(classpath, code, logger, console)
- with JSRunner {
-
- def run(): Unit = {
- val vmInst = startVM()
-
- pipeVMData(vmInst)
- waitForVM(vmInst)
- }
- }
-
- protected class AsyncExtRunner(classpath: CompleteClasspath,
- code: VirtualJSFile, logger: Logger, console: JSConsole
- ) extends AbstractExtRunner(classpath, code, logger, console)
- with AsyncJSRunner {
-
- private[this] var vmInst: Process = null
- private[this] var ioThreadEx: Throwable = null
- private[this] val promise = Promise[Unit]
-
- private[this] val thread = new Thread {
- override def run(): Unit = {
- // This thread should not be interrupted, so it is safe to use Trys
- val pipeResult = Try(pipeVMData(vmInst))
- val vmComplete = Try(waitForVM(vmInst))
-
- // Store IO exception
- pipeResult recover {
- case e => ioThreadEx = e
- }
-
- // Chain Try's the other way: We want VM failure first, then IO failure
- promise.complete(pipeResult orElse vmComplete)
- }
- }
-
- def start(): Future[Unit] = {
- require(vmInst == null, "start() may only be called once")
- vmInst = startVM()
- thread.start()
- promise.future
- }
-
- def stop(): Unit = {
- require(vmInst != null, "start() must have been called")
- vmInst.destroy()
- }
-
- def isRunning(): Boolean = {
- require(vmInst != null, "start() must have been called")
- // Emulate JDK 8 Process.isAlive
- try {
- vmInst.exitValue()
- false
- } catch {
- case e: IllegalThreadStateException =>
- true
- }
- }
-
- def await(): Unit = {
- require(vmInst != null, "start() must have been called")
- thread.join()
- waitForVM(vmInst)
-
- // At this point, the VM itself didn't fail. We need to check if
- // anything bad happened while piping the data from the VM
-
- if (ioThreadEx != null)
- throw ioThreadEx
- }
- }
-
-}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala
deleted file mode 100644
index fca1c47..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/VirtualFileMaterializer.scala
+++ /dev/null
@@ -1,67 +0,0 @@
-package scala.scalajs.sbtplugin.env
-
-import scala.scalajs.tools.io.{IO => _, _}
-
-import sbt.IO
-
-import java.io.File
-
-/** A helper class to temporarily store virtual files to the filesystem.
- *
- * Can be used with tools that require real files.
- * @param singleDir if true, forces files to be copied into
- * [[cacheDir]]. Useful to setup include directories for
- * example.
- */
-final class VirtualFileMaterializer(singleDir: Boolean = false) {
-
- val cacheDir = {
- val dir = IO.createTemporaryDirectory
- dir.deleteOnExit()
- dir
- }
-
- /** Create a target file to write/copy to. Will also call
- * deleteOnExit on the file.
- */
- private def trgFile(name: String): File = {
- val f = new File(cacheDir, name)
- f.deleteOnExit()
- f
- }
-
- private def materializeFileVF(vf: FileVirtualFile): File = {
- if (!singleDir) vf.file
- else {
- val trg = trgFile(vf.name)
- IO.copyFile(vf.file, trg)
- trg
- }
- }
-
- def materialize(vf: VirtualTextFile): File = vf match {
- case vf: FileVirtualFile => materializeFileVF(vf)
- case _ =>
- val trg = trgFile(vf.name)
- IO.write(trg, vf.content)
- trg
- }
-
- def materialize(vf: VirtualBinaryFile): File = vf match {
- case vf: FileVirtualFile => materializeFileVF(vf)
- case _ =>
- val trg = trgFile(vf.name)
- IO.write(trg, vf.content)
- trg
- }
-
- /** Removes the cache directory. Any operation on this
- * VirtualFileMaterializer is invalid after [[close]] has been
- * called.
- */
- def close(): Unit = {
- cacheDir.listFiles().foreach(_.delete)
- cacheDir.delete()
- }
-
-}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala
deleted file mode 100644
index dfabe23..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/nodejs/NodeJSEnv.scala
+++ /dev/null
@@ -1,306 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.sbtplugin.env.nodejs
-
-import scala.scalajs.sbtplugin.env._
-import scala.scalajs.sbtplugin.JSUtils.toJSstr
-
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.classpath._
-import scala.scalajs.tools.env._
-import scala.scalajs.tools.jsdep._
-import scala.scalajs.tools.logging._
-
-import scala.scalajs.sbtplugin.JSUtils._
-
-import java.io.{ Console => _, _ }
-import java.net._
-
-import scala.io.Source
-
-class NodeJSEnv(
- nodejsPath: String = "node",
- addArgs: Seq[String] = Seq.empty,
- addEnv: Map[String, String] = Map.empty
-) extends ExternalJSEnv(addArgs, addEnv) with ComJSEnv {
-
- protected def vmName: String = "node.js"
- protected def executable: String = nodejsPath
-
- override def jsRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): JSRunner = {
- new NodeRunner(classpath, code, logger, console)
- }
-
- override def asyncRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): AsyncJSRunner = {
- new AsyncNodeRunner(classpath, code, logger, console)
- }
-
- override def comRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): ComJSRunner = {
- new ComNodeRunner(classpath, code, logger, console)
- }
-
- protected class NodeRunner(classpath: CompleteClasspath,
- code: VirtualJSFile, logger: Logger, console: JSConsole
- ) extends ExtRunner(classpath, code, logger, console)
- with AbstractNodeRunner
-
- protected class AsyncNodeRunner(classpath: CompleteClasspath,
- code: VirtualJSFile, logger: Logger, console: JSConsole
- ) extends AsyncExtRunner(classpath, code, logger, console)
- with AbstractNodeRunner
-
- protected class ComNodeRunner(classpath: CompleteClasspath,
- code: VirtualJSFile, logger: Logger, console: JSConsole
- ) extends AsyncNodeRunner(classpath, code, logger, console)
- with ComJSRunner {
-
- /** Retry-timeout to wait for the JS VM to connect */
- private final val acceptTimeout = 1000
-
- private[this] val serverSocket =
- new ServerSocket(0, 0, InetAddress.getByName(null)) // Loopback address
- private[this] var comSocket: Socket = _
- private[this] var jvm2js: DataOutputStream = _
- private[this] var js2jvm: DataInputStream = _
-
- private def comSetup = new MemVirtualJSFile("comSetup.js").withContent(
- s"""
- (function() {
- // The socket for communication
- var socket = null;
- // The callback where received messages go
- var recvCallback = null;
-
- // Buffers received data
- var inBuffer = new Buffer(0);
-
- function onData(data) {
- inBuffer = Buffer.concat([inBuffer, data]);
- tryReadMsg();
- }
-
- function tryReadMsg() {
- if (inBuffer.length < 4) return;
- var msgLen = inBuffer.readInt32BE(0);
- var byteLen = 4 + msgLen * 2;
-
- if (inBuffer.length < byteLen) return;
- var res = "";
-
- for (var i = 0; i < msgLen; ++i)
- res += String.fromCharCode(inBuffer.readInt16BE(4 + i * 2));
-
- inBuffer = inBuffer.slice(byteLen);
-
- recvCallback(res);
- }
-
- global.scalajsCom = {
- init: function(recvCB) {
- if (socket !== null) throw new Error("Com already open");
-
- var net = require('net');
- recvCallback = recvCB;
- socket = net.connect(${serverSocket.getLocalPort});
- socket.on('data', onData);
- },
- send: function(msg) {
- if (socket === null) throw new Error("Com not open");
-
- var len = msg.length;
- var buf = new Buffer(4 + len * 2);
- buf.writeInt32BE(len, 0);
- for (var i = 0; i < len; ++i)
- buf.writeInt16BE(msg.charCodeAt(i), 4 + i * 2);
- socket.write(buf);
- },
- close: function() {
- if (socket === null) throw new Error("Com not open");
- socket.end();
- }
- }
- }).call(this);
- """
- )
-
- def send(msg: String): Unit = {
- if (awaitConnection()) {
- jvm2js.writeInt(msg.length)
- jvm2js.writeChars(msg)
- jvm2js.flush()
- }
- }
-
- def receive(): String = {
- if (!awaitConnection())
- throw new ComJSEnv.ComClosedException
- try {
- val len = js2jvm.readInt()
- val carr = Array.fill(len)(js2jvm.readChar())
- String.valueOf(carr)
- } catch {
- case e: EOFException =>
- throw new ComJSEnv.ComClosedException
- }
- }
-
- def close(): Unit = {
- serverSocket.close()
- if (jvm2js != null)
- jvm2js.close()
- if (js2jvm != null)
- js2jvm.close()
- if (comSocket != null)
- comSocket.close()
- }
-
- override def stop(): Unit = {
- close()
- super.stop()
- }
-
- /** Waits until the JS VM has established a connection or terminates
- * @return true if the connection was established
- */
- private def awaitConnection(): Boolean = {
- serverSocket.setSoTimeout(acceptTimeout)
- while (comSocket == null && isRunning) {
- try {
- comSocket = serverSocket.accept()
- jvm2js = new DataOutputStream(
- new BufferedOutputStream(comSocket.getOutputStream()))
- js2jvm = new DataInputStream(
- new BufferedInputStream(comSocket.getInputStream()))
- } catch {
- case to: SocketTimeoutException =>
- }
- }
-
- comSocket != null
- }
-
- override protected def initFiles(): Seq[VirtualJSFile] =
- super.initFiles :+ comSetup
-
- override protected def finalize(): Unit = close()
- }
-
- protected trait AbstractNodeRunner extends AbstractExtRunner {
-
- protected[this] val libCache = new VirtualFileMaterializer(true)
-
- /** File(s) to automatically install source-map-support.
- * Is used by [[initFiles]], override to change/disable.
- */
- protected def installSourceMap(): Seq[VirtualJSFile] = Seq(
- new MemVirtualJSFile("sourceMapSupport.js").withContent(
- """
- try {
- require('source-map-support').install();
- } catch (e) {}
- """
- )
- )
-
- /** File(s) to hack console.log to prevent if from changing `%%` to `%`.
- * Is used by [[initFiles]], override to change/disable.
- */
- protected def fixPercentConsole(): Seq[VirtualJSFile] = Seq(
- new MemVirtualJSFile("nodeConsoleHack.js").withContent(
- """
- // Hack console log to duplicate double % signs
- (function() {
- var oldLog = console.log;
- var newLog = function() {
- var args = arguments;
- if (args.length >= 1 && args[0] !== void 0 && args[0] !== null) {
- args[0] = args[0].toString().replace(/%/g, "%%");
- }
- oldLog.apply(console, args);
- };
- console.log = newLog;
- })();
- """
- )
- )
-
- /** File(s) to define `__ScalaJSEnv`. Defines `exitFunction`.
- * Is used by [[initFiles]], override to change/disable.
- */
- protected def runtimeEnv(): Seq[VirtualJSFile] = Seq(
- new MemVirtualJSFile("scalaJSEnvInfo.js").withContent(
- """
- __ScalaJSEnv = {
- exitFunction: function(status) { process.exit(status); }
- };
- """
- )
- )
-
- /** Concatenates results from [[installSourceMap]], [[fixPercentConsole]] and
- * [[runtimeEnv]] (in this order).
- */
- override protected def initFiles(): Seq[VirtualJSFile] =
- installSourceMap() ++ fixPercentConsole() ++ runtimeEnv()
-
- /** Libraries are loaded via require in Node.js */
- override protected def getLibJSFiles(): Seq[VirtualJSFile] = {
- initFiles() ++
- classpath.jsLibs.map(requireLibrary) :+
- classpath.scalaJSCode
- }
-
- /** Rewrites a library virtual file to a require statement if possible */
- protected def requireLibrary(dep: ResolvedJSDependency): VirtualJSFile = {
- dep.info.commonJSName.fold(dep.lib) { varname =>
- val fname = dep.lib.name
- libCache.materialize(dep.lib)
- new MemVirtualJSFile(s"require-$fname").withContent(
- s"""$varname = require(${toJSstr(fname)});"""
- )
- }
- }
-
- // Send code to Stdin
- override protected def sendVMStdin(out: OutputStream): Unit = {
- sendJS(getJSFiles(), out)
- }
-
- /** write a single JS file to a writer using an include fct if appropriate
- * uses `require` if the file exists on the filesystem
- */
- override protected def writeJSFile(file: VirtualJSFile,
- writer: Writer): Unit = {
- file match {
- case file: FileVirtualJSFile =>
- val fname = toJSstr(file.file.getAbsolutePath)
- writer.write(s"require($fname);\n")
- case _ =>
- super.writeJSFile(file, writer)
- }
- }
-
- // Node.js specific (system) environment
- override protected def getVMEnv(): Map[String, String] = {
- val baseNodePath = sys.env.get("NODE_PATH").filter(_.nonEmpty)
- val nodePath = libCache.cacheDir.getAbsolutePath +
- baseNodePath.fold("")(p => File.pathSeparator + p)
-
- sys.env ++ Seq(
- "NODE_MODULE_CONTEXTS" -> "0",
- "NODE_PATH" -> nodePath
- ) ++ additionalEnv
- }
- }
-
-}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala
deleted file mode 100644
index 3dec79c..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/JettyWebsocketManager.scala
+++ /dev/null
@@ -1,126 +0,0 @@
-package scala.scalajs.sbtplugin.env.phantomjs
-
-import javax.servlet.http.HttpServletRequest
-
-import org.eclipse.jetty.server.Server
-import org.eclipse.jetty.server.nio.SelectChannelConnector
-import org.eclipse.jetty.websocket.{WebSocket, WebSocketHandler}
-import org.eclipse.jetty.util.component.{LifeCycle, AbstractLifeCycle}
-import org.eclipse.jetty.util.log
-
-private[phantomjs] final class JettyWebsocketManager(
- wsListener: WebsocketListener) extends WebsocketManager { thisMgr =>
-
- private[this] var webSocketConn: WebSocket.Connection = null
- private[this] var closed = false
-
- // We can just set the logger here, since we are supposed to be protected by
- // the private ClassLoader that loads us reflectively.
- log.Log.setLog(new WSLogger("root"))
-
- private[this] val connector = new SelectChannelConnector
-
- connector.setHost("localhost")
- connector.setPort(0)
-
- private[this] val server = new Server()
-
- server.addConnector(connector)
- server.setHandler(new WebSocketHandler {
- // Support Hixie 76 for Phantom.js
- getWebSocketFactory().setMinVersion(-1)
-
- override def doWebSocketConnect(
- request: HttpServletRequest, protocol: String): WebSocket =
- new ComWebSocketListener
- })
-
- server.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener {
- override def lifeCycleStarted(event: LifeCycle): Unit = {
- if (event.isRunning())
- wsListener.onRunning()
- }
- })
-
- private class ComWebSocketListener extends WebSocket.OnTextMessage {
- override def onOpen(connection: WebSocket.Connection): Unit = {
- thisMgr.synchronized {
- if (isConnected)
- throw new IllegalStateException("Client connected twice")
- webSocketConn = connection
- }
- wsListener.onOpen()
- }
-
- override def onClose(statusCode: Int, reason: String): Unit = {
- thisMgr.synchronized {
- webSocketConn = null
- closed = true
- }
- wsListener.onClose()
- server.stop()
-
- if (statusCode != 1000) {
- throw new Exception("Abnormal closing of connection. " +
- s"Code: $statusCode, Reason: $reason")
- }
- }
-
- override def onMessage(message: String): Unit =
- wsListener.onMessage(message)
- }
-
- private class WSLogger(fullName: String) extends log.AbstractLogger {
- private[this] var debugEnabled = false
-
- def debug(msg: String, args: Object*): Unit =
- if (debugEnabled) log("DEBUG", msg, args)
-
- def debug(msg: String, thrown: Throwable): Unit =
- if (debugEnabled) log("DEBUG", msg, thrown)
-
- def debug(thrown: Throwable): Unit =
- if (debugEnabled) log("DEBUG", thrown)
-
- def getName(): String = fullName
-
- def ignore(ignored: Throwable): Unit = ()
-
- def info(msg: String, args: Object*): Unit = log("INFO", msg, args)
- def info(msg: String, thrown: Throwable): Unit = log("INFO", msg, thrown)
- def info(thrown: Throwable): Unit = log("INFO", thrown)
-
- def warn(msg: String, args: Object*): Unit = log("WARN", msg, args)
- def warn(msg: String, thrown: Throwable): Unit = log("WARN", msg, thrown)
- def warn(thrown: Throwable): Unit = log("WARN", thrown)
-
- def isDebugEnabled(): Boolean = debugEnabled
- def setDebugEnabled(enabled: Boolean): Unit = debugEnabled = enabled
-
- private def log(lvl: String, msg: String, args: Object*): Unit =
- wsListener.log(s"$lvl: $msg " + args.mkString(", "))
-
- private def log(lvl: String, msg: String, thrown: Throwable): Unit =
- wsListener.log(s"$lvl: $msg $thrown\n{$thrown.getStackStrace}")
-
- private def log(lvl: String, thrown: Throwable): Unit =
- wsListener.log(s"$lvl: $thrown\n{$thrown.getStackStrace}")
-
- protected def newLogger(fullName: String) = new WSLogger(fullName)
- }
-
- def start(): Unit = server.start()
-
- def stop(): Unit = server.stop()
-
- def isConnected: Boolean = webSocketConn != null && !closed
- def isClosed: Boolean = closed
-
- def localPort: Int = connector.getLocalPort()
-
- def sendMessage(msg: String) = synchronized {
- if (webSocketConn != null)
- webSocketConn.sendMessage(msg)
- }
-
-}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala
deleted file mode 100644
index 7bb47d2..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJSEnv.scala
+++ /dev/null
@@ -1,466 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.sbtplugin.env.phantomjs
-
-import scala.scalajs.sbtplugin.env._
-
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.classpath._
-import scala.scalajs.tools.env._
-import scala.scalajs.tools.logging._
-
-import scala.scalajs.sbtplugin.JSUtils._
-
-import java.io.{ Console => _, _ }
-import java.net._
-
-import scala.io.Source
-import scala.collection.mutable
-import scala.annotation.tailrec
-
-class PhantomJSEnv(
- phantomjsPath: String = "phantomjs",
- addArgs: Seq[String] = Seq.empty,
- addEnv: Map[String, String] = Map.empty,
- val autoExit: Boolean = true,
- jettyClassLoader: ClassLoader = getClass().getClassLoader()
-) extends ExternalJSEnv(addArgs, addEnv) with ComJSEnv {
-
- import PhantomJSEnv._
-
- protected def vmName: String = "PhantomJS"
- protected def executable: String = phantomjsPath
-
- override def jsRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): JSRunner = {
- new PhantomRunner(classpath, code, logger, console)
- }
-
- override def asyncRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): AsyncJSRunner = {
- new AsyncPhantomRunner(classpath, code, logger, console)
- }
-
- override def comRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): ComJSRunner = {
- new ComPhantomRunner(classpath, code, logger, console)
- }
-
- protected class PhantomRunner(classpath: CompleteClasspath,
- code: VirtualJSFile, logger: Logger, console: JSConsole
- ) extends ExtRunner(classpath, code, logger, console)
- with AbstractPhantomRunner
-
- protected class AsyncPhantomRunner(classpath: CompleteClasspath,
- code: VirtualJSFile, logger: Logger, console: JSConsole
- ) extends AsyncExtRunner(classpath, code, logger, console)
- with AbstractPhantomRunner
-
- protected class ComPhantomRunner(classpath: CompleteClasspath,
- code: VirtualJSFile, logger: Logger, console: JSConsole
- ) extends AsyncPhantomRunner(classpath, code, logger, console)
- with ComJSRunner with WebsocketListener {
-
- private def loadMgr() = {
- val clazz = jettyClassLoader.loadClass(
- "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager")
-
- val ctors = clazz.getConstructors()
- assert(ctors.length == 1, "JettyWebsocketManager may only have one ctor")
-
- val mgr = ctors.head.newInstance(this)
-
- mgr.asInstanceOf[WebsocketManager]
- }
-
- val mgr: WebsocketManager = loadMgr()
-
- def onRunning(): Unit = synchronized(notifyAll())
- def onOpen(): Unit = synchronized(notifyAll())
- def onClose(): Unit = synchronized(notifyAll())
-
- def onMessage(msg: String): Unit = synchronized {
- recvBuf.enqueue(msg)
- notifyAll()
- }
-
- def log(msg: String): Unit = logger.debug(s"PhantomJS WS Jetty: $msg")
-
- private[this] val recvBuf = mutable.Queue.empty[String]
-
- mgr.start()
-
- /** The websocket server starts asynchronously, but we need the port it is
- * running on. This method waits until the port is non-negative and
- * returns its value.
- */
- private def waitForPort(): Int = {
- while (mgr.localPort < 0)
- wait()
- mgr.localPort
- }
-
- private def comSetup = {
- def maybeExit(code: Int) =
- if (autoExit)
- s"window.callPhantom({ action: 'exit', returnValue: $code });"
- else
- ""
-
- val serverPort = waitForPort()
-
- val code = s"""
- |(function() {
- | var MaxPayloadSize = $MaxCharPayloadSize;
- |
- | // The socket for communication
- | var websocket = null;
- |
- | // Buffer for messages sent before socket is open
- | var outMsgBuf = null;
- |
- | function sendImpl(msg) {
- | var frags = (msg.length / MaxPayloadSize) | 0;
- |
- | for (var i = 0; i < frags; ++i) {
- | var payload = msg.substring(
- | i * MaxPayloadSize, (i + 1) * MaxPayloadSize);
- | websocket.send("1" + payload);
- | }
- |
- | websocket.send("0" + msg.substring(frags * MaxPayloadSize));
- | }
- |
- | function recvImpl(recvCB) {
- | var recvBuf = "";
- |
- | return function(evt) {
- | var newData = recvBuf + evt.data.substring(1);
- | if (evt.data.charAt(0) == "0") {
- | recvBuf = "";
- | recvCB(newData);
- | } else if (evt.data.charAt(0) == "1") {
- | recvBuf = newData;
- | } else {
- | throw new Error("Bad fragmentation flag in " + evt.data);
- | }
- | };
- | }
- |
- | window.scalajsCom = {
- | init: function(recvCB) {
- | if (websocket !== null) throw new Error("Com already open");
- |
- | outMsgBuf = [];
- |
- | websocket = new WebSocket("ws://localhost:$serverPort");
- |
- | websocket.onopen = function(evt) {
- | for (var i = 0; i < outMsgBuf.length; ++i)
- | sendImpl(outMsgBuf[i]);
- | outMsgBuf = null;
- | };
- | websocket.onclose = function(evt) {
- | websocket = null;
- | ${maybeExit(0)}
- | };
- | websocket.onmessage = recvImpl(recvCB);
- | websocket.onerror = function(evt) {
- | websocket = null;
- | throw new Error("Websocket failed: " + evt);
- | };
- |
- | // Take over responsibility to auto exit
- | window.callPhantom({
- | action: 'setAutoExit',
- | autoExit: false
- | });
- | },
- | send: function(msg) {
- | if (websocket === null)
- | return; // we are closed already. ignore message
- |
- | if (outMsgBuf !== null)
- | outMsgBuf.push(msg);
- | else
- | sendImpl(msg);
- | },
- | close: function() {
- | if (websocket === null)
- | return; // we are closed already. all is well.
- |
- | if (outMsgBuf !== null)
- | // Reschedule ourselves to give onopen a chance to kick in
- | window.setTimeout(window.scalajsCom.close, 10);
- | else
- | websocket.close();
- | }
- | }
- |}).call(this);""".stripMargin
-
- new MemVirtualJSFile("comSetup.js").withContent(code)
- }
-
- def send(msg: String): Unit = synchronized {
- if (awaitConnection()) {
- val fragParts = msg.length / MaxCharPayloadSize
-
- for (i <- 0 until fragParts) {
- val payload = msg.substring(
- i * MaxCharPayloadSize, (i + 1) * MaxCharPayloadSize)
- mgr.sendMessage("1" + payload)
- }
-
- mgr.sendMessage("0" + msg.substring(fragParts * MaxCharPayloadSize))
- }
- }
-
- def receive(): String = synchronized {
- if (recvBuf.isEmpty && !awaitConnection())
- throw new ComJSEnv.ComClosedException
-
- @tailrec
- def loop(acc: String): String = {
- val frag = receiveFrag()
- val newAcc = acc + frag.substring(1)
-
- if (frag(0) == '0')
- newAcc
- else if (frag(0) == '1')
- loop(newAcc)
- else
- throw new AssertionError("Bad fragmentation flag in " + frag)
- }
-
- loop("")
- }
-
- private def receiveFrag(): String = {
- while (recvBuf.isEmpty && !mgr.isClosed)
- wait()
-
- if (recvBuf.isEmpty)
- throw new ComJSEnv.ComClosedException
- else
- recvBuf.dequeue()
- }
-
- def close(): Unit = mgr.stop()
-
- override def stop(): Unit = {
- close()
- super.stop()
- }
-
- /** Waits until the JS VM has established a connection, or the VM
- * terminated. Returns true if a connection was established.
- */
- private def awaitConnection(): Boolean = {
- while (!mgr.isConnected && !mgr.isClosed && isRunning)
- wait(200) // We sleep-wait for isRunning
-
- mgr.isConnected
- }
-
- override protected def initFiles(): Seq[VirtualJSFile] =
- super.initFiles :+ comSetup
- }
-
- protected trait AbstractPhantomRunner extends AbstractExtRunner {
-
- override protected def getVMArgs() =
- // Add launcher file to arguments
- additionalArgs :+ createTmpLauncherFile().getAbsolutePath
-
- /** In phantom.js, we include JS using HTML */
- override protected def writeJSFile(file: VirtualJSFile, writer: Writer) = {
- file match {
- case file: FileVirtualJSFile =>
- val fname = htmlEscape(file.file.getAbsolutePath)
- writer.write(
- s"""<script type="text/javascript" src="$fname"></script>""" + "\n")
- case _ =>
- writer.write("""<script type="text/javascript">""" + "\n")
- writer.write(s"// Virtual File: ${file.path}\n")
- writer.write(file.content)
- writer.write("</script>\n")
- }
- }
-
- /**
- * PhantomJS doesn't support Function.prototype.bind. We polyfill it.
- * https://github.com/ariya/phantomjs/issues/10522
- */
- override protected def initFiles(): Seq[VirtualJSFile] = Seq(
- new MemVirtualJSFile("bindPolyfill.js").withContent(
- """
- |// Polyfill for Function.bind from Facebook react:
- |// https://github.com/facebook/react/blob/3dc10749080a460e48bee46d769763ec7191ac76/src/test/phantomjs-shims.js
- |// Originally licensed under Apache 2.0
- |(function() {
- |
- | var Ap = Array.prototype;
- | var slice = Ap.slice;
- | var Fp = Function.prototype;
- |
- | if (!Fp.bind) {
- | // PhantomJS doesn't support Function.prototype.bind natively, so
- | // polyfill it whenever this module is required.
- | Fp.bind = function(context) {
- | var func = this;
- | var args = slice.call(arguments, 1);
- |
- | function bound() {
- | var invokedAsConstructor = func.prototype && (this instanceof func);
- | return func.apply(
- | // Ignore the context parameter when invoking the bound function
- | // as a constructor. Note that this includes not only constructor
- | // invocations using the new keyword but also calls to base class
- | // constructors such as BaseClass.call(this, ...) or super(...).
- | !invokedAsConstructor && context || this,
- | args.concat(slice.call(arguments))
- | );
- | }
- |
- | // The bound function must share the .prototype of the unbound
- | // function so that any object created by one constructor will count
- | // as an instance of both constructors.
- | bound.prototype = func.prototype;
- |
- | return bound;
- | };
- | }
- |
- |})();
- |""".stripMargin
- ),
- new MemVirtualJSFile("scalaJSEnvInfo.js").withContent(
- """
- |__ScalaJSEnv = {
- | exitFunction: function(status) {
- | window.callPhantom({
- | action: 'exit',
- | returnValue: status | 0
- | });
- | }
- |};
- """.stripMargin
- )
- )
-
- protected def writeWebpageLauncher(out: Writer): Unit = {
- out.write("<html>\n<head>\n<title>Phantom.js Launcher</title>\n")
- sendJS(getLibJSFiles(), out)
- writeCodeLauncher(code, out)
- out.write("</head>\n<body></body>\n</html>\n")
- }
-
- protected def createTmpLauncherFile(): File = {
- val webF = createTmpWebpage()
-
- val launcherTmpF = File.createTempFile("phantomjs-launcher", ".js")
- launcherTmpF.deleteOnExit()
-
- val out = new FileWriter(launcherTmpF)
-
- try {
- out.write(
- s"""// Scala.js Phantom.js launcher
- |var page = require('webpage').create();
- |var url = ${toJSstr(webF.getAbsolutePath)};
- |var autoExit = $autoExit;
- |page.onConsoleMessage = function(msg) {
- | console.log(msg);
- |};
- |page.onError = function(msg, trace) {
- | console.error(msg);
- | if (trace && trace.length) {
- | console.error('');
- | trace.forEach(function(t) {
- | console.error(' ' + t.file + ':' + t.line + (t.function ? ' (in function "' + t.function +'")' : ''));
- | });
- | }
- |
- | phantom.exit(2);
- |};
- |page.onCallback = function(data) {
- | if (!data.action) {
- | console.error('Called callback without action');
- | phantom.exit(3);
- | } else if (data.action === 'exit') {
- | phantom.exit(data.returnValue || 0);
- | } else if (data.action === 'setAutoExit') {
- | if (typeof(data.autoExit) === 'boolean')
- | autoExit = data.autoExit;
- | else
- | autoExit = true;
- | } else {
- | console.error('Unknown callback action ' + data.action);
- | phantom.exit(4);
- | }
- |};
- |page.open(url, function (status) {
- | if (autoExit || status !== 'success')
- | phantom.exit(status !== 'success');
- |});
- |""".stripMargin)
- } finally {
- out.close()
- }
-
- logger.debug(
- "PhantomJS using launcher at: " + launcherTmpF.getAbsolutePath())
-
- launcherTmpF
- }
-
- protected def createTmpWebpage(): File = {
- val webTmpF = File.createTempFile("phantomjs-launcher-webpage", ".html")
- webTmpF.deleteOnExit()
-
- val out = new BufferedWriter(new FileWriter(webTmpF))
- try {
- writeWebpageLauncher(out)
- } finally {
- out.close()
- }
-
- logger.debug(
- "PhantomJS using webpage launcher at: " + webTmpF.getAbsolutePath())
-
- webTmpF
- }
-
- protected def writeCodeLauncher(code: VirtualJSFile, out: Writer): Unit = {
- out.write("""<script type="text/javascript">""" + "\n")
- out.write("// Phantom.js code launcher\n")
- out.write(s"// Origin: ${code.path}\n")
- out.write("window.addEventListener('load', function() {\n")
- out.write(code.content)
- out.write("}, false);\n")
- out.write("</script>\n")
- }
- }
-
- protected def htmlEscape(str: String): String = str.flatMap {
- case '<' => "&lt;"
- case '>' => "&gt;"
- case '"' => "&quot;"
- case '&' => "&amp;"
- case c => c :: Nil
- }
-
-}
-
-object PhantomJSEnv {
- private final val MaxByteMessageSize = 32768 // 32 KB
- private final val MaxCharMessageSize = MaxByteMessageSize / 2 // 2B per char
- private final val MaxCharPayloadSize = MaxCharMessageSize - 1 // frag flag
-}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala
deleted file mode 100644
index 02c229b..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/PhantomJettyClassLoader.scala
+++ /dev/null
@@ -1,63 +0,0 @@
-package scala.scalajs.sbtplugin.env.phantomjs
-
-import scala.scalajs.tools.io.IO
-
-/** A special [[ClassLoader]] to load the Jetty 8 dependency of [[PhantomJSEnv]]
- * in a private space.
- *
- * It loads everything that belongs to [[JettyWebsocketManager]] itself (while
- * retrieving the requested class file from its parent.
- * For all other classes, it first tries to load them from [[jettyLoader]],
- * which should only contain the Jetty 8 classpath.
- * If this fails, it delegates to its parent.
- *
- * The rationale is, that [[JettyWebsocketManager]] and its dependees can use
- * the classes on the Jetty 8 classpath, while they remain hidden from the rest
- * of the Java world. This allows to load another version of Jetty in the same
- * JVM for the rest of the project.
- */
-private[sbtplugin] class PhantomJettyClassLoader(jettyLoader: ClassLoader,
- parent: ClassLoader) extends ClassLoader(parent) {
-
- def this(loader: ClassLoader) =
- this(loader, ClassLoader.getSystemClassLoader())
-
- /** Classes needed to bridge private jetty classpath and public PhantomJS
- * Basically everything defined in JettyWebsocketManager.
- */
- private val bridgeClasses = Set(
- "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager",
- "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$WSLogger",
- "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$ComWebSocketListener",
- "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$$anon$1",
- "scala.scalajs.sbtplugin.env.phantomjs.JettyWebsocketManager$$anon$2"
- )
-
- override protected def loadClass(name: String, resolve: Boolean): Class[_] = {
- if (bridgeClasses.contains(name)) {
- // Load bridgeClasses manually since they must be associated to this
- // class loader, rather than the parent class loader in order to find the
- // jetty classes
-
- // First check if we have loaded it already
- Option(findLoadedClass(name)) getOrElse {
- val wsManager =
- parent.getResourceAsStream(name.replace('.', '/') + ".class")
-
- if (wsManager == null) {
- throw new ClassNotFoundException(name)
- } else {
- val buf = IO.readInputStreamToByteArray(wsManager)
- defineClass(name, buf, 0, buf.length)
- }
- }
- } else {
- try {
- jettyLoader.loadClass(name)
- } catch {
- case _: ClassNotFoundException =>
- super.loadClass(name, resolve)
- }
- }
- }
-}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala
deleted file mode 100644
index 4faac64..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketListener.scala
+++ /dev/null
@@ -1,10 +0,0 @@
-package scala.scalajs.sbtplugin.env.phantomjs
-
-private[phantomjs] trait WebsocketListener {
- def onRunning(): Unit
- def onOpen(): Unit
- def onClose(): Unit
- def onMessage(msg: String): Unit
-
- def log(msg: String): Unit
-}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala
deleted file mode 100644
index a466841..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/phantomjs/WebsocketManager.scala
+++ /dev/null
@@ -1,10 +0,0 @@
-package scala.scalajs.sbtplugin.env.phantomjs
-
-private[phantomjs] trait WebsocketManager {
- def start(): Unit
- def stop(): Unit
- def sendMessage(msg: String): Unit
- def localPort: Int
- def isConnected: Boolean
- def isClosed: Boolean
-}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala
deleted file mode 100644
index d4cdaee..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/LazyScalaJSScope.scala
+++ /dev/null
@@ -1,96 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.sbtplugin.env.rhino
-
-import scala.collection.mutable
-
-import org.mozilla.javascript.Scriptable
-
-/** A proxy for a ScalaJS "scope" field that loads scripts lazily
- *
- * E.g., ScalaJS.c, which is a scope with the Scala.js classes, can be
- * turned to a LazyScalaJSScope. Upon first access to a field of ScalaJS.c,
- * say ScalaJS.c.scala_Option, the script defining that particular
- * field will be loaded.
- * This is possible because the relative path to the script can be derived
- * from the name of the property being accessed.
- *
- * It is immensely useful, because it allows to load lazily only the scripts
- * that are actually needed.
- */
-class LazyScalaJSScope(
- coreLib: ScalaJSCoreLib,
- globalScope: Scriptable,
- base: Scriptable,
- isModule: Boolean = false,
- isTraitImpl: Boolean = false) extends Scriptable {
-
- private val fields = mutable.HashMap.empty[String, Any]
- private var prototype: Scriptable = _
- private var parentScope: Scriptable = _
-
- {
- // Pre-fill fields with the properties of `base`
- for (id <- base.getIds()) {
- (id.asInstanceOf[Any]: @unchecked) match {
- case name: String => put(name, this, base.get(name, base))
- case index: Int => put(index, this, base.get(index, base))
- }
- }
- }
-
- private def load(name: String): Unit =
- coreLib.load(globalScope, propNameToEncodedName(name))
-
- private def propNameToEncodedName(name: String): String = {
- if (isTraitImpl) name.split("__")(0)
- else if (isModule) name + "$"
- else name
- }
-
- override def getClassName() = "LazyScalaJSScope"
-
- override def get(name: String, start: Scriptable) = {
- fields.getOrElse(name, {
- load(name)
- fields.getOrElse(name, Scriptable.NOT_FOUND)
- }).asInstanceOf[AnyRef]
- }
- override def get(index: Int, start: Scriptable) =
- get(index.toString, start)
-
- override def has(name: String, start: Scriptable) =
- fields.contains(name)
- override def has(index: Int, start: Scriptable) =
- has(index.toString, start)
-
- override def put(name: String, start: Scriptable, value: Any) = {
- fields(name) = value
- }
- override def put(index: Int, start: Scriptable, value: Any) =
- put(index.toString, start, value)
-
- override def delete(name: String) = ()
- override def delete(index: Int) = ()
-
- override def getPrototype() = prototype
- override def setPrototype(value: Scriptable) = prototype = value
-
- override def getParentScope() = parentScope
- override def setParentScope(value: Scriptable) = parentScope = value
-
- override def getIds() = fields.keys.toArray
-
- override def getDefaultValue(hint: java.lang.Class[_]) = {
- base.getDefaultValue(hint)
- }
-
- override def hasInstance(instance: Scriptable) = false
-}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala
deleted file mode 100644
index cd35ff6..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/RhinoJSEnv.scala
+++ /dev/null
@@ -1,303 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.sbtplugin.env.rhino
-
-import scala.scalajs.tools.sem.Semantics
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.classpath._
-import scala.scalajs.tools.env._
-import scala.scalajs.tools.logging._
-
-import scala.io.Source
-
-import scala.collection.mutable
-
-import scala.concurrent.{Future, Promise, Await}
-import scala.concurrent.duration.Duration
-
-import org.mozilla.javascript._
-
-class RhinoJSEnv(semantics: Semantics,
- withDOM: Boolean = false) extends ComJSEnv {
-
- import RhinoJSEnv._
-
- /** Executes code in an environment where the Scala.js library is set up to
- * load its classes lazily.
- *
- * Other .js scripts in the inputs are executed eagerly before the provided
- * `code` is called.
- */
- override def jsRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): JSRunner = {
- new Runner(classpath, code, logger, console)
- }
-
- private class Runner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole) extends JSRunner {
- def run(): Unit = internalRunJS(classpath, code, logger, console, None)
- }
-
- override def asyncRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): AsyncJSRunner = {
- new AsyncRunner(classpath, code, logger, console)
- }
-
- private class AsyncRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole) extends AsyncJSRunner {
-
- private[this] val promise = Promise[Unit]
-
- private[this] val thread = new Thread {
- override def run(): Unit = {
- try {
- internalRunJS(classpath, code, logger, console, optChannel)
- promise.success(())
- } catch {
- case t: Throwable =>
- promise.failure(t)
- }
- }
- }
-
- def start(): Future[Unit] = {
- thread.start()
- promise.future
- }
-
- def stop(): Unit = thread.interrupt()
-
- def isRunning(): Boolean = !promise.isCompleted
-
- def await(): Unit = Await.result(promise.future, Duration.Inf)
-
- protected def optChannel(): Option[Channel] = None
- }
-
- override def comRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): ComJSRunner = {
- new ComRunner(classpath, code, logger, console)
- }
-
- private class ComRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole)
- extends AsyncRunner(classpath, code, logger, console) with ComJSRunner {
-
- private[this] val channel = new Channel
-
- override protected def optChannel(): Option[Channel] = Some(channel)
-
- def send(msg: String): Unit = {
- try {
- channel.sendToJS(msg)
- } catch {
- case _: ChannelClosedException =>
- throw new ComJSEnv.ComClosedException
- }
- }
-
- def receive(): String = {
- try {
- channel.recvJVM()
- } catch {
- case _: ChannelClosedException =>
- throw new ComJSEnv.ComClosedException
- }
- }
-
- def close(): Unit = channel.close()
-
- override def stop(): Unit = {
- close()
- super.stop()
- }
-
- }
-
- private def internalRunJS(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole, optChannel: Option[Channel]): Unit = {
-
- val context = Context.enter()
- try {
- val scope = context.initStandardObjects()
-
- if (withDOM) {
- // Fetch env.rhino.js from webjar
- val name = "env.rhino.js"
- val path = "/META-INF/resources/webjars/envjs/1.2/" + name
- val resource = getClass.getResource(path)
- assert(resource != null, s"need $name as resource")
-
- // Rhino can't optimize envjs
- context.setOptimizationLevel(-1)
-
- // Don't print envjs header
- scope.addFunction("print", args => ())
-
- // Pipe file to Rhino
- val reader = Source.fromURL(resource).bufferedReader
- context.evaluateReader(scope, reader, name, 1, null);
-
- // No need to actually define print here: It is captured by envjs to
- // implement console.log, which we'll override in the next statement
- }
-
- // Make sure Rhino does not do its magic for JVM top-level packages (#364)
- val PackagesObject =
- ScriptableObject.getProperty(scope, "Packages").asInstanceOf[Scriptable]
- val topLevelPackageIds = ScriptableObject.getPropertyIds(PackagesObject)
- for (id <- topLevelPackageIds) (id: Any) match {
- case name: String => ScriptableObject.deleteProperty(scope, name)
- case index: Int => ScriptableObject.deleteProperty(scope, index)
- case _ => // should not happen, I think, but with Rhino you never know
- }
-
- // Setup console.log
- val jsconsole = context.newObject(scope)
- jsconsole.addFunction("log", _.foreach(console.log _))
- ScriptableObject.putProperty(scope, "console", jsconsole)
-
- // Optionally setup scalaJSCom
- var recvCallback: Option[String => Unit] = None
- for (channel <- optChannel) {
- val comObj = context.newObject(scope)
-
- comObj.addFunction("send", s =>
- channel.sendToJVM(Context.toString(s(0))))
-
- comObj.addFunction("init", s => s(0) match {
- case f: Function =>
- val cb: String => Unit =
- msg => f.call(context, scope, scope, Array(msg))
- recvCallback = Some(cb)
- case _ =>
- sys.error("First argument to init must be a function")
- })
-
- comObj.addFunction("close", _ => {
- // Tell JVM side we won't send anything
- channel.close()
- // Internally register that we're done
- recvCallback = None
- })
-
- ScriptableObject.putProperty(scope, "scalajsCom", comObj)
- }
-
- try {
- // Make the classpath available. Either through lazy loading or by
- // simply inserting
- classpath match {
- case cp: IRClasspath =>
- // Setup lazy loading classpath and source mapper
- val optLoader = if (cp.scalaJSIR.nonEmpty) {
- val loader = new ScalaJSCoreLib(semantics, cp)
-
- // Setup sourceMapper
- val scalaJSenv = context.newObject(scope)
-
- scalaJSenv.addFunction("sourceMapper", args => {
- val trace = Context.toObject(args(0), scope)
- loader.mapStackTrace(trace, context, scope)
- })
-
- ScriptableObject.putProperty(scope, "__ScalaJSEnv", scalaJSenv)
-
- Some(loader)
- } else {
- None
- }
-
- // Load JS libraries
- cp.jsLibs.foreach(dep => context.evaluateFile(scope, dep.lib))
-
- optLoader.foreach(_.insertInto(context, scope))
- case cp =>
- cp.allCode.foreach(context.evaluateFile(scope, _))
- }
-
- context.evaluateFile(scope, code)
-
- // Callback the com channel if necessary (if comCallback = None, channel
- // wasn't initialized on the client)
- for ((channel, callback) <- optChannel zip recvCallback) {
- try {
- while (recvCallback.isDefined)
- callback(channel.recvJS())
- } catch {
- case _: ChannelClosedException =>
- // the JVM side closed the connection
- }
- }
-
- // Enusre the channel is closed to release JVM side
- optChannel.foreach(_.close)
-
- } catch {
- case e: RhinoException =>
- // Trace here, since we want to be in the context to trace.
- logger.trace(e)
- sys.error(s"Exception while running JS code: ${e.getMessage}")
- }
- } finally {
- Context.exit()
- }
- }
-
-}
-
-object RhinoJSEnv {
-
- /** Communication channel between the Rhino thread and the rest of the JVM */
- private class Channel {
- private[this] var _closed = false
- private[this] val js2jvm = mutable.Queue.empty[String]
- private[this] val jvm2js = mutable.Queue.empty[String]
-
- def sendToJS(msg: String): Unit = synchronized {
- jvm2js.enqueue(msg)
- notify()
- }
-
- def sendToJVM(msg: String): Unit = synchronized {
- js2jvm.enqueue(msg)
- notify()
- }
-
- def recvJVM(): String = synchronized {
- while (js2jvm.isEmpty && ensureOpen())
- wait()
-
- js2jvm.dequeue()
- }
-
- def recvJS(): String = synchronized {
- while (jvm2js.isEmpty && ensureOpen())
- wait()
-
- jvm2js.dequeue()
- }
-
- def close(): Unit = synchronized {
- _closed = true
- notify()
- }
-
- /** Throws if the channel is closed and returns true */
- private def ensureOpen(): Boolean = {
- if (_closed)
- throw new ChannelClosedException
- true
- }
- }
-
- private class ChannelClosedException extends Exception
-
-}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala
deleted file mode 100644
index e937e5b..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala
+++ /dev/null
@@ -1,173 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.sbtplugin.env.rhino
-
-import scala.collection.mutable
-
-import org.mozilla.javascript.{Context, Scriptable}
-
-import scala.scalajs.ir
-
-import scala.scalajs.tools.sem.Semantics
-import scala.scalajs.tools.javascript.{Printers, ScalaJSClassEmitter}
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.classpath._
-import scala.scalajs.tools.corelib._
-
-class ScalaJSCoreLib(semantics: Semantics, classpath: IRClasspath) {
- import ScalaJSCoreLib._
-
- private val (providers, exportedSymbols) = {
- val providers = mutable.Map.empty[String, VirtualScalaJSIRFile]
- val exportedSymbols = mutable.ListBuffer.empty[String]
-
- for (irFile <- classpath.scalaJSIR) {
- val info = irFile.roughInfo
- providers += info.encodedName -> irFile
- if (info.isExported)
- exportedSymbols += info.encodedName
- }
-
- (providers, exportedSymbols)
- }
-
- def insertInto(context: Context, scope: Scriptable) = {
- CoreJSLibs.libs(semantics).foreach(context.evaluateFile(scope, _))
- lazifyScalaJSFields(scope)
-
- // Make sure exported symbols are loaded
- val ScalaJS = Context.toObject(scope.get("ScalaJS", scope), scope)
- val c = Context.toObject(ScalaJS.get("c", ScalaJS), scope)
- for (encodedName <- exportedSymbols)
- c.get(encodedName, c)
- }
-
- /** Source maps the given stack trace (where possible) */
- def mapStackTrace(stackTrace: Scriptable,
- context: Context, scope: Scriptable): Scriptable = {
- val count = Context.toNumber(stackTrace.get("length", stackTrace)).toInt
-
- // Maps file -> max line (0-based)
- val neededMaps = mutable.Map.empty[String, Int]
-
- // Collect required line counts
- for (i <- 0 until count) {
- val elem = Context.toObject(stackTrace.get(i, stackTrace), scope)
- val fileName = Context.toString(elem.get("fileName", elem))
-
- if (fileName.endsWith(PseudoFileSuffix) &&
- providers.contains(fileName.stripSuffix(PseudoFileSuffix))) {
-
- val curMaxLine = neededMaps.getOrElse(fileName, -1)
- val reqLine = Context.toNumber(elem.get("lineNumber", elem)).toInt - 1
-
- if (reqLine > curMaxLine)
- neededMaps.put(fileName, reqLine)
- }
- }
-
- // Map required files
- val maps =
- for ((fileName, maxLine) <- neededMaps)
- yield (fileName, getSourceMapper(fileName, maxLine))
-
- // Create new stack trace to return
- val res = context.newArray(scope, count)
-
- for (i <- 0 until count) {
- val elem = Context.toObject(stackTrace.get(i, stackTrace), scope)
- val fileName = Context.toString(elem.get("fileName", elem))
- val line = Context.toNumber(elem.get("lineNumber", elem)).toInt - 1
-
- val pos = maps.get(fileName).fold(ir.Position.NoPosition)(_(line))
-
- val newElem =
- if (pos.isDefined) newPosElem(scope, context, elem, pos)
- else elem
-
- res.put(i, res, newElem)
- }
-
- res
- }
-
- private def getSourceMapper(fileName: String, untilLine: Int) = {
- val irFile = providers(fileName.stripSuffix(PseudoFileSuffix))
- val mapper = new Printers.ReverseSourceMapPrinter(untilLine)
- val classDef = irFile.tree
- val desugared = new ScalaJSClassEmitter(semantics).genClassDef(classDef)
- mapper.reverseSourceMap(desugared)
- mapper
- }
-
- private def newPosElem(scope: Scriptable, context: Context,
- origElem: Scriptable, pos: ir.Position): Scriptable = {
- assert(pos.isDefined)
-
- val elem = context.newObject(scope)
-
- elem.put("declaringClass", elem, origElem.get("declaringClass", origElem))
- elem.put("methodName", elem, origElem.get("methodName", origElem))
- elem.put("fileName", elem, pos.source.toString)
- elem.put("lineNumber", elem, pos.line + 1)
- elem.put("columnNumber", elem, pos.column + 1)
-
- elem
- }
-
- private val scalaJSLazyFields = Seq(
- Info("d"),
- Info("c"),
- Info("h"),
- Info("i", isTraitImpl = true),
- Info("n", isModule = true),
- Info("m", isModule = true),
- Info("is"),
- Info("as"),
- Info("isArrayOf"),
- Info("asArrayOf"))
-
- private def lazifyScalaJSFields(scope: Scriptable) = {
- val ScalaJS = Context.toObject(scope.get("ScalaJS", scope), scope)
-
- def makeLazyScalaJSScope(base: Scriptable, isModule: Boolean, isTraitImpl: Boolean) =
- new LazyScalaJSScope(this, scope, base, isModule, isTraitImpl)
-
- for (Info(name, isModule, isTraitImpl) <- scalaJSLazyFields) {
- val base = ScalaJS.get(name, ScalaJS).asInstanceOf[Scriptable]
- val lazified = makeLazyScalaJSScope(base, isModule, isTraitImpl)
- ScalaJS.put(name, ScalaJS, lazified)
- }
- }
-
- private[rhino] def load(scope: Scriptable, encodedName: String): Unit = {
- providers.get(encodedName) foreach { irFile =>
- val codeWriter = new java.io.StringWriter
- val printer = new Printers.JSTreePrinter(codeWriter)
- val classDef = irFile.tree
- val desugared = new ScalaJSClassEmitter(semantics).genClassDef(classDef)
- printer.printTopLevelTree(desugared)
- printer.complete()
- val ctx = Context.getCurrentContext()
- val fakeFileName = encodedName + PseudoFileSuffix
- ctx.evaluateString(scope, codeWriter.toString(),
- fakeFileName, 1, null)
- }
- }
-}
-
-object ScalaJSCoreLib {
- private case class Info(name: String,
- isModule: Boolean = false, isTraitImpl: Boolean = false)
-
- private val EncodedNameLine = raw""""encodedName": *"([^"]+)"""".r.unanchored
-
- private final val PseudoFileSuffix = ".sjsir"
-}
diff --git a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala
deleted file mode 100644
index 926fbb2..0000000
--- a/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/package.scala
+++ /dev/null
@@ -1,42 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.sbtplugin.env
-
-import org.mozilla.javascript._
-
-import scala.scalajs.tools.io._
-
-package object rhino {
-
- implicit class ContextOps(val self: Context) extends AnyVal {
- def evaluateFile(scope: Scriptable, file: VirtualJSFile,
- securityDomain: AnyRef = null): Any = {
- self.evaluateString(scope, file.content, file.path, 1, securityDomain)
- }
- }
-
- implicit class ScriptableObjectOps(val self: Scriptable) {
- def addFunction(name: String, function: Array[AnyRef] => Any) = {
- val rhinoFunction =
- new BaseFunction {
- ScriptRuntime.setFunctionProtoAndParent(this, self)
- override def call(context: Context, scope: Scriptable,
- thisObj: Scriptable, args: Array[AnyRef]): AnyRef = {
- function(args) match {
- case () => Undefined.instance
- case r => r.asInstanceOf[AnyRef]
- }
- }
- }
-
- ScriptableObject.putProperty(self, name, rhinoFunction)
- }
- }
-}