diff options
Diffstat (limited to 'examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala')
-rw-r--r-- | examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala | 173 |
1 files changed, 173 insertions, 0 deletions
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 new file mode 100644 index 0000000..e937e5b --- /dev/null +++ b/examples/scala-js/sbt-plugin/src/main/scala/scala/scalajs/sbtplugin/env/rhino/ScalaJSCoreLib.scala @@ -0,0 +1,173 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ 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" +} |