summaryrefslogblamecommitdiff
path: root/examples/scala-js/partest/src/main/scala/scala/tools/nsc/MainGenericRunner.scala
blob: 68571421c19678ed32a0be0b9e304a033c8a3bba (plain) (tree)



























































































































































































































                                                                                                                  
package scala.tools.nsc

/* Super hacky overriding of the MainGenericRunner used by partest */

import scala.scalajs.ir

import scala.scalajs.tools.sem.Semantics
import scala.scalajs.tools.classpath._
import scala.scalajs.tools.classpath.builder._
import scala.scalajs.tools.logging._
import scala.scalajs.tools.io._
import scala.scalajs.tools.optimizer.ScalaJSOptimizer
import scala.scalajs.tools.optimizer.ScalaJSClosureOptimizer
import scala.scalajs.tools.optimizer.ParIncOptimizer
import scala.scalajs.tools.env.JSConsole

import scala.scalajs.sbtplugin.env.rhino.RhinoJSEnv
import scala.scalajs.sbtplugin.env.nodejs.NodeJSEnv
import scala.scalajs.sbtplugin.JSUtils._

import scala.tools.partest.scalajs.ScalaJSPartestOptions._

import java.io.File
import scala.io.Source

import Properties.{ versionString, copyrightString }
import GenericRunnerCommand._

class ScalaConsoleJSConsole extends JSConsole {
  def log(msg: Any) = scala.Console.out.println(msg.toString)
}

class MainGenericRunner {
  def errorFn(ex: Throwable): Boolean = {
    ex.printStackTrace()
    false
  }
  def errorFn(str: String): Boolean = {
    scala.Console.err println str
    false
  }

  val optMode = OptMode.fromId(sys.props("scalajs.partest.optMode"))

  def noWarnMissing = {
    import ScalaJSOptimizer._

    for {
      fname <- sys.props.get("scalajs.partest.noWarnFile").toList
      line  <- Source.fromFile(fname).getLines
      if !line.startsWith("#")
    } yield line.split('.') match {
      case Array(className) =>             NoWarnClass(className)
      case Array(className, methodName) => NoWarnMethod(className, methodName)
    }
  }

  def readSemantics() = {
    val opt = sys.props.get("scalajs.partest.compliantSems")
    opt.fold(Semantics.Defaults) { str =>
      val sems = str.split(',')
      Semantics.compliantTo(sems.toList)
    }
  }

  def process(args: Array[String]): Boolean = {
    val command = new GenericRunnerCommand(args.toList, (x: String) => errorFn(x))
    import command.{ settings, howToRun, thingToRun }

    if (!command.ok) return errorFn("\n" + command.shortUsageMsg)
    else if (settings.version) return errorFn("Scala code runner %s -- %s".format(versionString, copyrightString))
    else if (command.shouldStopWithInfo) return errorFn("shouldStopWithInfo")

    if (howToRun != AsObject)
      return errorFn("Scala.js runner can only run an object")

    // Load basic Scala.js classpath (used for running or further packaging)
    val usefulClasspathEntries = for {
      url <- settings.classpathURLs
      f = urlToFile(url)
      if (f.isDirectory || f.getName.startsWith("scalajs-library"))
    } yield f
    val classpath =
      PartialClasspathBuilder.build(usefulClasspathEntries).resolve()

    val logger    = new ScalaConsoleLogger(Level.Warn)
    val jsConsole = new ScalaConsoleJSConsole

    val mainObjName = ir.Definitions.encodeClassName(thingToRun)
    val baseRunner  = runnerJSFile(mainObjName, command.arguments)
    val semantics   = readSemantics()

    def fastOpted = fastOptimize(classpath, mainObjName, logger, semantics)
    def fullOpted = fullOptimize(classpath, mainObjName, logger,
      baseRunner, semantics.optimized)

    val runner = {
      if (optMode == FullOpt)
        fullOptRunner()
      else
        baseRunner
    }

    val env =
      if (optMode == NoOpt) new RhinoJSEnv(semantics)
      else new NodeJSEnv

    val runClasspath = optMode match {
      case NoOpt         => classpath
      case FastOpt       => fastOpted
      case FullOpt       => fullOpted
    }

    env.jsRunner(runClasspath, runner, logger, jsConsole).run()

    true
  }

  private def runnerJSFile(mainObj: String, args: List[String]) = {
    val jsObj = "ScalaJS.m." + mainObj
    val jsArgs = argArray(args)
    new MemVirtualJSFile("Generated launcher file").
      withContent(s"$jsObj().main__AT__V($jsArgs);")
  }

  /** constructs a scala.Array[String] with the given elements */
  private def argArray(args: List[String]) = {
    s"""ScalaJS.makeNativeArrayWrapper(
          ScalaJS.d.T.getArrayOf(),
          ${listToJS(args)})"""
  }

  private def fastOptimize(
      classpath: IRClasspath,
      mainObjName: String,
      logger: Logger,
      semantics: Semantics) = {
    import ScalaJSOptimizer._

    val optimizer = newScalaJSOptimizer(semantics)
    val output = WritableMemVirtualJSFile("partest fastOpt file")

    optimizer.optimizeCP(
        Inputs(classpath,
            manuallyReachable = fastOptReachable(mainObjName),
            noWarnMissing = noWarnMissing
        ),
        OutputConfig(
            output        = output,
            wantSourceMap = false,
            checkIR       = true
            ),
        logger)
  }

  private def fastOptReachable(mainObjName: String) = {
    import ScalaJSOptimizer._
    List(
      ReachObject(mainObjName),
      ReachMethod(mainObjName + '$', "main__AT__V", static = false)
    )
  }

  private def fullOptimize(
      classpath: IRClasspath,
      mainObjName: String,
      logger: Logger,
      runner: VirtualJSFile,
      semantics: Semantics) = {
    import ScalaJSClosureOptimizer._

    val fastOptimizer = newScalaJSOptimizer(semantics)
    val fullOptimizer = new ScalaJSClosureOptimizer(semantics)
    val output = WritableMemVirtualJSFile("partest fullOpt file")
    val exportFile = fullOptExportFile(runner)

    fullOptimizer.optimizeCP(fastOptimizer, Inputs(
      input = ScalaJSOptimizer.Inputs(
        classpath,
        manuallyReachable = fastOptReachable(mainObjName),
        noWarnMissing = noWarnMissing),
      additionalExports = exportFile :: Nil),
      OutputConfig(
        output,
        checkIR = true,
        wantSourceMap = false),
    logger)

  }

  private def newScalaJSOptimizer(semantics: Semantics) =
    new ScalaJSOptimizer(semantics, new ParIncOptimizer(_))

  /** generates an exporter statement for the google closure compiler that runs
   *  what the normal test would
   */
  private def fullOptExportFile(runnerFile: VirtualJSFile) = {
    new MemVirtualJSFile("partest fullOpt exports").withContent(
      s"""this["runFullOptPartest"] = function() { ${runnerFile.content} };"""
    )
  }

  private def fullOptRunner() = new MemVirtualJSFile("partest fullOpt runner").
    withContent("runFullOptPartest();")

  private def urlToFile(url: java.net.URL) = {
    try {
      new File(url.toURI())
    } catch {
      case e: java.net.URISyntaxException => new File(url.getPath())
    }
  }
}

object MainGenericRunner extends MainGenericRunner {
  def main(args: Array[String]) {
    if (!process(args))
      sys.exit(1)
  }
}