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) } }