summaryrefslogtreecommitdiff
path: root/examples/scala-js/tools/shared/src/main/scala/scala/scalajs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/tools/shared/src/main/scala/scala/scalajs')
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/CompleteClasspath.scala35
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ComplianceRequirement.scala7
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/Exceptions.scala42
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/IRClasspath.scala69
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/LinkedClasspath.scala26
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/PartialClasspath.scala99
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ResolvedJSDependency.scala10
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractJarLibClasspathBuilder.scala53
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractPartialClasspathBuilder.scala47
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathContentHandler.scala25
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathElementsTraverser.scala38
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/DirTraverser.scala60
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/FileSystem.scala57
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/JarTraverser.scala85
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/corelib/CoreJSLibs.scala113
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSEnv.scala19
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSRunner.scala19
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSEnv.scala38
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSRunner.scala22
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ConsoleJSConsole.scala17
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSConsole.scala15
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSEnv.scala20
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSRunner.scala15
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/NullJSConsole.scala5
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/CacheUtils.scala52
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/IO.scala116
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/MemFiles.scala105
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/VirtualFiles.scala169
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala1525
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala116
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala420
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala569
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala50
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala194
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Exceptions.scala59
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/FlatJSDependency.scala17
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependency.scala66
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala130
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Origin.scala28
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/ResolutionInfo.scala21
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/AbstractJSONImpl.scala32
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONDeserializer.scala30
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjBuilder.scala20
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjExtractor.scala13
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONSerializer.scala32
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/package.scala26
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Level.scala24
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Logger.scala25
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/NullLogger.scala7
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/ScalaConsoleLogger.scala15
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/Analyzer.scala587
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala921
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IRChecker.scala854
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IncOptimizer.scala158
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/JSTreeBuilder.scala16
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala3572
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSOptimizer.scala552
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/CheckedBehavior.scala24
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/Semantics.scala97
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/JSFileBuilder.scala144
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapWriter.scala213
61 files changed, 0 insertions, 11935 deletions
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/CompleteClasspath.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/CompleteClasspath.scala
deleted file mode 100644
index 6646a7b..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/CompleteClasspath.scala
+++ /dev/null
@@ -1,35 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.classpath
-
-import scala.collection.immutable.Seq
-
-import scala.scalajs.tools.io.VirtualJSFile
-import scala.scalajs.tools.jsdep.ResolutionInfo
-
-/** A classpath where nothing is missing. Therefore:
- * - All JS libraries are resolved and ordered
- * - The CoreJSLibs are present
- * - Nothing can be added anymore
- */
-abstract class CompleteClasspath(
- /** Resolved JS libraries */
- val jsLibs: Seq[ResolvedJSDependency],
- val requiresDOM: Boolean,
- val version: Option[String]
-) {
-
- /** Fully linked Scala.js code */
- def scalaJSCode: VirtualJSFile
-
- /** All code in this complete classpath */
- final def allCode: Seq[VirtualJSFile] = jsLibs.map(_.lib) :+ scalaJSCode
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ComplianceRequirement.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ComplianceRequirement.scala
deleted file mode 100644
index f6ec36f..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ComplianceRequirement.scala
+++ /dev/null
@@ -1,7 +0,0 @@
-package scala.scalajs.tools.classpath
-
-import scala.scalajs.tools.jsdep.Origin
-
-/** Expresses a requirement for a given semantic to be compliant */
-final class ComplianceRequirement(
- val semantics: String, val origins: List[Origin])
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/Exceptions.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/Exceptions.scala
deleted file mode 100644
index 62bf75f..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/Exceptions.scala
+++ /dev/null
@@ -1,42 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.classpath
-
-import scala.scalajs.tools.jsdep.ResolutionInfo
-
-class MissingJSLibException(val dependencies: List[ResolutionInfo])
- extends Exception(MissingJSLibException.mkMsg(dependencies))
-
-object MissingJSLibException {
- private def mkMsg(deps: List[ResolutionInfo]): String = {
- val msg = new StringBuilder()
- msg.append("Missing dependencies: \n")
- for (d <- deps) {
- msg.append(s"- ${d.resourceName}")
- msg.append(s" originating from: ${d.origins.mkString(", ")}\n")
- }
- msg.toString()
- }
-}
-
-class BadComplianceException(val unmet: List[ComplianceRequirement])
- extends Exception(BadComplianceException.mkMsg(unmet))
-
-object BadComplianceException {
- private def mkMsg(unmets: List[ComplianceRequirement]): String = {
- val msg = new StringBuilder()
- msg.append("Unmet required semantic compliance(s): \n")
- for (unmet <- unmets) {
- msg.append(s"- ${unmet.semantics}")
- msg.append(s" originating from: ${unmet.origins.mkString(", ")}\n")
- }
- msg.toString
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/IRClasspath.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/IRClasspath.scala
deleted file mode 100644
index a92293b..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/IRClasspath.scala
+++ /dev/null
@@ -1,69 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.classpath
-
-import scala.collection.immutable.{Seq, Traversable}
-
-import scala.scalajs.tools.sem.Semantics
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.logging._
-import scala.scalajs.tools.optimizer.ScalaJSOptimizer
-import scala.scalajs.tools.jsdep.ResolutionInfo
-
-/** A [[CompleteClasspath]] that contains only IR as scalaJSCode */
-final class IRClasspath(
- /** The JS libraries the IR code depends on */
- jsLibs: Seq[ResolvedJSDependency],
- val requiredCompliance: Traversable[ComplianceRequirement],
- /** The IR itself. Ancestor count is used for later ordering */
- val scalaJSIR: Traversable[VirtualScalaJSIRFile],
- requiresDOM: Boolean,
- version: Option[String]
-) extends CompleteClasspath(jsLibs, requiresDOM, version) {
-
- /** Orders and optimizes the contained IR.
- *
- * Consider using [[ScalaJSOptimizer]] for a canonical way to do so. It
- * allows to persist the resulting file and create a source map, as well as
- * using non-default [[Semantics]].
- */
- override lazy val scalaJSCode: VirtualJSFile = {
- import ScalaJSOptimizer._
-
- val outName = "temporary-fastOpt.js"
-
- if (scalaJSIR.nonEmpty) {
- val semantics = Semantics.compliantTo(requiredCompliance.map(_.semantics))
- val output = WritableMemVirtualJSFile(outName)
- new ScalaJSOptimizer(semantics).optimizeCP(
- Inputs(this),
- OutputConfig(output),
- NullLogger)
- output
- } else {
- // We cannot run the optimizer without IR, because it will complain about
- // java.lang.Object missing. However, an empty JS file is perfectly valid
- // for no IR at all.
- VirtualJSFile.empty(outName)
- }
- }
-
- /** Checks whether the given semantics are compliant with the requirements of
- * this CompleteClasspath. Throws an exception otherwise.
- */
- final def checkCompliance(semantics: Semantics): Unit = {
- val unmet = requiredCompliance filterNot { compliance =>
- semantics.isCompliant(compliance.semantics)
- }
-
- if (unmet.nonEmpty)
- throw new BadComplianceException(unmet.toList)
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/LinkedClasspath.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/LinkedClasspath.scala
deleted file mode 100644
index 3ace785..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/LinkedClasspath.scala
+++ /dev/null
@@ -1,26 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.classpath
-
-import scala.scalajs.tools.io.VirtualJSFile
-import scala.scalajs.tools.jsdep.ResolutionInfo
-
-import scala.collection.immutable.Seq
-
-/** A [[CompleteClasspath]] that is fully linked (either with the
- * [[ScalaJSOptimizer]] or the Closure Optimizer. It contains only a single
- * file that is scalaJSCode.
- */
-final class LinkedClasspath(
- jsLibs: Seq[ResolvedJSDependency],
- val scalaJSCode: VirtualJSFile,
- requiresDOM: Boolean,
- version: Option[String]
-) extends CompleteClasspath(jsLibs, requiresDOM, version)
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/PartialClasspath.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/PartialClasspath.scala
deleted file mode 100644
index 949cd6e..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/PartialClasspath.scala
+++ /dev/null
@@ -1,99 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.classpath
-
-import scala.collection.immutable.{Seq, Traversable}
-
-import scala.scalajs.tools.jsdep._
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.corelib.CoreJSLibs
-
-/** A partial Scala.js classpath is a collection of:
- * - Scala.js binary files *.sjsir
- * - Native JavaScript libraries
- * - Description of dependencies on other JavaScript libraries
- *
- * PartialClasspaths can be combined (using [[merge]]) and eventually resolved
- * to a [[CompleteIRClasspath]]
- */
-final class PartialClasspath(
- /** Description of JS libraries the content of this classpath depends on */
- val dependencies: Traversable[JSDependencyManifest],
- /** JS libraries this partial classpath provides */
- val availableLibs: Map[String, VirtualJSFile],
- /** Scala.js IR contained in this PartialClasspath (unordered) */
- val scalaJSIR: Traversable[VirtualScalaJSIRFile],
- val version: Option[String]
-) {
- import PartialClasspath.DependencyFilter
-
- /** Merges another [[PartialClasspath]] with this one. This means:
- * - Concatenate/merge dependencies
- * - Merge availableLibs (libs in that shadow libs in this)
- * - Merge Scala.js IR
- */
- def merge(that: PartialClasspath): PartialClasspath = {
- new PartialClasspath(
- this.dependencies ++ that.dependencies,
- this.availableLibs ++ that.availableLibs,
- this.scalaJSIR ++ that.scalaJSIR,
- CacheUtils.joinVersions(this.version, that.version))
- }
-
- /** Construct a [[IRClasspath]] out of this [[PartialClasspath]] by
- * resolving library dependencies (and failing if they are not met)
- */
- def resolve(filter: DependencyFilter = identity): IRClasspath = {
- new IRClasspath(resolveDependencies(filter), mergeCompliance(), scalaJSIR,
- dependencies.exists(_.requiresDOM), version)
- }
-
- /** Constructs an ordered list of JS libraries to include. Fails if:
- * - Dependencies have cycles
- * - Not all dependencies are available
- */
- protected def resolveDependencies(
- filter: DependencyFilter): List[ResolvedJSDependency] = {
- val flatDeps = filter(dependencies.flatMap(_.flatten))
- val includeList = JSDependencyManifest.createIncludeList(flatDeps)
-
- val missingDeps = includeList.filterNot { info =>
- availableLibs.contains(info.resourceName)
- }
-
- if (missingDeps.nonEmpty)
- throw new MissingJSLibException(missingDeps)
-
- for (info <- includeList)
- yield new ResolvedJSDependency(availableLibs(info.resourceName), info)
- }
-
- protected def mergeCompliance(): Traversable[ComplianceRequirement] = {
- val flatTups = for {
- dependency <- dependencies
- semantics <- dependency.compliantSemantics
- } yield (semantics, dependency.origin)
-
- for {
- (semantics, tups) <- flatTups.groupBy(_._1)
- } yield new ComplianceRequirement(semantics, tups.map(_._2).toList)
- }
-
-}
-
-object PartialClasspath {
-
- type DependencyFilter =
- Traversable[FlatJSDependency] => Traversable[FlatJSDependency]
-
- /** Creates an empty PartialClasspath */
- def empty: PartialClasspath =
- new PartialClasspath(Nil, Map.empty, Nil, Some(""))
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ResolvedJSDependency.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ResolvedJSDependency.scala
deleted file mode 100644
index 12ae8dc..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/ResolvedJSDependency.scala
+++ /dev/null
@@ -1,10 +0,0 @@
-package scala.scalajs.tools.classpath
-
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.jsdep._
-
-/** A dependency on a native JavaScript library that has been successfully
- * resolved
- */
-final class ResolvedJSDependency(
- val lib: VirtualJSFile, val info: ResolutionInfo)
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractJarLibClasspathBuilder.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractJarLibClasspathBuilder.scala
deleted file mode 100644
index 77f8509..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractJarLibClasspathBuilder.scala
+++ /dev/null
@@ -1,53 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.classpath.builder
-
-import scala.collection.mutable
-
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.jsdep.JSDependencyManifest
-import scala.scalajs.tools.classpath._
-
-/** reads a ScalaJS library JAR into a CP
- * - IR files go to scalaJSCode
- * - JS files go to availableLibs
- * - Reads a potential top-level JS_DEPENDENCIES file
- */
-trait AbstractJarLibClasspathBuilder extends JarTraverser {
-
- private val irFiles = mutable.ListBuffer.empty[VirtualScalaJSIRFile]
- private val jsFiles = mutable.Map.empty[String, VirtualJSFile]
- private var dependency: Option[JSDependencyManifest] = None
-
- def build(jar: File): PartialClasspath = {
- val v = traverseJar(jar)
- new PartialClasspath(dependency.toList,
- jsFiles.toMap, irFiles.toList, Some(v))
- }
-
- override protected def handleIR(relPath: String,
- ir: => VirtualScalaJSIRFile): Unit = {
- // We don't need to implement shadowing here: We have only a single JAR
- irFiles += ir
- }
-
- override protected def handleJS(js: => VirtualJSFile): Unit = {
- val file = js
- if (!jsFiles.contains(file.name))
- jsFiles += file.name -> file
- }
-
- override protected def handleDepManifest(m: => JSDependencyManifest): Unit = {
- if (dependency.isDefined)
- sys.error("A JAR cannot have multiple JS dependency manifests")
- dependency = Some(m)
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractPartialClasspathBuilder.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractPartialClasspathBuilder.scala
deleted file mode 100644
index 9889f4c..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/AbstractPartialClasspathBuilder.scala
+++ /dev/null
@@ -1,47 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.classpath.builder
-
-import scala.scalajs.tools.jsdep.JSDependencyManifest
-import scala.scalajs.tools.classpath._
-import scala.scalajs.tools.io._
-
-import scala.collection.mutable
-import scala.collection.immutable.Seq
-
-trait AbstractPartialClasspathBuilder extends ClasspathContentHandler
- with ClasspathElementsTraverser {
-
- private val jsDepManifests = mutable.ListBuffer.empty[JSDependencyManifest]
- private val irFiles = mutable.Map.empty[String, VirtualScalaJSIRFile]
- private val otherJSFiles = mutable.Map.empty[String, VirtualJSFile]
-
- override protected def handleIR(relPath: String,
- ir: => VirtualScalaJSIRFile): Unit = {
- if (!irFiles.contains(relPath))
- irFiles += relPath -> ir
- }
-
- override protected def handleJS(js: => VirtualJSFile): Unit = {
- val file = js
- if (!otherJSFiles.contains(file.name))
- otherJSFiles += file.name -> file
- }
-
- override protected def handleDepManifest(m: => JSDependencyManifest): Unit = {
- jsDepManifests += m
- }
-
- def build(cp: Seq[File]): PartialClasspath = {
- val version = traverseClasspathElements(cp)
- new PartialClasspath(jsDepManifests.toList, otherJSFiles.toMap,
- irFiles.values.toList, Some(version))
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathContentHandler.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathContentHandler.scala
deleted file mode 100644
index 71106c2..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathContentHandler.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.classpath.builder
-
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.jsdep._
-import scala.scalajs.tools.classpath._
-
-import java.io._
-
-import scala.collection.immutable.Seq
-
-/** Base-trait used by traversers to handle content with callbacks */
-trait ClasspathContentHandler {
- protected def handleIR(relPath: String, ir: => VirtualScalaJSIRFile): Unit
- protected def handleJS(js: => VirtualJSFile): Unit
- protected def handleDepManifest(m: => JSDependencyManifest): Unit
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathElementsTraverser.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathElementsTraverser.scala
deleted file mode 100644
index 9403ce3..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/ClasspathElementsTraverser.scala
+++ /dev/null
@@ -1,38 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.classpath.builder
-
-import scala.scalajs.tools.io._
-
-/** A helper trait to traverse an arbitrary classpath element
- * (i.e. a JAR or a directory).
- */
-trait ClasspathElementsTraverser extends JarTraverser
- with DirTraverser
- with FileSystem {
-
- protected def traverseClasspathElements(cp: Seq[File]): String =
- CacheUtils.joinVersions(cp.map(readEntriesInClasspathElement _): _*)
-
- /** Adds the Scala.js classpath entries in a directory or jar.
- * Returns the accumulated version
- */
- private def readEntriesInClasspathElement(element: File): String = {
- if (!exists(element))
- getDummyVersion(element)
- else if (isDirectory(element))
- traverseDir(element)
- else if (isJARFile(element))
- traverseJar(element)
- else
- sys.error(s"$element (in classpath) exists and is neither JAR or directory")
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/DirTraverser.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/DirTraverser.scala
deleted file mode 100644
index 6609b29..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/DirTraverser.scala
+++ /dev/null
@@ -1,60 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.classpath.builder
-
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.jsdep.JSDependencyManifest
-
-import scala.collection.mutable
-
-trait DirTraverser extends ClasspathContentHandler with FileSystem {
-
- /** Traverses elements, returns a version string */
- protected def traverseDir(dir: File): String = {
- val versions = mutable.SortedSet.empty[String]
-
- recurseDir(dir, "", versions)
-
- // Construct version
- CacheUtils.joinVersions(versions.toSeq: _*)
- }
-
- /** Recursively adds the Scala.js classpath entries in a directory */
- private def recurseDir(dir: File, dirPath: String,
- versions: mutable.SortedSet[String]): Unit = {
- val files = listFiles(dir)
- for (file <- files) {
- val name = getName(file)
- if (isDirectory(file)) {
- recurseDir(file, dirPath + name + "/", versions)
- } else {
- val path = dirPath + name
- path match {
- case JSDependencyManifest.ManifestFileName =>
- versions += getGlobalVersion(file)
- val reader = toReader(file)
- try handleDepManifest(JSDependencyManifest.read(reader))
- finally reader.close()
-
- case _ if isJSFile(file) =>
- versions += getGlobalVersion(file)
- handleJS(toJSFile(file))
-
- case _ if isIRFile(file) =>
- versions += getGlobalVersion(file)
- handleIR(path, toIRFile(file))
-
- case _ => // ignore other files
- }
- }
- }
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/FileSystem.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/FileSystem.scala
deleted file mode 100644
index 99a8ca2..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/FileSystem.scala
+++ /dev/null
@@ -1,57 +0,0 @@
-package scala.scalajs.tools.classpath.builder
-
-import scala.scalajs.tools.io._
-
-import scala.collection.immutable.Traversable
-
-import java.io.{InputStream, Reader}
-
-/** Abstraction of a FileSystem, so classpath builders can be used with virtual
- * file systems
- */
-trait FileSystem {
-
- type File
-
- /** Dummy version constant to identify files for which a version can not be
- * found.
- * This constant should never collide with the result of getVersion.
- */
- val DummyVersion: String
-
- def isDirectory(f: File): Boolean
- def isFile(f: File): Boolean
- def isJSFile(f: File): Boolean
- def isIRFile(f: File): Boolean
- def isJARFile(f: File): Boolean
- def exists(f: File): Boolean
-
- def getName(f: File): String
- /** A string that uniquely identifies this file's location */
- def getAbsolutePath(f: File): String
- /** A string that identifies the version of a file: If it equals the version
- * of another file with the same absolute path, the two files must be equal.
- * This is usually the lastModified date, but ordering is not required
- */
- def getVersion(f: File): String
- /** A string that globally identifies the version of a file: If it equals the
- * global version of any other file, they must equal.
- */
- def getGlobalVersion(f: File): String =
- CacheUtils.joinVersions(getAbsolutePath(f), getVersion(f))
-
- /** A string that globally identifies a file for which a version can not be
- * found. Example: a file that does not exists.
- */
- def getDummyVersion(f: File): String =
- CacheUtils.joinVersions(getAbsolutePath(f), DummyVersion)
-
- /** List files in a directory */
- def listFiles(d: File): Traversable[File]
-
- def toJSFile(f: File): VirtualJSFile
- def toIRFile(f: File): VirtualScalaJSIRFile
- def toReader(f: File): Reader
- def toInputStream(f: File): InputStream
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/JarTraverser.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/JarTraverser.scala
deleted file mode 100644
index bbf5270..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/JarTraverser.scala
+++ /dev/null
@@ -1,85 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.classpath.builder
-
-import scala.collection.mutable
-import scala.annotation.tailrec
-
-import java.util.zip._
-import java.io.{InputStream, InputStreamReader, Reader}
-
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.jsdep.JSDependencyManifest
-
-trait JarTraverser extends ClasspathContentHandler with FileSystem {
-
- private val jsFiles = mutable.Map.empty[String, MemVirtualJSFile]
-
- /** Traverse a Jar and return a version */
- protected def traverseJar(jar: File): String = {
- val zipStream = new ZipInputStream(toInputStream(jar))
-
- try readEntries(zipStream, getAbsolutePath(jar))
- finally zipStream.close()
-
- for {
- (_, jsFile) <- jsFiles
- if jsFile.content != "" // drop if this is just a lone sourcemap
- } handleJS(jsFile)
-
- getGlobalVersion(jar)
- }
-
- private def getOrCreateJSFile(relPath: String, fullPath: String, fn: String) =
- jsFiles.getOrElseUpdate(relPath, new MemVirtualJSFile(fullPath) {
- override val name = fn
- })
-
- @tailrec
- private def readEntries(in: ZipInputStream, jarPath: String): Unit = {
- val entry = in.getNextEntry()
- if (entry != null) {
- readEntry(entry, in, jarPath)
- readEntries(in, jarPath)
- }
- }
-
- private def readEntry(entry: ZipEntry, in: InputStream, jarPath: String) = {
- val longName = entry.getName
- val shortName = VirtualFile.nameFromPath(longName)
- val fullPath = jarPath + "#" + longName
-
- def entryReader: Reader = new InputStreamReader(in, "UTF-8")
- def entryContent: String = IO.readInputStreamToString(in)
- def entryBinaryContent: Array[Byte] = IO.readInputStreamToByteArray(in)
- def entryVersion: Option[String] = Some(entry.getTime.toString)
-
- if (longName == JSDependencyManifest.ManifestFileName)
- handleDepManifest(JSDependencyManifest.read(entryReader))
- else if (longName.endsWith(".js")) {
- val relPath = longName
- getOrCreateJSFile(relPath, fullPath, shortName)
- .withContent(entryContent)
- .withVersion(entryVersion)
- } else if (longName.endsWith(".js.map")) {
- // assume the source map of a JS file
- val relPath = longName.dropRight(".map".length)
- getOrCreateJSFile(relPath, fullPath, shortName)
- .withSourceMap(Some(entryContent))
- } else if (longName.endsWith(".sjsir")) {
- val vf = new MemVirtualSerializedScalaJSIRFile(fullPath) {
- override val name = shortName
- }.withContent(entryBinaryContent)
- .withVersion(entryVersion)
-
- handleIR(longName, vf)
- }
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/corelib/CoreJSLibs.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/corelib/CoreJSLibs.scala
deleted file mode 100644
index ecbea1f..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/corelib/CoreJSLibs.scala
+++ /dev/null
@@ -1,113 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.corelib
-
-import java.net.URI
-
-import scala.scalajs.ir.ScalaJSVersions
-import scala.scalajs.tools.io._
-
-import scala.scalajs.tools.sem._
-
-import scala.collection.immutable.Seq
-import scala.collection.mutable
-
-object CoreJSLibs {
-
- private val cachedLibBySemantics =
- mutable.HashMap.empty[Semantics, VirtualJSFile]
-
- private val ScalaJSEnvLines =
- ScalaJSEnvHolder.scalajsenv.split("\n|\r\n?")
-
- private val gitHubBaseURI =
- new URI("https://raw.githubusercontent.com/scala-js/scala-js/")
-
- def libs(semantics: Semantics): Seq[VirtualJSFile] = synchronized {
- Seq(cachedLibBySemantics.getOrElseUpdate(semantics, makeLib(semantics)))
- }
-
- private def makeLib(semantics: Semantics): VirtualJSFile = {
- new ScalaJSEnvVirtualJSFile(makeContent(semantics))
- }
-
- private def makeContent(semantics: Semantics): String = {
- // This is a basic sort-of-C-style preprocessor
-
- def getOption(name: String): String = name match {
- case "asInstanceOfs" =>
- semantics.asInstanceOfs.toString()
- case "floats" =>
- if (semantics.strictFloats) "Strict"
- else "Loose"
- }
-
- var skipping = false
- var skipDepth = 0
- val lines = for (line <- ScalaJSEnvLines) yield {
- val includeThisLine = if (skipping) {
- if (line == "//!else" && skipDepth == 1) {
- skipping = false
- skipDepth = 0
- } else if (line == "//!endif") {
- skipDepth -= 1
- if (skipDepth == 0)
- skipping = false
- } else if (line.startsWith("//!if ")) {
- skipDepth += 1
- }
- false
- } else {
- if (line.startsWith("//!")) {
- if (line.startsWith("//!if ")) {
- val Array(_, option, op, value) = line.split(" ")
- val optionValue = getOption(option)
- val success = op match {
- case "==" => optionValue == value
- case "!=" => optionValue != value
- }
- if (!success) {
- skipping = true
- skipDepth = 1
- }
- } else if (line == "//!else") {
- skipping = true
- skipDepth = 1
- } else if (line == "//!endif") {
- // nothing to do
- } else {
- throw new MatchError(line)
- }
- false
- } else {
- true
- }
- }
- if (includeThisLine) line
- else "" // blank line preserves line numbers in source maps
- }
-
- lines.mkString("", "\n", "\n")
- }
-
- private class ScalaJSEnvVirtualJSFile(override val content: String) extends VirtualJSFile {
- override def path: String = "scalajsenv.js"
- override def version: Option[String] = Some("")
- override def exists: Boolean = true
-
- override def toURI: URI = {
- if (!ScalaJSVersions.currentIsSnapshot)
- gitHubBaseURI.resolve(s"v${ScalaJSVersions.current}/tools/$path")
- else
- super.toURI
- }
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSEnv.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSEnv.scala
deleted file mode 100644
index d439ae2..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSEnv.scala
+++ /dev/null
@@ -1,19 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.env
-
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.classpath._
-import scala.scalajs.tools.logging._
-
-trait AsyncJSEnv extends JSEnv {
- def asyncRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): AsyncJSRunner
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSRunner.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSRunner.scala
deleted file mode 100644
index 09e2dda..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/AsyncJSRunner.scala
+++ /dev/null
@@ -1,19 +0,0 @@
-package scala.scalajs.tools.env
-
-import scala.concurrent.Future
-
-trait AsyncJSRunner {
- /** Start the associated run and returns a Future that completes when the run
- * terminates.
- */
- def start(): Future[Unit]
-
- /** Abort the associated run */
- def stop(): Unit
-
- /** Checks whether this async runner is still running */
- def isRunning(): Boolean
-
- /** Await completion of the started Run. Throws if the run failed */
- def await(): Unit
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSEnv.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSEnv.scala
deleted file mode 100644
index 882e46a..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSEnv.scala
+++ /dev/null
@@ -1,38 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.env
-
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.classpath._
-import scala.scalajs.tools.logging._
-
-/** An [[AsyncJSEnv]] that provides communication to and from the JS VM.
- *
- * Inside the VM there is a global JavaScript object named `scalajsCom` that
- * can be used to control the message channel. It's operations are:
- * {{{
- * // initialize com (with callback)
- * scalajsCom.init(function(msg) { console.log("Received: " + msg); });
- *
- * // send a message to host system
- * scalajsCom.send("my message");
- *
- * // close com (releases callback, allowing VM to terminate)
- * scalajsCom.close();
- * }}}
- */
-trait ComJSEnv extends AsyncJSEnv {
- def comRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): ComJSRunner
-}
-
-object ComJSEnv {
- class ComClosedException extends Exception("JSCom has been closed")
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSRunner.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSRunner.scala
deleted file mode 100644
index 44302b8..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ComJSRunner.scala
+++ /dev/null
@@ -1,22 +0,0 @@
-package scala.scalajs.tools.env
-
-trait ComJSRunner extends AsyncJSRunner {
-
- /** Send a message to the JS VM. Throws if the message cannot be sent. */
- def send(msg: String): Unit
-
- /** Block until a message is received. Throws a [[ComClosedExcpetion]]
- * if the channel is closed before a message is received.
- */
- def receive(): String
-
- /** Close the communication channel. Allows the VM to terminate if it is
- * still waiting for callback. The JVM side **must** call close in
- * order to be able to expect termination of the VM.
- *
- * Calling [[stop]] on a [ComJSRunner]] automatically closes the
- * channel.
- */
- def close(): Unit
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ConsoleJSConsole.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ConsoleJSConsole.scala
deleted file mode 100644
index 5b3d055..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/ConsoleJSConsole.scala
+++ /dev/null
@@ -1,17 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.env
-
-/** A JS console that prints on the console */
-object ConsoleJSConsole extends JSConsole {
- override def log(msg: Any): Unit = {
- Console.println(msg)
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSConsole.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSConsole.scala
deleted file mode 100644
index a93768f..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSConsole.scala
+++ /dev/null
@@ -1,15 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.env
-
-/** Trait representing a JS console */
-trait JSConsole {
- def log(msg: Any): Unit
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSEnv.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSEnv.scala
deleted file mode 100644
index f1fbf44..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSEnv.scala
+++ /dev/null
@@ -1,20 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.env
-
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.classpath._
-import scala.scalajs.tools.logging._
-
-trait JSEnv {
- /** Prepare a runner for the code in the virtual file. */
- def jsRunner(classpath: CompleteClasspath, code: VirtualJSFile,
- logger: Logger, console: JSConsole): JSRunner
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSRunner.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSRunner.scala
deleted file mode 100644
index 460fff0..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/JSRunner.scala
+++ /dev/null
@@ -1,15 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js sbt plugin **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.env
-
-trait JSRunner {
- /** Run the associated JS code. Throw if an error occurs. */
- def run(): Unit
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/NullJSConsole.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/NullJSConsole.scala
deleted file mode 100644
index 8147bbe..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/env/NullJSConsole.scala
+++ /dev/null
@@ -1,5 +0,0 @@
-package scala.scalajs.tools.env
-
-object NullJSConsole extends JSConsole {
- def log(msg: Any): Unit = {}
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/CacheUtils.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/CacheUtils.scala
deleted file mode 100644
index 14773f8..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/CacheUtils.scala
+++ /dev/null
@@ -1,52 +0,0 @@
-package scala.scalajs.tools.io
-
-object CacheUtils {
-
- def joinVersions(vs: Option[String]*): Option[String] = {
- val bld = new StringBuilder
-
- @scala.annotation.tailrec
- def loop(vs: Seq[Option[String]]): Option[String] = {
- vs match {
- case Some(v) :: vss =>
- bld.append(mangleVersionString(v))
- loop(vss)
- case None :: _ =>
- None
- case Nil =>
- Some(bld.toString)
- }
- }
-
- loop(vs.toList)
- }
-
- def joinVersions(vs: String*): String =
- vs.map(mangleVersionString _).mkString
-
- private def mangleVersionString(str: String) = s"${str.length}:$str"
-
- def cached(version: Option[String], output: VirtualFile,
- cache: Option[WritableVirtualTextFile])(action: => Unit): Unit = {
-
- val upToDate = output.exists && (
- for {
- v <- version
- c <- cache if c.exists
- } yield c.content == v
- ).getOrElse(false)
-
- // Are we outdated?
- if (!upToDate) {
- action
-
- // Write cache
- for (c <- cache; v <- version) {
- val w = c.contentWriter
- try w.write(v)
- finally w.close()
- }
- }
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/IO.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/IO.scala
deleted file mode 100644
index b69b07c..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/IO.scala
+++ /dev/null
@@ -1,116 +0,0 @@
-package scala.scalajs.tools.io
-
-import scala.annotation.tailrec
-
-import scala.reflect.ClassTag
-
-import java.io._
-
-object IO {
- /** Returns the lines in an input stream.
- * Lines do not contain the new line characters.
- */
- def readLines(stream: InputStream): List[String] =
- readLines(new InputStreamReader(stream))
-
- /** Returns the lines in a string.
- * Lines do not contain the new line characters.
- */
- def readLines(content: String): List[String] =
- readLines(new StringReader(content))
-
- /** Returns the lines in a reader.
- * Lines do not contain the new line characters.
- */
- def readLines(reader: Reader): List[String] = {
- val br = new BufferedReader(reader)
- try {
- val builder = List.newBuilder[String]
- @tailrec
- def loop(): Unit = {
- val line = br.readLine()
- if (line ne null) {
- builder += line
- loop()
- }
- }
- loop()
- builder.result()
- } finally {
- br.close()
- }
- }
-
- /** Reads the entire content of a reader as a string. */
- def readReaderToString(reader: Reader): String = {
- val buffer = newBuffer[Char]
- val builder = new StringBuilder
- @tailrec
- def loop(): Unit = {
- val len = reader.read(buffer)
- if (len > 0) {
- builder.appendAll(buffer, 0, len)
- loop()
- }
- }
- loop()
- builder.toString()
- }
-
- /** Reads the entire content of an input stream as a UTF-8 string. */
- def readInputStreamToString(stream: InputStream): String = {
- val reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"))
- readReaderToString(reader)
- }
-
- /** Reads the entire content of an input stream as a byte array. */
- def readInputStreamToByteArray(stream: InputStream): Array[Byte] = {
- val builder = new ByteArrayOutputStream()
- val buffer = newBuffer[Byte]
- @tailrec
- def loop(): Unit = {
- val size = stream.read(buffer)
- if (size > 0) {
- builder.write(buffer, 0, size)
- loop()
- }
- }
- loop()
- builder.toByteArray()
- }
-
- /** Concatenates a bunch of VirtualTextFiles to a WritableVirtualTextFile.
- * Adds a '\n' after each file.
- */
- def concatFiles(output: WritableVirtualTextFile,
- files: Seq[VirtualTextFile]): Unit = {
- val buffer = newBuffer[Char]
- val out = output.contentWriter
-
- try {
- for (file <- files) {
- val reader = file.reader
-
- @tailrec
- def loop(): Unit = {
- val size = reader.read(buffer)
- if (size > 0) {
- out.write(buffer, 0, size)
- loop()
- }
- }
-
- try loop()
- finally reader.close()
-
- // New line after each file
- out.write('\n')
- }
- } finally {
- out.close()
- }
- }
-
- @inline
- private def newBuffer[T : ClassTag] = new Array[T](4096)
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/MemFiles.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/MemFiles.scala
deleted file mode 100644
index 68f66dc..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/MemFiles.scala
+++ /dev/null
@@ -1,105 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.io
-
-import java.io._
-
-/** A base class for simple in-memory mutable virtual files. */
-class MemVirtualFile(val path: String) extends VirtualFile {
- private[this] var _version: Option[String] = None
-
- override def version: Option[String] = _version
- def version_=(v: Option[String]): Unit = _version = v
-
- final def withVersion(v: Option[String]): this.type = {
- version = v
- this
- }
-
- override def exists: Boolean = true
-}
-
-/** A simple in-memory mutable virtual text file. */
-class MemVirtualTextFile(p: String) extends MemVirtualFile(p)
- with VirtualTextFile {
- private[this] var _content: String = ""
-
- override def content: String = _content
- def content_=(v: String): Unit = _content = v
-
- final def withContent(v: String): this.type = {
- content = v
- this
- }
-}
-
-trait WritableMemVirtualTextFile extends MemVirtualTextFile
- with WritableVirtualTextFile {
- def contentWriter: Writer = new StringWriter {
- override def close(): Unit = {
- super.close()
- WritableMemVirtualTextFile.this.content = this.toString
- }
- }
-}
-
-object WritableMemVirtualTextFile {
- def apply(path: String): WritableMemVirtualTextFile =
- new MemVirtualTextFile(path) with WritableMemVirtualTextFile
-}
-
-/** A simple in-memory mutable virtual binary file. */
-class MemVirtualBinaryFile(p: String) extends MemVirtualFile(p)
- with VirtualBinaryFile {
- private[this] var _content: Array[Byte] = new Array[Byte](0)
-
- override def content: Array[Byte] = _content
- def content_=(v: Array[Byte]): Unit = _content = v
-
- final def withContent(v: Array[Byte]): this.type = {
- content = v
- this
- }
-}
-
-/** A simple in-memory mutable virtual JS file. */
-class MemVirtualJSFile(p: String) extends MemVirtualTextFile(p)
- with VirtualJSFile {
- private[this] var _sourceMap: Option[String] = None
-
- override def sourceMap: Option[String] = _sourceMap
- def sourceMap_=(v: Option[String]): Unit = _sourceMap = v
-
- final def withSourceMap(v: Option[String]): this.type = {
- sourceMap = v
- this
- }
-}
-
-trait WritableMemVirtualJSFile extends MemVirtualJSFile
- with WritableVirtualJSFile
- with WritableMemVirtualTextFile {
-
- def sourceMapWriter: Writer = new StringWriter {
- override def close(): Unit = {
- super.close()
- WritableMemVirtualJSFile.this.sourceMap = Some(this.toString)
- }
- }
-}
-
-object WritableMemVirtualJSFile {
- def apply(path: String): WritableMemVirtualJSFile =
- new MemVirtualJSFile(path) with WritableMemVirtualJSFile
-}
-
-/** A simple in-memory mutable virtual serialized Scala.js IR file. */
-class MemVirtualSerializedScalaJSIRFile(p: String) extends MemVirtualBinaryFile(p)
- with VirtualSerializedScalaJSIRFile
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/VirtualFiles.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/VirtualFiles.scala
deleted file mode 100644
index c62ab5c..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/io/VirtualFiles.scala
+++ /dev/null
@@ -1,169 +0,0 @@
-package scala.scalajs.tools.io
-
-import java.io._
-import java.net.URI
-
-import scala.scalajs.ir
-import scala.scalajs.tools.sourcemap._
-
-/** A virtual input file.
- */
-trait VirtualFile {
- /** Path of the file, including everything.
- * Unique if possible (used for lookup). */
- def path: String
-
- /** Name of the file/writer, including extension */
- def name: String = VirtualFile.nameFromPath(path)
-
- /** Optionally returns an implementation-dependent "version" token.
- * Versions are compared with ==.
- * If non-empty, a different version must be returned when the content
- * changes. It should be equal if the content has not changed, but it is
- * not mandatory.
- * Such a token can be used by caches: the file need not be read and
- * processed again if its version has not changed.
- */
- def version: Option[String] = None
-
- /** Whether this file exists. Reading a non-existent file may fail */
- def exists: Boolean
-
- /** URI for this virtual file */
- def toURI: URI = {
- new URI(
- "virtualfile", // Pseudo-Scheme
- path, // Scheme specific part
- null // Fragment
- )
- }
-}
-
-object VirtualFile {
- /** Splits at the last slash and returns remainder */
- def nameFromPath(path: String): String = {
- val pos = path.lastIndexOf('/')
- if (pos == -1) path
- else path.substring(pos + 1)
- }
-}
-
-/** A virtual input file.
- */
-trait VirtualTextFile extends VirtualFile {
- /** Returns the content of the file. */
- def content: String
-
- /** Returns a new Reader of the file. */
- def reader: Reader = new StringReader(content)
-
- /** Returns the lines in the content.
- * Lines do not contain the new line characters.
- */
- def readLines(): List[String] = IO.readLines(reader)
-}
-
-object VirtualTextFile {
- def empty(path: String): VirtualTextFile =
- new MemVirtualTextFile(path)
-}
-
-trait WritableVirtualTextFile extends VirtualTextFile {
- def contentWriter: Writer
-}
-
-/** A virtual binary input file.
- */
-trait VirtualBinaryFile extends VirtualFile {
- /** Returns the content of the file. */
- def content: Array[Byte]
-
- /** Returns a new InputStream of the file. */
- def inputStream: InputStream = new ByteArrayInputStream(content)
-}
-
-/** A virtual input file which contains JavaScript code.
- * It may have a source map associated with it.
- */
-trait VirtualJSFile extends VirtualTextFile {
- /** Optionally, content of the source map file associated with this
- * JavaScript source.
- */
- def sourceMap: Option[String] = None
-}
-
-object VirtualJSFile {
- def empty(path: String): VirtualJSFile =
- new MemVirtualJSFile(path).withVersion(Some(path))
-}
-
-trait WritableVirtualJSFile extends WritableVirtualTextFile with VirtualJSFile {
- def sourceMapWriter: Writer
-}
-
-/** A virtual Scala.js IR file.
- * It contains the class info and the IR tree.
- */
-trait VirtualScalaJSIRFile extends VirtualFile {
- /** Rough class info of this file. */
- def roughInfo: ir.Infos.RoughClassInfo = info
-
- /** Class info of this file. */
- def info: ir.Infos.ClassInfo =
- infoAndTree._1
-
- /** IR Tree of this file. */
- def tree: ir.Trees.ClassDef =
- infoAndTree._2
-
- /** Class info and IR tree of this file. */
- def infoAndTree: (ir.Infos.ClassInfo, ir.Trees.ClassDef)
-}
-
-/** Base trait for virtual Scala.js IR files that are serialized as binary file.
- */
-trait VirtualSerializedScalaJSIRFile extends VirtualBinaryFile with VirtualScalaJSIRFile {
- /** Rough class info of this file. */
- override def roughInfo: ir.Infos.RoughClassInfo = {
- // Overridden to read only the necessary parts
- val stream = inputStream
- try {
- ir.InfoSerializers.deserializeRoughInfo(stream)
- } catch {
- case e: IOException =>
- throw new IOException(s"Failed to deserialize rough info of $path", e)
- } finally {
- stream.close()
- }
- }
-
- /** Class info of this file. */
- override def info: ir.Infos.ClassInfo = {
- // Overridden to read only the necessary parts
- val stream = inputStream
- try {
- ir.InfoSerializers.deserializeFullInfo(stream)
- } catch {
- case e: IOException =>
- throw new IOException(s"Failed to deserialize info of $path", e)
- } finally {
- stream.close()
- }
- }
-
- /** Class info and IR tree of this file. */
- override def infoAndTree: (ir.Infos.ClassInfo, ir.Trees.ClassDef) = {
- val stream = inputStream
- try {
- val (version, info) = ir.InfoSerializers.deserializeVersionFullInfo(stream)
- val tree = ir.Serializers.deserialize(
- stream, version).asInstanceOf[ir.Trees.ClassDef]
- (info, tree)
- } catch {
- case e: IOException =>
- throw new IOException(s"Failed to deserialize $path", e)
- } finally {
- stream.close()
- }
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala
deleted file mode 100644
index b4d4005..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala
+++ /dev/null
@@ -1,1525 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-import scala.language.implicitConversions
-
-import scala.annotation.switch
-
-import scala.scalajs.ir._
-import Position._
-import Transformers._
-import scala.scalajs.ir.Trees._
-import scala.scalajs.ir.Types._
-
-import scala.scalajs.tools.sem._
-import CheckedBehavior._
-
-import scala.scalajs.tools.javascript.{Trees => js}
-
-/** Desugaring of the IR to regular ES5 JavaScript.
- *
- * The major difference between the IR and JS is that most constructs can be
- * used in expression position. The main work of the desugaring is to
- * unnest complex constructs in expression position so that they become
- * statements.
- *
- * The general idea is two-folded:
- * 1) Unnest complex constructs in "argument position":
- * When a complex construct is used in a non-rhs expression position
- * (argument to a function, operand, condition of an if, etc.), that we
- * call "argument position", declare a variable before the statement,
- * assign the complex construct to it and then use that variable in the
- * argument position instead.
- * 2) Push LHS's inside complex RHS's:
- * When an rhs is a complex construct, push the lhs inside the complex
- * construct. Are considered lhs:
- * * Assign, i.e., `x =`
- * * VarDef, i.e., `var x =`
- * * Return, i.e., `return`
- * * (EmptyTree is also used as a trick for code reuse)
- * In fact, think that, in this context, LHS means: what to do with the
- * result of evaluating the RHS.
- *
- * --------------------------------------------------------------------------
- *
- * Typical example, consider the method call:
- *
- * obj.meth({
- * var x = foo(42);
- * x*x
- * });
- *
- * According to rule 1), the block that is passed as a parameter to obj.meth
- * is first extracted in a synthetic var:
- *
- * var x\$1 = {
- * var x = foo(42);
- * x*x
- * }
- * obj.meth(x\$1);
- *
- * Then, according to rule 2), the lhs `var x\$1 =` is pushed inside the block:
- *
- * {
- * var x = foo(42);
- * var x\$1 = x*x;
- * }
- * obj.meth(x\$1);
- *
- * Because bare blocks are non-significant in JS, this is equivalent to
- *
- * var x = foo(42);
- * var x\$1 = x*x;
- * obj.meth(x\$1);
- *
- * --------------------------------------------------------------------------
- *
- * JSDesugaring does all this in a single pass, but it helps to think that:
- * * Rule 1) is implemented by unnest(), and used most notably in
- * * transformStat() for statement-only constructs
- * * pushLhsInto() for statement-or-expression constructs
- * * Rule 2) is implemented by pushLhsInto()
- * * Emitting the class structure is delegated to [[ScalaJSClassEmitter]].
- *
- * There are a few other things that JSDesugaring takes care of:
- * * Transform Scala expressions into their JS equivalent, taking the
- * Scala.js class encoding into account.
- * * And tiny details.
- *
- * @author Sébastien Doeraene
- */
-object JSDesugaring {
-
- private final val ScalaJSEnvironmentName = "ScalaJS"
-
- /** Desugars a statement of the IR into ES5 JavaScript. */
- def desugarJavaScript(tree: Tree, semantics: Semantics): js.Tree = {
- new JSDesugar(semantics).transformStat(tree)
- }
-
- private[javascript] implicit def transformIdent(ident: Ident): js.Ident =
- js.Ident(ident.name, ident.originalName)(ident.pos)
-
- private[javascript] def transformParamDef(paramDef: ParamDef): js.ParamDef =
- js.ParamDef(paramDef.name, paramDef.mutable)(paramDef.pos)
-
- private class JSDesugar(semantics: Semantics) {
-
- // Synthetic variables
-
- var syntheticVarCounter: Int = 0
-
- def newSyntheticVar()(implicit pos: Position): Ident = {
- syntheticVarCounter += 1
- Ident("jsx$" + syntheticVarCounter, None)
- }
-
- def resetSyntheticVarCounterIn[A](f: => A): A = {
- val savedCounter = syntheticVarCounter
- syntheticVarCounter = 0
- try f
- finally syntheticVarCounter = savedCounter
- }
-
- // Record names
-
- def makeRecordFieldIdent(recIdent: Ident, fieldIdent: Ident)(
- implicit pos: Position): Ident =
- makeRecordFieldIdent(recIdent.name, recIdent.originalName,
- fieldIdent.name, fieldIdent.originalName)
-
- def makeRecordFieldIdent(recIdent: Ident,
- fieldName: String, fieldOrigiName: Option[String])(
- implicit pos: Position): Ident =
- makeRecordFieldIdent(recIdent.name, recIdent.originalName,
- fieldName, fieldOrigiName)
-
- def makeRecordFieldIdent(recName: String, recOrigName: Option[String],
- fieldName: String, fieldOrigName: Option[String])(
- implicit pos: Position): Ident = {
- val name = recName + "_$_" + fieldName
- val originalName = Some(recOrigName.getOrElse(recName) + "." +
- fieldOrigName.getOrElse(fieldName))
- Ident(name, originalName)
- }
-
- // LHS'es for labeled expressions
-
- var labeledExprLHSes: Map[Ident, Tree] = Map.empty
-
- // Now the work
-
- /** Desugar a statement of the IR into ES5 JS */
- def transformStat(tree: Tree): js.Tree = {
- implicit val pos = tree.pos
-
- tree match {
- // Statement-only language constructs
-
- case Skip() =>
- js.Skip()
-
- case VarDef(varIdent, RecordType(fields), recMutable, EmptyTree) =>
- js.Block(for {
- RecordType.Field(fieldName, fieldOrigName, tpe, fieldMutable) <- fields
- } yield {
- transformStat {
- VarDef(makeRecordFieldIdent(varIdent, fieldName, fieldOrigName),
- tpe, recMutable || fieldMutable, EmptyTree)
- }
- })
-
- case VarDef(name, _, mutable, EmptyTree) =>
- js.VarDef(name, mutable, js.EmptyTree)
-
- case VarDef(_, _, _, rhs) =>
- pushLhsInto(tree, rhs)
-
- case Assign(RecordFieldVarRef(lhs), rhs) =>
- pushLhsInto(Assign(lhs, EmptyTree), rhs)
-
- case Assign(select @ Select(qualifier, item, mutable), rhs) =>
- unnest(qualifier, rhs) { (newQualifier, newRhs) =>
- js.Assign(
- js.DotSelect(transformExpr(newQualifier), item)(select.pos),
- transformExpr(newRhs))
- }
-
- case Assign(select @ ArraySelect(array, index), rhs) =>
- unnest(List(array, index, rhs)) {
- case List(newArray, newIndex, newRhs) =>
- js.Assign(
- js.BracketSelect(js.DotSelect(transformExpr(newArray),
- js.Ident("u"))(select.pos),
- transformExpr(newIndex))(select.pos),
- transformExpr(newRhs))
- }
-
- case Assign(select @ JSDotSelect(qualifier, item), rhs) =>
- unnest(qualifier, rhs) { (newQualifier, newRhs) =>
- js.Assign(
- js.DotSelect(transformExpr(newQualifier), item)(select.pos),
- transformExpr(newRhs))
- }
-
- case Assign(select @ JSBracketSelect(qualifier, item), rhs) =>
- unnest(List(qualifier, item, rhs)) {
- case List(newQualifier, newItem, newRhs) =>
- js.Assign(
- js.BracketSelect(transformExpr(newQualifier),
- transformExpr(newItem))(select.pos),
- transformExpr(newRhs))
- }
-
- case Assign(_ : VarRef, rhs) =>
- pushLhsInto(tree, rhs)
-
- case Assign(_, _) =>
- sys.error(s"Illegal Assign in transformStat: $tree")
-
- case StoreModule(cls, value) =>
- assert(cls.className.endsWith("$"),
- s"Trying to store module for non-module class $cls")
- val moduleName = cls.className.dropRight(1)
- unnest(value) { newValue =>
- js.Assign(
- js.DotSelect(envField("n"), Ident(moduleName)),
- transformExpr(newValue))
- }
-
- case While(cond, body, label) =>
- /* We cannot simply unnest(cond) here, because that would eject the
- * evaluation of the condition out of the loop.
- */
- val newLabel = label.map(transformIdent)
- if (isExpression(cond)) {
- js.While(transformExpr(cond), transformStat(body), newLabel)
- } else {
- js.While(js.BooleanLiteral(true), {
- unnest(cond) { newCond =>
- js.If(transformExpr(newCond), transformStat(body), js.Break())
- }
- }, newLabel)
- }
-
- case DoWhile(body, cond, label) =>
- /* We cannot simply unnest(cond) here, because that would eject the
- * evaluation of the condition out of the loop.
- */
- val newLabel = label.map(transformIdent)
- if (isExpression(cond)) {
- js.DoWhile(transformStat(body), transformExpr(cond), newLabel)
- } else {
- /* This breaks 'continue' statements for this loop, but we don't
- * care because we never emit continue statements for do..while
- * loops.
- */
- js.While(js.BooleanLiteral(true), {
- js.Block(transformStat(body), {
- unnest(cond) { newCond =>
- js.If(transformExpr(newCond), js.Skip(), js.Break())
- }
- })
- }, newLabel)
- }
-
- case Debugger() =>
- js.Debugger()
-
- case JSDelete(JSDotSelect(obj, prop)) =>
- unnest(obj) { (newObj) =>
- js.Delete(js.DotSelect(transformExpr(newObj), prop))
- }
-
- case JSDelete(JSBracketSelect(obj, prop)) =>
- unnest(obj, prop) { (newObj, newProp) =>
- js.Delete(js.BracketSelect(
- transformExpr(newObj), transformExpr(newProp)))
- }
-
- // Treat 'return' as an LHS
-
- case Return(expr, label) =>
- pushLhsInto(tree, expr)
-
- /* Anything else is an expression => pushLhsInto(EmptyTree, _)
- * In order not to duplicate all the code of pushLhsInto() here, we
- * use a trick: EmptyTree is a dummy LHS that says "do nothing
- * with the result of the rhs".
- * This is exactly what an expression statement is doing: it evaluates
- * the expression, but does nothing with its result.
- */
-
- case _ =>
- pushLhsInto(EmptyTree, tree)
- }
- }
-
- private object RecordFieldVarRef {
- def unapply(tree: Tree): Option[VarRef] = {
- tree match {
- case Select(RecordVarRef(VarRef(recIdent, recMutable)),
- fieldIdent, fieldMutable) =>
- implicit val pos = tree.pos
- Some(VarRef(makeRecordFieldIdent(recIdent, fieldIdent),
- recMutable || fieldMutable)(tree.tpe))
- case _ =>
- None
- }
- }
- }
-
- private object RecordVarRef {
- def unapply(tree: Tree): Option[VarRef] = {
- if (!tree.tpe.isInstanceOf[RecordType]) None
- else {
- tree match {
- case tree: VarRef => Some(tree)
- case Select(RecordVarRef(VarRef(recIdent, recMutable)),
- fieldIdent, fieldMutable) =>
- implicit val pos = tree.pos
- Some(VarRef(makeRecordFieldIdent(recIdent, fieldIdent),
- recMutable || fieldMutable)(tree.tpe))
- }
- }
- }
- }
-
- /** Unnest complex constructs in argument position in temporary variables
- *
- * If all the arguments are JS expressions, there is nothing to do.
- * Any argument that is not a JS expression must be unnested and stored
- * in a temporary variable before the statement produced by `makeStat`.
- *
- * But *this changes the evaluation order!* In order not to lose it, it
- * is necessary to also unnest arguments that are expressions but that
- * are supposed to be evaluated before the argument-to-be-unnested and
- * could have side-effects or even whose evaluation could be influenced
- * by the side-effects of another unnested argument.
- *
- * Without deep effect analysis, which we do not do, we need to take
- * a very pessimistic approach, and unnest any expression that contains
- * an identifier (except those after the last non-expression argument).
- * Hence the predicate `isPureExpressionWithoutIdent`.
- */
- def unnest(args: List[Tree])(
- makeStat: List[Tree] => js.Tree): js.Tree = {
- if (args forall isExpression) makeStat(args)
- else {
- val extractedStatements = new scala.collection.mutable.ListBuffer[js.Tree]
-
- /* Attention! Everything must be processed recursively
- * *right-to-left*! Indeed, the point is that noExtractYet will tell
- * whether anything supposed to be evaluated *after* the currently
- * being processed expression has been (at least partly) extracted
- * in temporary variables (or simply statements, in the Block case).
- * If nothing has, we can keep more in place without having to extract
- * that expression in a temporary variable.
- */
-
- def rec(arg: Tree): Tree = {
- def noExtractYet = extractedStatements.isEmpty
-
- if (if (noExtractYet) isExpression(arg) else isPureExpression(arg)) {
- arg
- } else {
- implicit val pos = arg.pos
- arg match {
- case Block(stats :+ expr) =>
- val result = rec(expr) // right-to-left, remember?
- // Put the stats in a Block because ++=: is not smart
- js.Block(stats.map(transformStat)) +=: extractedStatements
- result
-
- case UnaryOp(op, lhs) =>
- UnaryOp(op, rec(lhs))
- case BinaryOp(op, lhs, rhs) =>
- val newRhs = rec(rhs)
- BinaryOp(op, rec(lhs), newRhs)
- case JSBinaryOp(op, lhs, rhs) =>
- val newRhs = rec(rhs)
- JSBinaryOp(op, rec(lhs), newRhs)
- case JSUnaryOp(op, lhs) =>
- JSUnaryOp(op, rec(lhs))
- case IsInstanceOf(expr, tpe) =>
- IsInstanceOf(rec(expr), tpe)
-
- case AsInstanceOf(expr, tpe)
- if noExtractYet || semantics.asInstanceOfs == Unchecked =>
- AsInstanceOf(rec(expr), tpe)
- case Unbox(expr, tpe)
- if noExtractYet || semantics.asInstanceOfs == Unchecked =>
- Unbox(rec(expr), tpe)
-
- case NewArray(tpe, lengths) =>
- NewArray(tpe, recs(lengths))
- case ArrayValue(tpe, elems) =>
- ArrayValue(tpe, recs(elems))
- case JSArrayConstr(items) =>
- JSArrayConstr(recs(items))
- case JSObjectConstr(items) =>
- val newValues = recs(items.map(_._2))
- JSObjectConstr(items.map(_._1) zip newValues)
- case Closure(captureParams, params, body, captureValues) =>
- Closure(captureParams, params, body, recs(captureValues))
-
- case New(cls, constr, args) if noExtractYet =>
- New(cls, constr, recs(args))
- case Select(qualifier, item, mutable) if noExtractYet =>
- Select(rec(qualifier), item, mutable)(arg.tpe)
- case Apply(receiver, method, args) if noExtractYet =>
- val newArgs = recs(args)
- Apply(rec(receiver), method, newArgs)(arg.tpe)
- case StaticApply(receiver, cls, method, args) if noExtractYet =>
- val newArgs = recs(args)
- StaticApply(rec(receiver), cls, method, newArgs)(arg.tpe)
- case TraitImplApply(impl, method, args) if noExtractYet =>
- TraitImplApply(impl, method, recs(args))(arg.tpe)
- case ArrayLength(array) if noExtractYet =>
- ArrayLength(rec(array))
- case ArraySelect(array, index) if noExtractYet =>
- val newIndex = rec(index)
- ArraySelect(rec(array), newIndex)(arg.tpe)
- case CallHelper(helper, args) if noExtractYet =>
- CallHelper(helper, recs(args))(arg.tpe)
-
- case If(cond, thenp, elsep)
- if noExtractYet && isExpression(thenp) && isExpression(elsep) =>
- If(rec(cond), thenp, elsep)(arg.tpe)
-
- case _ =>
- val temp = newSyntheticVar()
- val computeTemp =
- pushLhsInto(VarDef(temp, arg.tpe, mutable = false, EmptyTree), arg)
- computeTemp +=: extractedStatements
- VarRef(temp, mutable = false)(arg.tpe)
- }
- }
- }
-
- def recs(args: List[Tree]): List[Tree] = {
- // This is a right-to-left map
- args.foldRight[List[Tree]](Nil) { (arg, acc) =>
- rec(arg) :: acc
- }
- }
-
- val newArgs = recs(args)
-
- assert(extractedStatements.nonEmpty,
- "Reached computeTemps with no temp to compute")
-
- val newStatement = makeStat(newArgs)
- js.Block(extractedStatements.result() ::: List(newStatement))(newStatement.pos)
- }
- }
-
- /** Same as above, for a single argument */
- def unnest(arg: Tree)(
- makeStat: Tree => js.Tree): js.Tree = {
- unnest(List(arg)) {
- case List(newArg) => makeStat(newArg)
- }
- }
-
- /** Same as above, for two arguments */
- def unnest(lhs: Tree, rhs: Tree)(
- makeStat: (Tree, Tree) => js.Tree): js.Tree = {
- unnest(List(lhs, rhs)) {
- case List(newLhs, newRhs) => makeStat(newLhs, newRhs)
- }
- }
-
- /** Same as above, for one head argument and a list of arguments */
- def unnest(arg0: Tree, args: List[Tree])(
- makeStat: (Tree, List[Tree]) => js.Tree): js.Tree = {
- unnest(arg0 :: args) { newArgs =>
- makeStat(newArgs.head, newArgs.tail)
- }
- }
-
- /** Common implementation for the functions below.
- * A pure expression can be moved around or executed twice, because it
- * will always produce the same result and never have side-effects.
- * A side-effect free expression can be elided if its result is not used.
- */
- private def isExpressionInternal(tree: Tree,
- allowUnpure: Boolean, allowSideEffects: Boolean): Boolean = {
-
- require(!allowSideEffects || allowUnpure)
-
- def test(tree: Tree): Boolean = tree match {
- // Atomic expressions
- case _: Literal => true
- case _: This => true
- case _: JSEnvInfo => true
-
- // Vars and fields (side-effect free, pure if immutable)
- case VarRef(_, mutable) =>
- allowUnpure || !mutable
- case Select(qualifier, item, mutable) =>
- (allowUnpure || !mutable) && test(qualifier)
-
- // Expressions preserving pureness
- case Block(trees) => trees forall test
- case If(cond, thenp, elsep) => test(cond) && test(thenp) && test(elsep)
- case BinaryOp(_, lhs, rhs) => test(lhs) && test(rhs)
- case UnaryOp(_, lhs) => test(lhs)
- case JSBinaryOp(_, lhs, rhs) => test(lhs) && test(rhs)
- case JSUnaryOp(_, lhs) => test(lhs)
- case ArrayLength(array) => test(array)
- case IsInstanceOf(expr, _) => test(expr)
-
- // Expressions preserving side-effect freedom
- case NewArray(tpe, lengths) =>
- allowUnpure && (lengths forall test)
- case ArrayValue(tpe, elems) =>
- allowUnpure && (elems forall test)
- case ArraySelect(array, index) =>
- allowUnpure && test(array) && test(index)
- case JSArrayConstr(items) =>
- allowUnpure && (items forall test)
- case JSObjectConstr(items) =>
- allowUnpure && (items forall (item => test(item._2)))
- case Closure(captureParams, params, body, captureValues) =>
- allowUnpure && (captureValues forall test)
-
- // Scala expressions that can always have side-effects
- case New(cls, constr, args) =>
- allowSideEffects && (args forall test)
- case LoadModule(cls) => // unfortunately
- allowSideEffects
- case Apply(receiver, method, args) =>
- allowSideEffects && test(receiver) && (args forall test)
- case StaticApply(receiver, cls, method, args) =>
- allowSideEffects && test(receiver) && (args forall test)
- case TraitImplApply(impl, method, args) =>
- allowSideEffects && (args forall test)
- case GetClass(arg) =>
- allowSideEffects && test(arg)
- case CallHelper(helper, args) =>
- allowSideEffects && (args forall test)
-
- // Casts
- case AsInstanceOf(expr, _) =>
- (allowSideEffects || semantics.asInstanceOfs == Unchecked) && test(expr)
- case Unbox(expr, _) =>
- (allowSideEffects || semantics.asInstanceOfs == Unchecked) && test(expr)
-
- // Because the env is a frozen object, env["global"] is pure
- case JSBracketSelect(JSEnvInfo(), StringLiteral("global")) => true
-
- // JavaScript expressions that can always have side-effects
- case JSNew(fun, args) =>
- allowSideEffects && test(fun) && (args forall test)
- case JSDotSelect(qualifier, item) =>
- allowSideEffects && test(qualifier)
- case JSBracketSelect(qualifier, item) =>
- allowSideEffects && test(qualifier) && test(item)
- case JSFunctionApply(fun, args) =>
- allowSideEffects && test(fun) && (args forall test)
- case JSDotMethodApply(receiver, method, args) =>
- allowSideEffects && test(receiver) && (args forall test)
- case JSBracketMethodApply(receiver, method, args) =>
- allowSideEffects && test(receiver) && test(method) && (args forall test)
-
- // Non-expressions
- case _ => false
- }
- test(tree)
- }
-
- /** Test whether the given tree is a standard JS expression.
- */
- def isExpression(tree: Tree): Boolean =
- isExpressionInternal(tree, allowUnpure = true, allowSideEffects = true)
-
- /** Test whether the given tree is a side-effect-free standard JS expression.
- */
- def isSideEffectFreeExpression(tree: Tree): Boolean =
- isExpressionInternal(tree, allowUnpure = true, allowSideEffects = false)
-
- /** Test whether the given tree is a pure standard JS expression.
- */
- def isPureExpression(tree: Tree): Boolean =
- isExpressionInternal(tree, allowUnpure = false, allowSideEffects = false)
-
- def doVarDef(ident: Ident, tpe: Type, mutable: Boolean, rhs: Tree): js.Tree = {
- implicit val pos = rhs.pos
- tpe match {
- case RecordType(fields) =>
- val elems = (rhs: @unchecked) match {
- case RecordValue(_, elems) =>
- elems
- case VarRef(rhsIdent, rhsMutable) =>
- for (RecordType.Field(fName, fOrigName, fTpe, fMutable) <- fields)
- yield VarRef(makeRecordFieldIdent(rhsIdent, fName, fOrigName),
- rhsMutable || fMutable)(fTpe)
- }
- js.Block(for {
- (RecordType.Field(fName, fOrigName, fTpe, fMutable),
- fRhs) <- fields zip elems
- } yield {
- doVarDef(makeRecordFieldIdent(ident, fName, fOrigName),
- fTpe, mutable || fMutable, fRhs)
- })
-
- case _ =>
- js.VarDef(ident, mutable, transformExpr(rhs))
- }
- }
-
- def doAssign(lhs: Tree, rhs: Tree): js.Tree = {
- implicit val pos = rhs.pos
- lhs.tpe match {
- case RecordType(fields) =>
- val VarRef(ident, mutable) = lhs
- val elems = (rhs: @unchecked) match {
- case VarRef(rhsIdent, rhsMutable) =>
- for (RecordType.Field(fName, fOrigName, fTpe, fMutable) <- fields)
- yield VarRef(makeRecordFieldIdent(rhsIdent, fName, fOrigName),
- rhsMutable || fMutable)(fTpe)
- }
- js.Block(for {
- (RecordType.Field(fName, fOrigName, fTpe, fMutable),
- fRhs) <- fields zip elems
- } yield {
- doAssign(VarRef(makeRecordFieldIdent(ident, fName, fOrigName),
- mutable || fMutable)(fTpe), fRhs)
- })
-
- case _ =>
- js.Assign(transformExpr(lhs), transformExpr(rhs))
- }
- }
-
- /** Push an lhs into a (potentially complex) rhs
- * lhs can be either a EmptyTree, a VarDef, a Assign or a
- * Return
- */
- def pushLhsInto(lhs: Tree, rhs: Tree): js.Tree = {
- implicit val rhsPos = rhs.pos
-
- /** Push the current lhs further into a deeper rhs */
- @inline def redo(newRhs: Tree) = pushLhsInto(lhs, newRhs)
-
- if (rhs.tpe == NothingType && lhs != EmptyTree) {
- /* A touch of peephole dead code elimination.
- * Actually necessary to handle pushing an lhs into an infinite loop,
- * for example.
- */
- val transformedRhs = pushLhsInto(EmptyTree, rhs)
- lhs match {
- case VarDef(name, _, mutable, _) =>
- /* We still need to declare the var, in case it is used somewhere
- * else in the function, where we can't dce it.
- */
- js.Block(js.VarDef(name, true, js.EmptyTree), transformedRhs)
- case _ =>
- transformedRhs
- }
- } else (rhs match {
- // Handle the Block before testing whether it is an expression
-
- case Block(stats :+ expr) =>
- js.Block((stats map transformStat) :+ redo(expr))
-
- // Base case, rhs is already a regular JS expression
-
- case _ if isExpression(rhs) =>
- (lhs: @unchecked) match {
- case EmptyTree =>
- if (isSideEffectFreeExpression(rhs)) js.Skip()
- else transformExpr(rhs)
- case VarDef(name, tpe, mutable, _) =>
- doVarDef(name, tpe, mutable, rhs)
- case Assign(lhs, _) =>
- doAssign(lhs, rhs)
- case Return(_, None) =>
- js.Return(transformExpr(rhs))
- case Return(_, label @ Some(l)) =>
- labeledExprLHSes(l) match {
- case newLhs @ Return(_, _) =>
- pushLhsInto(newLhs, rhs) // no need to break here
- case newLhs =>
- js.Block(pushLhsInto(newLhs, rhs),
- js.Break(label.map(transformIdent)))
- }
- }
-
- // Almost base case with RecordValue
-
- case RecordValue(recTpe, elems) =>
- (lhs: @unchecked) match {
- case EmptyTree =>
- js.Block(elems map transformStat)
- case VarDef(name, tpe, mutable, _) =>
- unnest(elems) { newElems =>
- doVarDef(name, tpe, mutable, RecordValue(recTpe, newElems))
- }
- case Assign(lhs, _) =>
- unnest(elems) { newElems =>
- val temp = newSyntheticVar()
- js.Block(
- doVarDef(temp, recTpe, false, RecordValue(recTpe, newElems)),
- doAssign(lhs, VarRef(temp, false)(recTpe)))
- }
- case Return(_, label @ Some(l)) =>
- val newLhs = labeledExprLHSes(l)
- js.Block(pushLhsInto(newLhs, rhs),
- js.Break(label.map(transformIdent)))
- }
-
- // Control flow constructs
-
- case Labeled(label, tpe, body) =>
- val savedMap = labeledExprLHSes
- labeledExprLHSes = labeledExprLHSes + (label -> lhs)
- try {
- lhs match {
- case Return(_, _) => redo(body)
- case _ => js.Labeled(label, redo(body))
- }
- } finally {
- labeledExprLHSes = savedMap
- }
-
- case Return(expr, _) =>
- pushLhsInto(rhs, expr)
-
- case Continue(label) =>
- js.Continue(label.map(transformIdent))
-
- case If(cond, thenp, elsep) =>
- unnest(cond) { newCond =>
- js.If(transformExpr(newCond), redo(thenp), redo(elsep))
- }
-
- case Try(block, errVar, handler, finalizer) =>
- val newHandler =
- if (handler == EmptyTree) js.EmptyTree else redo(handler)
- val newFinalizer =
- if (finalizer == EmptyTree) js.EmptyTree else transformStat(finalizer)
-
- if (newHandler != js.EmptyTree && newFinalizer != js.EmptyTree) {
- /* The Google Closure Compiler wrongly eliminates finally blocks, if
- * the catch block throws an exception.
- * Issues: #563, google/closure-compiler#186
- *
- * Therefore, we desugar
- *
- * try { ... } catch { ... } finally { ... }
- *
- * into
- *
- * try { try { ... } catch { ... } } finally { ... }
- */
- js.Try(js.Try(redo(block), errVar, newHandler, js.EmptyTree),
- errVar, js.EmptyTree, newFinalizer)
- } else
- js.Try(redo(block), errVar, newHandler, newFinalizer)
-
- // TODO Treat throw as an LHS?
- case Throw(expr) =>
- unnest(expr) { newExpr =>
- js.Throw(transformExpr(newExpr))
- }
-
- /** Matches are desugared into switches
- *
- * A match is different from a switch in two respects, both linked
- * to match being designed to be used in expression position in
- * Extended-JS.
- *
- * * There is no fall-through from one case to the next one, hence,
- * no break statement.
- * * Match supports _alternatives_ explicitly (with a switch, one
- * would use the fall-through behavior to implement alternatives).
- */
- case Match(selector, cases, default) =>
- unnest(selector) { newSelector =>
- val newCases = {
- for {
- (values, body) <- cases
- newValues = (values map transformExpr)
- // add the break statement
- newBody = js.Block(redo(body), js.Break())
- // desugar alternatives into several cases falling through
- caze <- (newValues.init map (v => (v, js.Skip()))) :+ (newValues.last, newBody)
- } yield {
- caze
- }
- }
- val newDefault =
- if (default == EmptyTree) js.EmptyTree
- else redo(default)
- js.Switch(transformExpr(newSelector), newCases, newDefault)
- }
-
- // Scala expressions (if we reach here their arguments are not expressions)
-
- case New(cls, ctor, args) =>
- unnest(args) { newArgs =>
- redo(New(cls, ctor, newArgs))
- }
-
- case Select(qualifier, item, mutable) =>
- unnest(qualifier) { newQualifier =>
- redo(Select(newQualifier, item, mutable)(rhs.tpe))
- }
-
- case Apply(receiver, method, args) =>
- unnest(receiver, args) { (newReceiver, newArgs) =>
- redo(Apply(newReceiver, method, newArgs)(rhs.tpe))
- }
-
- case StaticApply(receiver, cls, method, args) =>
- unnest(receiver, args) { (newReceiver, newArgs) =>
- redo(StaticApply(newReceiver, cls, method, newArgs)(rhs.tpe))
- }
-
- case TraitImplApply(impl, method, args) =>
- unnest(args) { newArgs =>
- redo(TraitImplApply(impl, method, newArgs)(rhs.tpe))
- }
-
- case UnaryOp(op, lhs) =>
- unnest(lhs) { newLhs =>
- redo(UnaryOp(op, newLhs))
- }
-
- case BinaryOp(op, lhs, rhs) =>
- unnest(lhs, rhs) { (newLhs, newRhs) =>
- redo(BinaryOp(op, newLhs, newRhs))
- }
-
- case NewArray(tpe, lengths) =>
- unnest(lengths) { newLengths =>
- redo(NewArray(tpe, newLengths))
- }
-
- case ArrayValue(tpe, elems) =>
- unnest(elems) { newElems =>
- redo(ArrayValue(tpe, newElems))
- }
-
- case ArrayLength(array) =>
- unnest(array) { newArray =>
- redo(ArrayLength(newArray))
- }
-
- case ArraySelect(array, index) =>
- unnest(array, index) { (newArray, newIndex) =>
- redo(ArraySelect(newArray, newIndex)(rhs.tpe))
- }
-
- case IsInstanceOf(expr, cls) =>
- unnest(expr) { newExpr =>
- redo(IsInstanceOf(newExpr, cls))
- }
-
- case AsInstanceOf(expr, cls) =>
- if (semantics.asInstanceOfs == Unchecked) {
- redo(expr)
- } else {
- unnest(expr) { newExpr =>
- redo(AsInstanceOf(newExpr, cls))
- }
- }
-
- case Unbox(expr, charCode) =>
- unnest(expr) { newExpr =>
- redo(Unbox(newExpr, charCode))
- }
-
- case GetClass(expr) =>
- unnest(expr) { newExpr =>
- redo(GetClass(newExpr))
- }
-
- case CallHelper(helper, args) =>
- unnest(args) { newArgs =>
- redo(CallHelper(helper, newArgs)(rhs.tpe))
- }
-
- // JavaScript expressions (if we reach here their arguments are not expressions)
-
- case JSNew(ctor, args) =>
- unnest(ctor :: args) { newCtorAndArgs =>
- val newCtor :: newArgs = newCtorAndArgs
- redo(JSNew(newCtor, newArgs))
- }
-
- case JSFunctionApply(fun, args) =>
- unnest(fun :: args) { newFunAndArgs =>
- val newFun :: newArgs = newFunAndArgs
- redo(JSFunctionApply(newFun, newArgs))
- }
-
- case JSDotMethodApply(receiver, method, args) =>
- unnest(receiver :: args) { newReceiverAndArgs =>
- val newReceiver :: newArgs = newReceiverAndArgs
- redo(JSDotMethodApply(newReceiver, method, newArgs))
- }
-
- case JSBracketMethodApply(receiver, method, args) =>
- unnest(receiver :: method :: args) { newReceiverAndArgs =>
- val newReceiver :: newMethod :: newArgs = newReceiverAndArgs
- redo(JSBracketMethodApply(newReceiver, newMethod, newArgs))
- }
-
- case JSDotSelect(qualifier, item) =>
- unnest(qualifier) { newQualifier =>
- redo(JSDotSelect(newQualifier, item))
- }
-
- case JSBracketSelect(qualifier, item) =>
- unnest(qualifier, item) { (newQualifier, newItem) =>
- redo(JSBracketSelect(newQualifier, newItem))
- }
-
- case JSUnaryOp(op, lhs) =>
- unnest(lhs) { newLhs =>
- redo(JSUnaryOp(op, newLhs))
- }
-
- case JSBinaryOp("&&", lhs, rhs) =>
- if (lhs.tpe == BooleanType) {
- redo(If(lhs, rhs, BooleanLiteral(false))(AnyType))
- } else {
- unnest(lhs) { newLhs =>
- redo(If(newLhs, rhs, newLhs)(AnyType))
- }
- }
-
- case JSBinaryOp("||", lhs, rhs) =>
- if (lhs.tpe == BooleanType) {
- redo(If(lhs, BooleanLiteral(true), rhs)(AnyType))
- } else {
- unnest(lhs) { newLhs =>
- redo(If(newLhs, newLhs, rhs)(AnyType))
- }
- }
-
- case JSBinaryOp(op, lhs, rhs) =>
- unnest(lhs, rhs) { (newLhs, newRhs) =>
- redo(JSBinaryOp(op, newLhs, newRhs))
- }
-
- case JSArrayConstr(items) =>
- unnest(items) { newItems =>
- redo(JSArrayConstr(newItems))
- }
-
- case JSObjectConstr(fields) =>
- val names = fields map (_._1)
- val items = fields map (_._2)
- unnest(items) { newItems =>
- redo(JSObjectConstr(names.zip(newItems)))
- }
-
- // Closures
-
- case Closure(captureParams, params, body, captureValues) =>
- unnest(captureValues) { newCaptureValues =>
- redo(Closure(captureParams, params, body, newCaptureValues))
- }
-
- case _ =>
- if (lhs == EmptyTree) {
- /* Go "back" to transformStat() after having dived into
- * expression statements. Remember that (lhs == EmptyTree)
- * is a trick that we use to "add" all the code of pushLhsInto()
- * to transformStat().
- */
- rhs match {
- case _:Skip | _:VarDef | _:Assign | _:While | _:DoWhile |
- _:Debugger | _:JSDelete | _:StoreModule | _:ClassDef =>
- transformStat(rhs)
- case _ =>
- sys.error("Illegal tree in JSDesugar.pushLhsInto():\n" +
- "lhs = " + lhs + "\n" + "rhs = " + rhs +
- " of class " + rhs.getClass)
- }
- } else {
- sys.error("Illegal tree in JSDesugar.pushLhsInto():\n" +
- "lhs = " + lhs + "\n" + "rhs = " + rhs +
- " of class " + rhs.getClass)
- }
- })
- }
-
- // Desugar Scala operations to JavaScript operations -----------------------
-
- /** Desugar an expression of the IR into ES5 JS */
- def transformExpr(tree: Tree): js.Tree = {
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- def or0(tree: js.Tree): js.Tree =
- js.BinaryOp("|", tree, js.IntLiteral(0))
-
- tree match {
- // Control flow constructs
-
- case Block(stats :+ expr) =>
- js.Block((stats map transformStat) :+ transformExpr(expr))
-
- // Note that these work even if thenp/elsep is not a BooleanType
- case If(cond, BooleanLiteral(true), elsep) =>
- js.BinaryOp("||", transformExpr(cond), transformExpr(elsep))
- case If(cond, thenp, BooleanLiteral(false)) =>
- js.BinaryOp("&&", transformExpr(cond), transformExpr(thenp))
-
- case If(cond, thenp, elsep) =>
- js.If(transformExpr(cond), transformExpr(thenp), transformExpr(elsep))
-
- // Scala expressions
-
- case New(cls, ctor, args) =>
- js.Apply(js.New(encodeClassVar(cls.className), Nil) DOT ctor,
- args map transformExpr)
-
- case LoadModule(cls) =>
- genLoadModule(cls.className)
-
- case RecordFieldVarRef(VarRef(name, mutable)) =>
- js.VarRef(name, mutable)
-
- case Select(qualifier, item, _) =>
- transformExpr(qualifier) DOT item
-
- case Apply(receiver, method, args) =>
- val newReceiver = transformExpr(receiver)
- val newArgs = args map transformExpr
- if (isMaybeHijackedClass(receiver.tpe) &&
- !Definitions.isReflProxyName(method.name)) {
- val helperName = hijackedClassMethodToHelperName(method.name)
- genCallHelper(helperName, newReceiver :: newArgs: _*)
- } else {
- js.Apply(newReceiver DOT method, newArgs)
- }
-
- case StaticApply(receiver, cls, method, args) =>
- val fun = encodeClassVar(cls.className).prototype DOT method
- js.Apply(fun DOT "call", (receiver :: args) map transformExpr)
-
- case TraitImplApply(impl, method, args) =>
- js.Apply(envField("i") DOT method, args map transformExpr)
-
- case UnaryOp(op, lhs) =>
- import UnaryOp._
- val newLhs = transformExpr(lhs)
- (op: @switch) match {
- case `typeof` => js.UnaryOp("typeof", newLhs)
- case Boolean_! => js.UnaryOp("!", newLhs)
- case DoubleToInt => js.BinaryOp("|", newLhs, js.IntLiteral(0))
-
- case LongToInt => genLongMethodApply(newLhs, LongImpl.toInt)
- case LongToDouble => genLongMethodApply(newLhs, LongImpl.toDouble)
-
- case DoubleToFloat => genFround(newLhs)
-
- case IntToLong =>
- genNewLong(LongImpl.initFromInt, newLhs)
- case DoubleToLong =>
- genLongModuleApply(LongImpl.fromDouble, newLhs)
- }
-
- case BinaryOp(op, lhs, rhs) =>
- import BinaryOp._
- val lhs1 = lhs match {
- case UnaryOp(UnaryOp.DoubleToInt, inner)
- if op == Int_& || op == Int_<< =>
- /* This case is emitted typically by conversions from
- * Float/Double to Char/Byte/Short. We have to introduce an
- * (int) cast in the IR so that it typechecks, but in JavaScript
- * this is redundant because & and << already convert both their
- * operands to ints. So we get rid of the conversion here.
- */
- inner
- case _ =>
- lhs
- }
-
- val newLhs = transformExpr(lhs1)
- val newRhs = transformExpr(rhs)
-
- (op: @switch) match {
- case === | Num_== | Boolean_== => js.BinaryOp("===", newLhs, newRhs)
- case !== | Num_!= | Boolean_!= => js.BinaryOp("!==", newLhs, newRhs)
-
- case String_+ =>
- if (lhs.tpe == StringType || rhs.tpe == StringType)
- js.BinaryOp("+", newLhs, newRhs)
- else
- js.BinaryOp("+", js.BinaryOp("+", js.StringLiteral(""), newLhs), newRhs)
-
- case `in` => js.BinaryOp("in", newLhs, newRhs)
- case `instanceof` => js.BinaryOp("instanceof", newLhs, newRhs)
-
- case Int_+ => or0(js.BinaryOp("+", newLhs, newRhs))
- case Int_- =>
- lhs match {
- case IntLiteral(0) => or0(js.UnaryOp("-", newRhs))
- case _ => or0(js.BinaryOp("-", newLhs, newRhs))
- }
- case Int_* => genCallHelper("imul", newLhs, newRhs)
- case Int_/ => or0(js.BinaryOp("/", newLhs, newRhs))
- case Int_% => js.BinaryOp("%", newLhs, newRhs)
-
- case Int_| => js.BinaryOp("|", newLhs, newRhs)
- case Int_& => js.BinaryOp("&", newLhs, newRhs)
- case Int_^ =>
- lhs match {
- case IntLiteral(-1) => js.UnaryOp("~", newRhs)
- case _ => js.BinaryOp("^", newLhs, newRhs)
- }
- case Int_<< => js.BinaryOp("<<", newLhs, newRhs)
- case Int_>>> => or0(js.BinaryOp(">>>", newLhs, newRhs))
- case Int_>> => js.BinaryOp(">>", newLhs, newRhs)
-
- case Float_+ => genFround(js.BinaryOp("+", newLhs, newRhs))
- case Float_- =>
- genFround(lhs match {
- case DoubleLiteral(0.0) => js.UnaryOp("-", newRhs)
- case _ => js.BinaryOp("-", newLhs, newRhs)
- })
- case Float_* => genFround(js.BinaryOp("*", newLhs, newRhs))
- case Float_/ => genFround(js.BinaryOp("/", newLhs, newRhs))
- case Float_% => genFround(js.BinaryOp("%", newLhs, newRhs))
-
- case Double_+ => js.BinaryOp("+", newLhs, newRhs)
- case Double_- =>
- lhs match {
- case DoubleLiteral(0.0) => js.UnaryOp("-", newRhs)
- case _ => js.BinaryOp("-", newLhs, newRhs)
- }
- case Double_* => js.BinaryOp("*", newLhs, newRhs)
- case Double_/ => js.BinaryOp("/", newLhs, newRhs)
- case Double_% => js.BinaryOp("%", newLhs, newRhs)
-
- case Num_< => js.BinaryOp("<", newLhs, newRhs)
- case Num_<= => js.BinaryOp("<=", newLhs, newRhs)
- case Num_> => js.BinaryOp(">", newLhs, newRhs)
- case Num_>= => js.BinaryOp(">=", newLhs, newRhs)
-
- case Long_+ => genLongMethodApply(newLhs, LongImpl.+, newRhs)
- case Long_- =>
- lhs match {
- case LongLiteral(0L) => genLongMethodApply(newRhs, LongImpl.UNARY_-)
- case _ => genLongMethodApply(newLhs, LongImpl.-, newRhs)
- }
- case Long_* => genLongMethodApply(newLhs, LongImpl.*, newRhs)
- case Long_/ => genLongMethodApply(newLhs, LongImpl./, newRhs)
- case Long_% => genLongMethodApply(newLhs, LongImpl.%, newRhs)
-
- case Long_| => genLongMethodApply(newLhs, LongImpl.|, newRhs)
- case Long_& => genLongMethodApply(newLhs, LongImpl.&, newRhs)
- case Long_^ =>
- lhs match {
- case LongLiteral(-1L) => genLongMethodApply(newRhs, LongImpl.UNARY_~)
- case _ => genLongMethodApply(newLhs, LongImpl.^, newRhs)
- }
- case Long_<< => genLongMethodApply(newLhs, LongImpl.<<, newRhs)
- case Long_>>> => genLongMethodApply(newLhs, LongImpl.>>>, newRhs)
- case Long_>> => genLongMethodApply(newLhs, LongImpl.>>, newRhs)
-
- case Long_== => genLongMethodApply(newLhs, LongImpl.===, newRhs)
- case Long_!= => genLongMethodApply(newLhs, LongImpl.!==, newRhs)
- case Long_< => genLongMethodApply(newLhs, LongImpl.<, newRhs)
- case Long_<= => genLongMethodApply(newLhs, LongImpl.<=, newRhs)
- case Long_> => genLongMethodApply(newLhs, LongImpl.>, newRhs)
- case Long_>= => genLongMethodApply(newLhs, LongImpl.>=, newRhs)
-
- case Boolean_| => !(!js.BinaryOp("|", newLhs, newRhs))
- case Boolean_& => !(!js.BinaryOp("&", newLhs, newRhs))
- }
-
- case NewArray(tpe, lengths) =>
- genCallHelper("newArrayObject",
- genClassDataOf(tpe), js.ArrayConstr(lengths map transformExpr))
-
- case ArrayValue(tpe, elems) =>
- genCallHelper("makeNativeArrayWrapper",
- genClassDataOf(tpe), js.ArrayConstr(elems map transformExpr))
-
- case ArrayLength(array) =>
- js.BracketSelect(js.DotSelect(transformExpr(array),
- Ident("u")), js.StringLiteral("length"))
-
- case ArraySelect(array, index) =>
- js.BracketSelect(js.DotSelect(transformExpr(array),
- Ident("u")), transformExpr(index))
-
- case IsInstanceOf(expr, cls) =>
- genIsInstanceOf(transformExpr(expr), cls)
-
- case AsInstanceOf(expr, cls) =>
- val newExpr = transformExpr(expr)
- if (semantics.asInstanceOfs == Unchecked) newExpr
- else genAsInstanceOf(newExpr, cls)
-
- case Unbox(expr, charCode) =>
- val newExpr = transformExpr(expr)
-
- if (semantics.asInstanceOfs == Unchecked) {
- (charCode: @switch) match {
- case 'Z' => !(!newExpr)
- case 'B' | 'S' | 'I' => js.BinaryOp("|", newExpr, js.IntLiteral(0))
- case 'J' => genCallHelper("uJ", newExpr)
- case 'F' => genFround(newExpr)
- case 'D' => js.UnaryOp("+", newExpr)
- }
- } else {
- genCallHelper("u"+charCode, newExpr)
- }
-
- case GetClass(expr) =>
- genCallHelper("objectGetClass", transformExpr(expr))
-
- case CallHelper(helper, args) =>
- genCallHelper(helper, args map transformExpr: _*)
-
- // JavaScript expressions
-
- case JSBracketSelect(JSEnvInfo(), StringLiteral("global")) =>
- // Shortcut for this field which is heavily used
- envField("g")
-
- case JSNew(constr, args) =>
- js.New(transformExpr(constr), args map transformExpr)
-
- case JSDotSelect(qualifier, item) =>
- js.DotSelect(transformExpr(qualifier), item)
-
- case JSBracketSelect(qualifier, item) =>
- js.BracketSelect(transformExpr(qualifier), transformExpr(item))
-
- case JSFunctionApply(fun, args) =>
- /* Protect the fun so that if it is, e.g.,
- * path.f
- * we emit
- * (0, path.f)(args...)
- * instead of
- * path.f(args...)
- * If we emit the latter, then `this` will be bound to `path` in
- * `f`, which is sometimes extremely harmful (e.g., for builtin
- * methods of `window`).
- */
- val transformedFun = transformExpr(fun)
- val protectedFun = transformedFun match {
- case _:js.DotSelect | _:js.BracketSelect =>
- js.Block(js.IntLiteral(0), transformedFun)
- case _ =>
- transformedFun
- }
- js.Apply(protectedFun, args map transformExpr)
-
- case JSDotMethodApply(receiver, method, args) =>
- js.Apply(js.DotSelect(transformExpr(receiver), method),
- args map transformExpr)
-
- case JSBracketMethodApply(receiver, method, args) =>
- js.Apply(js.BracketSelect(transformExpr(receiver),
- transformExpr(method)), args map transformExpr)
-
- case JSUnaryOp(op, lhs) =>
- js.UnaryOp(op, transformExpr(lhs))
-
- case JSBinaryOp(op, lhs, rhs) =>
- js.BinaryOp(op, transformExpr(lhs), transformExpr(rhs))
-
- case JSArrayConstr(items) =>
- js.ArrayConstr(items map transformExpr)
-
- case JSObjectConstr(fields) =>
- js.ObjectConstr(fields map {
- case (name: Ident, value) =>
- (transformIdent(name), transformExpr(value))
- case (StringLiteral(name), value) =>
- (js.StringLiteral(name), transformExpr(value))
- })
-
- case JSEnvInfo() =>
- envField("env")
-
- // Literals
-
- case Undefined() => js.Undefined()
- case Null() => js.Null()
- case BooleanLiteral(value) => js.BooleanLiteral(value)
- case IntLiteral(value) => js.IntLiteral(value)
- case FloatLiteral(value) => js.DoubleLiteral(value.toDouble)
- case DoubleLiteral(value) => js.DoubleLiteral(value)
- case StringLiteral(value) => js.StringLiteral(value)
-
- case LongLiteral(0L) =>
- genLongModuleApply(LongImpl.Zero)
- case LongLiteral(value) =>
- val (l, m, h) = LongImpl.extractParts(value)
- genNewLong(LongImpl.initFromParts,
- js.IntLiteral(l), js.IntLiteral(m), js.IntLiteral(h))
-
- case ClassOf(cls) =>
- js.Apply(js.DotSelect(genClassDataOf(cls), Ident("getClassOf")), Nil)
-
- // Atomic expressions
-
- case VarRef(name, mutable) =>
- js.VarRef(name, mutable)
-
- case This() =>
- js.This()
-
- case Closure(captureParams, params, body, captureValues) =>
- val transformedBody = {
- val withReturn = Return(body, None)
- transformStat(withReturn) match {
- case js.Block(stats :+ js.Return(js.Undefined())) => js.Block(stats)
- case other => other
- }
- }
-
- val innerFunction =
- js.Function(params.map(transformParamDef), transformedBody)
-
- if (captureParams.isEmpty) {
- innerFunction
- } else {
- js.Apply(
- js.Function(captureParams.map(transformParamDef), {
- js.Return(innerFunction)
- }),
- captureValues.map(transformExpr))
- }
-
- // Invalid trees
-
- case _ =>
- sys.error("Invalid tree in JSDesugar.transformExpr() "+
- s"of class ${tree.getClass}")
- }
- }
-
- def isMaybeHijackedClass(tpe: Type): Boolean = tpe match {
- case ClassType(cls) =>
- Definitions.HijackedClasses.contains(cls) ||
- Definitions.AncestorsOfHijackedClasses.contains(cls)
- case AnyType | UndefType | BooleanType | IntType | LongType |
- FloatType | DoubleType | StringType =>
- true
- case _ =>
- false
- }
-
- val hijackedClassMethodToHelperName: Map[String, String] = Map(
- "toString__T" -> "objectToString",
- "clone__O" -> "objectClone",
- "finalize__V" -> "objectFinalize",
- "notify__V" -> "objectNotify",
- "notifyAll__V" -> "objectNotifyAll",
- "equals__O__Z" -> "objectEquals",
- "hashCode__I" -> "objectHashCode",
-
- "length__I" -> "charSequenceLength",
- "charAt__I__C" -> "charSequenceCharAt",
- "subSequence__I__I__jl_CharSequence" -> "charSequenceSubSequence",
-
- "compareTo__O__I" -> "comparableCompareTo",
- "compareTo__jl_Boolean__I" -> "comparableCompareTo",
- "compareTo__jl_Byte__I" -> "comparableCompareTo",
- "compareTo__jl_Short__I" -> "comparableCompareTo",
- "compareTo__jl_Integer__I" -> "comparableCompareTo",
- "compareTo__jl_Long__I" -> "comparableCompareTo",
- "compareTo__jl_Float__I" -> "comparableCompareTo",
- "compareTo__jl_Double__I" -> "comparableCompareTo",
- "compareTo__jl_String__I" -> "comparableCompareTo",
-
- "booleanValue__Z" -> "booleanBooleanValue",
-
- "byteValue__B" -> "numberByteValue",
- "shortValue__S" -> "numberShortValue",
- "intValue__I" -> "numberIntValue",
- "longValue__J" -> "numberLongValue",
- "floatValue__F" -> "numberFloatValue",
- "doubleValue__D" -> "numberDoubleValue",
-
- "isNaN__Z" -> "isNaN",
- "isInfinite__Z" -> "isInfinite"
- )
-
- def genClassDataOf(cls: ReferenceType)(implicit pos: Position): js.Tree = {
- cls match {
- case ClassType(className) =>
- encodeClassField("d", className)
- case ArrayType(base, dims) =>
- (1 to dims).foldLeft(encodeClassField("d", base)) { (prev, _) =>
- js.Apply(js.DotSelect(prev, js.Ident("getArrayOf")), Nil)
- }
- }
- }
-
- private def genFround(arg: js.Tree)(implicit pos: Position): js.Tree = {
- genCallHelper("fround", arg)
- }
-
- private def genNewLong(ctor: String, args: js.Tree*)(
- implicit pos: Position): js.Tree = {
- import TreeDSL._
- js.Apply(
- js.New(encodeClassVar(LongImpl.RuntimeLongClass), Nil) DOT ctor,
- args.toList)
- }
-
- private def genLongMethodApply(receiver: js.Tree, methodName: String,
- args: js.Tree*)(implicit pos: Position): js.Tree = {
- import TreeDSL._
- js.Apply(receiver DOT methodName, args.toList)
- }
-
- private def genLongModuleApply(methodName: String, args: js.Tree*)(
- implicit pos: Position): js.Tree = {
- import TreeDSL._
- js.Apply(
- genLoadModule(LongImpl.RuntimeLongModuleClass) DOT methodName,
- args.toList)
- }
-
- private def genLoadModule(moduleClass: String)(
- implicit pos: Position): js.Tree = {
- import TreeDSL._
- assert(moduleClass.endsWith("$"),
- s"Trying to load module for non-module class $moduleClass")
- val moduleName = moduleClass.dropRight(1)
- js.Apply(envField("m") DOT moduleName, Nil)
- }
-
- }
-
- // Helpers
-
- private[javascript] def genIsInstanceOf(expr: js.Tree, cls: ReferenceType)(
- implicit pos: Position): js.Tree =
- genIsAsInstanceOf(expr, cls, test = true)
-
- private def genAsInstanceOf(expr: js.Tree, cls: ReferenceType)(
- implicit pos: Position): js.Tree =
- genIsAsInstanceOf(expr, cls, test = false)
-
- private def genIsAsInstanceOf(expr: js.Tree, cls: ReferenceType, test: Boolean)(
- implicit pos: Position): js.Tree = {
- import Definitions._
- import TreeDSL._
-
- cls match {
- case ClassType(className0) =>
- val className =
- if (className0 == BoxedLongClass) LongImpl.RuntimeLongClass
- else className0
-
- if (HijackedBoxedClasses.contains(className)) {
- if (test) {
- className match {
- case BoxedUnitClass => expr === js.Undefined()
- case BoxedBooleanClass => typeof(expr) === "boolean"
- case BoxedByteClass => genCallHelper("isByte", expr)
- case BoxedShortClass => genCallHelper("isShort", expr)
- case BoxedIntegerClass => genCallHelper("isInt", expr)
- case BoxedFloatClass => genCallHelper("isFloat", expr)
- case BoxedDoubleClass => typeof(expr) === "number"
- }
- } else {
- className match {
- case BoxedUnitClass => genCallHelper("asUnit", expr)
- case BoxedBooleanClass => genCallHelper("asBoolean", expr)
- case BoxedByteClass => genCallHelper("asByte", expr)
- case BoxedShortClass => genCallHelper("asShort", expr)
- case BoxedIntegerClass => genCallHelper("asInt", expr)
- case BoxedFloatClass => genCallHelper("asFloat", expr)
- case BoxedDoubleClass => genCallHelper("asDouble", expr)
- }
- }
- } else {
- js.Apply(
- envField(if (test) "is" else "as") DOT js.Ident(className),
- List(expr))
- }
-
- case ArrayType(base, depth) =>
- js.Apply(
- envField(if (test) "isArrayOf" else "asArrayOf") DOT js.Ident(base),
- List(expr, js.IntLiteral(depth)))
- }
- }
-
- private[javascript] def genCallHelper(helperName: String, args: js.Tree*)(
- implicit pos: Position): js.Tree =
- js.Apply(envField(helperName), args.toList)
-
- private[javascript] def encodeClassVar(className: String)(
- implicit pos: Position): js.Tree =
- encodeClassField("c", className)
-
- private[javascript] def encodeClassField(field: String, className: String)(
- implicit pos: Position): js.Tree =
- js.DotSelect(envField(field), js.Ident(className))
-
- private[javascript] def envField(field: String)(implicit pos: Position): js.Tree =
- js.DotSelect(js.VarRef(js.Ident(ScalaJSEnvironmentName), false),
- js.Ident(field))
-
- private[javascript] implicit class MyTreeOps(val self: js.Tree) {
- def prototype(implicit pos: Position): js.Tree =
- js.DotSelect(self, js.Ident("prototype"))
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala
deleted file mode 100644
index 70b81a3..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala
+++ /dev/null
@@ -1,116 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-object LongImpl {
- final val RuntimeLongClass = "sjsr_RuntimeLong"
- final val RuntimeLongModuleClass = "sjsr_RuntimeLong$"
-
- private final val SigUnary = "__sjsr_RuntimeLong"
- private final val SigBinary = "__sjsr_RuntimeLong__sjsr_RuntimeLong"
- private final val SigShift = "__I__sjsr_RuntimeLong"
- private final val SigCompare = "__sjsr_RuntimeLong__Z"
-
- final val UNARY_- = "unary$und$minus" + SigUnary
- final val UNARY_~ = "unary$und$tilde" + SigUnary
-
- final val + = "$$plus" + SigBinary
- final val - = "$$minus" + SigBinary
- final val * = "$$times" + SigBinary
- final val / = "$$div" + SigBinary
- final val % = "$$percent" + SigBinary
-
- final val | = "$$bar" + SigBinary
- final val & = "$$amp" + SigBinary
- final val ^ = "$$up" + SigBinary
-
- final val << = "$$less$less" + SigShift
- final val >>> = "$$greater$greater$greater" + SigShift
- final val >> = "$$greater$greater" + SigShift
-
- final val === = "equals" + SigCompare
- final val !== = "notEquals" + SigCompare
- final val < = "$$less" + SigCompare
- final val <= = "$$less$eq" + SigCompare
- final val > = "$$greater" + SigCompare
- final val >= = "$$greater$eq" + SigCompare
-
- final val toInt = "toInt" + "__I"
- final val toDouble = "toDouble" + "__D"
-
- final val byteValue = "byteValue__B"
- final val shortValue = "shortValue__S"
- final val intValue = "intValue__I"
- final val longValue = "longValue__J"
- final val floatValue = "floatValue__F"
- final val doubleValue = "doubleValue__D"
-
- final val equals_ = "equals__O__Z"
- final val hashCode_ = "hashCode__I"
- final val compareTo = "compareTo__jl_Long__I"
- final val compareToO = "compareTo__O__I"
-
- private val OperatorMethods = Set(
- UNARY_-, UNARY_~, this.+, this.-, *, /, %, |, &, ^, <<, >>>, >>,
- ===, !==, <, <=, >, >=, toInt, toDouble)
-
- private val BoxedLongMethods = Set(
- byteValue, shortValue, intValue, longValue, floatValue, doubleValue,
- equals_, hashCode_, compareTo, compareToO)
-
- val AllMethods = OperatorMethods ++ BoxedLongMethods
-
- // Methods used for intrinsics
-
- final val bitCount = "bitCount__I"
- final val signum = "signum__sjsr_RuntimeLong"
- final val numberOfLeadingZeros = "numberOfLeadingZeros__I"
- final val numberOfTrailingZeros = "numberOfTrailingZeros__I"
- final val toBinaryString = "toBinaryString__T"
- final val toHexString = "toHexString__T"
- final val toOctalString = "toOctalString__T"
-
- val AllIntrinsicMethods = Set(
- bitCount, signum, numberOfLeadingZeros, numberOfTrailingZeros,
- toBinaryString, toHexString, toOctalString)
-
- // Constructors
-
- final val initFromParts = "init___I__I__I"
- final val initFromInt = "init___I"
-
- val AllConstructors = Set(
- initFromParts, initFromInt)
-
- // Methods on the companion
-
- final val fromDouble = "fromDouble__D__sjsr_RuntimeLong"
-
- final val Zero = "Zero__sjsr_RuntimeLong"
-
- val AllModuleMethods = Set(
- fromDouble, Zero)
-
- // Boldly copied from library/scala.scalajs.runtime.RuntimeLong
-
- /** Number of relevant bits in l and m each. */
- private final val BITS = 22
- /** Number of relevant bits in l and m together. */
- private final val BITS01 = 2 * BITS
- /** Number of relevant bits in h. */
- private final val BITS2 = 64 - BITS01
- /** Bitmask for l and m. */
- private final val MASK = (1 << BITS) - 1
- /** Bitmask for h. */
- private final val MASK_2 = (1 << BITS2) - 1
-
- def extractParts(value: Long): (Int, Int, Int) =
- (value.toInt & MASK, (value >> BITS).toInt & MASK, (value >> BITS01).toInt & MASK_2)
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala
deleted file mode 100644
index 264c548..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala
+++ /dev/null
@@ -1,420 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-import scala.annotation.switch
-
-import scala.util.control.Breaks
-
-import java.io.Writer
-import java.net.URI
-
-import scala.scalajs.ir
-import ir.Position
-import ir.Position.NoPosition
-import ir.Printers.IndentationManager
-import ir.Utils.escapeJS
-
-import Trees._
-
-import scala.scalajs.tools.sourcemap.SourceMapWriter
-
-object Printers {
-
- class JSTreePrinter(protected val out: Writer) extends IndentationManager {
- def printTopLevelTree(tree: Tree) {
- tree match {
- case Skip() =>
- // do not print anything
- case Block(stats) =>
- for (stat <- stats)
- printTopLevelTree(stat)
- case _ =>
- printStat(tree)
- if (shouldPrintSepAfterTree(tree))
- print(";")
- println()
- }
- }
-
- protected def shouldPrintSepAfterTree(tree: Tree): Boolean =
- !tree.isInstanceOf[DocComment]
-
- protected def printBlock(tree: Tree): Unit = {
- val trees = tree match {
- case Block(trees) => trees
- case _ => List(tree)
- }
- print("{"); indent(); println()
- printSeq(trees) { x =>
- printStat(x)
- } { x =>
- if (shouldPrintSepAfterTree(x))
- print(";")
- println()
- }
- undent(); println(); print("}")
- }
-
- protected def printSig(args: List[ParamDef]): Unit = {
- printRow(args, "(", ", ", ")")
- print(" ")
- }
-
- protected def printArgs(args: List[Tree]): Unit = {
- printRow(args, "(", ", ", ")")
- }
-
- def printStat(tree: Tree): Unit =
- printTree(tree, isStat = true)
-
- def printTree(tree: Tree, isStat: Boolean): Unit = {
- tree match {
- case EmptyTree =>
- print("<empty>")
-
- // Comments
-
- case DocComment(text) =>
- val lines = text.split("\n").toList
- if (lines.tail.isEmpty) {
- print("/** ", lines.head, " */")
- } else {
- print("/** ", lines.head); println()
- for (line <- lines.tail) {
- print(" * ", line); println()
- }
- print(" */")
- }
-
- // Definitions
-
- case VarDef(ident, mutable, rhs) =>
- print("var ", ident)
- if (rhs != EmptyTree)
- print(" = ", rhs)
-
- case ParamDef(ident, mutable) =>
- print(ident)
-
- // Control flow constructs
-
- case Skip() =>
- print("/*<skip>*/")
-
- case tree @ Block(trees) =>
- if (isStat)
- printBlock(tree)
- else
- printRow(trees, "(", ", ", ")")
-
- case Labeled(label, body) =>
- print(label, ": ")
- printBlock(body)
-
- case Assign(lhs, rhs) =>
- print(lhs, " = ", rhs)
-
- case Return(expr) =>
- print("return ", expr)
-
- case If(cond, thenp, elsep) =>
- if (isStat) {
- print("if (", cond, ") ")
- printBlock(thenp)
- elsep match {
- case Skip() => ()
- case If(_, _, _) =>
- print(" else ")
- printTree(elsep, isStat)
- case _ =>
- print(" else ")
- printBlock(elsep)
- }
- } else {
- print("(", cond, " ? ", thenp, " : ", elsep, ")")
- }
-
- case While(cond, body, label) =>
- if (label.isDefined)
- print(label.get, ": ")
- print("while (", cond, ") ")
- printBlock(body)
-
- case DoWhile(body, cond, label) =>
- if (label.isDefined)
- print(label.get, ": ")
- print("do ")
- printBlock(body)
- print(" while (", cond, ")")
-
- case Try(block, errVar, handler, finalizer) =>
- print("try ")
- printBlock(block)
- if (handler != EmptyTree) {
- print(" catch (", errVar, ") ")
- printBlock(handler)
- }
- if (finalizer != EmptyTree) {
- print(" finally ")
- printBlock(finalizer)
- }
-
- case Throw(expr) =>
- print("throw ", expr)
-
- case Break(label) =>
- if (label.isEmpty) print("break")
- else print("break ", label.get)
-
- case Continue(label) =>
- if (label.isEmpty) print("continue")
- else print("continue ", label.get)
-
- case Switch(selector, cases, default) =>
- print("switch (", selector, ") ")
- print("{"); indent
- for ((value, body) <- cases) {
- println()
- print("case ", value, ":"); indent; println()
- printStat(body)
- print(";")
- undent
- }
- if (default != EmptyTree) {
- println()
- print("default:"); indent; println()
- printStat(default)
- print(";")
- undent
- }
- undent; println(); print("}")
-
- case Debugger() =>
- print("debugger")
-
- // Expressions
-
- case New(ctor, args) =>
- def containsOnlySelectsFromAtom(tree: Tree): Boolean = tree match {
- case DotSelect(qual, _) => containsOnlySelectsFromAtom(qual)
- case BracketSelect(qual, _) => containsOnlySelectsFromAtom(qual)
- case VarRef(_, _) => true
- case This() => true
- case _ => false // in particular, Apply
- }
- if (containsOnlySelectsFromAtom(ctor))
- print("new ", ctor)
- else
- print("new (", ctor, ")")
- printArgs(args)
-
- case DotSelect(qualifier, item) =>
- print(qualifier, ".", item)
-
- case BracketSelect(qualifier, item) =>
- print(qualifier, "[", item, "]")
-
- case Apply(fun, args) =>
- print(fun)
- printArgs(args)
-
- case Delete(prop) =>
- print("delete ", prop)
-
- case UnaryOp("typeof", lhs) =>
- print("typeof(", lhs, ")")
-
- case UnaryOp(op, lhs) =>
- print("(", op, lhs, ")")
-
- case BinaryOp(op, lhs, rhs) =>
- print("(", lhs, " ", op, " ", rhs, ")")
-
- case ArrayConstr(items) =>
- printRow(items, "[", ", ", "]")
-
- case ObjectConstr(Nil) =>
- print("{}")
-
- case ObjectConstr(fields) =>
- print("{"); indent; println()
- printSeq(fields) {
- case (name, value) => print(name, ": ", value)
- } { _ =>
- print(",")
- println()
- }
- undent; println(); print("}")
-
- // Literals
-
- case Undefined() =>
- print("(void 0)")
-
- case Null() =>
- print("null")
-
- case BooleanLiteral(value) =>
- print(if (value) "true" else "false")
-
- case IntLiteral(value) =>
- if (value >= 0)
- print(value)
- else
- print("(", value, ")")
-
- case DoubleLiteral(value) =>
- if (value == 0 && 1 / value < 0)
- print("(-0)")
- else if (value >= 0)
- print(value)
- else
- print("(", value, ")")
-
- case StringLiteral(value) =>
- print("\"", escapeJS(value), "\"")
-
- // Atomic expressions
-
- case VarRef(ident, _) =>
- print(ident)
-
- case This() =>
- print("this")
-
- case Function(args, body) =>
- print("(function")
- printSig(args)
- printBlock(body)
- print(")")
-
- case _ =>
- print(s"<error, elem of class ${tree.getClass()}>")
- }
- }
-
- protected def printIdent(ident: Ident): Unit =
- printString(escapeJS(ident.name))
-
- def printOne(arg: Any): Unit = arg match {
- case tree: Tree =>
- printTree(tree, isStat = false)
- case ident: Ident =>
- printIdent(ident)
- case arg =>
- printString(if (arg == null) "null" else arg.toString)
- }
-
- protected def printString(s: String): Unit = {
- out.write(s)
- }
-
- // Make it public
- override def println(): Unit = super.println()
-
- def complete(): Unit = ()
- }
-
- class JSTreePrinterWithSourceMap(_out: Writer,
- sourceMap: SourceMapWriter) extends JSTreePrinter(_out) {
-
- private var column = 0
-
- override def printTree(tree: Tree, isStat: Boolean): Unit = {
- val pos = tree.pos
- if (pos.isDefined)
- sourceMap.startNode(column, pos)
-
- super.printTree(tree, isStat)
-
- if (pos.isDefined)
- sourceMap.endNode(column)
- }
-
- override protected def printIdent(ident: Ident): Unit = {
- if (ident.pos.isDefined)
- sourceMap.startNode(column, ident.pos, ident.originalName)
- super.printIdent(ident)
- if (ident.pos.isDefined)
- sourceMap.endNode(column)
- }
-
- override def println(): Unit = {
- super.println()
- sourceMap.nextLine()
- column = this.indentMargin
- }
-
- override protected def printString(s: String): Unit = {
- // assume no EOL char in s, and assume s only has ASCII characters
- super.printString(s)
- column += s.length()
- }
-
- override def complete(): Unit = {
- sourceMap.complete()
- super.complete()
- }
- }
-
- /** Prints a tree to find original locations based on line numbers.
- * @param untilLine last 0-based line the positions should be recorded for
- */
- class ReverseSourceMapPrinter(untilLine: Int)
- extends JSTreePrinter(ReverseSourceMapPrinter.NullWriter) {
-
- private val positions = Array.fill(untilLine+1)(NoPosition)
- private var curLine = 0
-
- private val doneBreak = new Breaks
-
- def apply(x: Int): Position = positions(x)
-
- def reverseSourceMap(tree: Tree): Unit = doneBreak.breakable {
- printTopLevelTree(tree)
- }
-
- override def printTree(tree: Tree, isStat: Boolean): Unit = {
- if (positions(curLine).isEmpty)
- positions(curLine) = tree.pos
-
- super.printTree(tree, isStat)
- }
-
- override protected def printIdent(ident: Ident): Unit = {
- if (positions(curLine).isEmpty)
- positions(curLine) = ident.pos
-
- super.printIdent(ident)
- }
-
- override def println(): Unit = {
- super.println()
- curLine += 1
- if (curLine > untilLine)
- doneBreak.break()
- }
-
- override protected def printString(s: String): Unit = {
- // assume no EOL char in s, and assume s only has ASCII characters
- // therefore, we fully ignore the string
- }
- }
-
- object ReverseSourceMapPrinter {
- private object NullWriter extends Writer {
- def close(): Unit = ()
- def flush(): Unit = ()
- def write(buf: Array[Char], off: Int, len: Int): Unit = ()
- }
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala
deleted file mode 100644
index b249f88..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala
+++ /dev/null
@@ -1,569 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-import scala.scalajs.ir._
-import Position._
-import Transformers._
-import scala.scalajs.ir.Trees._
-import Types._
-
-import scala.scalajs.tools.sem._
-import CheckedBehavior.Unchecked
-
-import scala.scalajs.tools.javascript.{Trees => js}
-
-/** Defines methods to emit Scala.js classes to JavaScript code.
- * The results are completely desugared.
- */
-final class ScalaJSClassEmitter(semantics: Semantics) {
-
- import JSDesugaring._
-
- /** Desugar a Scala.js class into ECMAScript 5 constructs */
- def genClassDef(tree: ClassDef): js.Tree = {
- implicit val pos = tree.pos
- val kind = tree.kind
-
- var reverseParts: List[js.Tree] = Nil
-
- if (kind == ClassKind.TraitImpl) {
- reverseParts ::= genTraitImpl(tree)
- } else {
- if (kind.isClass)
- reverseParts ::= genClass(tree)
- if (kind.isClass || kind == ClassKind.Interface ||
- tree.name.name == Definitions.StringClass)
- reverseParts ::= genInstanceTests(tree)
- reverseParts ::= genArrayInstanceTests(tree)
- reverseParts ::= genTypeData(tree)
- if (kind.isClass)
- reverseParts ::= genSetTypeData(tree)
- if (kind == ClassKind.ModuleClass)
- reverseParts ::= genModuleAccessor(tree)
- if (kind.isClass)
- reverseParts ::= genClassExports(tree)
- }
-
- js.Block(reverseParts.reverse)
- }
-
- def genClass(tree: ClassDef): js.Tree = {
- val className = tree.name.name
- val typeFunctionDef = genConstructor(tree)
- val memberDefs = tree.defs collect {
- case m: MethodDef =>
- genMethod(className, m)
- case p: PropertyDef =>
- genProperty(className, p)
- }
-
- js.Block(typeFunctionDef :: memberDefs)(tree.pos)
- }
-
- /** Generates the JS constructor for a class. */
- def genConstructor(tree: ClassDef): js.Tree = {
- assert(tree.kind.isClass)
-
- val classIdent = tree.name
- val className = classIdent.name
- val tpe = ClassType(className)
-
- assert(tree.parent.isDefined || className == Definitions.ObjectClass,
- "Class $className is missing a parent class")
-
- val ctorFun = {
- val superCtorCall = tree.parent.fold[js.Tree] {
- js.Skip()(tree.pos)
- } { parentIdent =>
- implicit val pos = tree.pos
- js.Apply(
- js.DotSelect(encodeClassVar(parentIdent.name), js.Ident("call")),
- List(js.This()))
- }
- val fieldDefs = for {
- field @ VarDef(name, vtpe, mutable, rhs) <- tree.defs
- } yield {
- implicit val pos = field.pos
- desugarJavaScript(
- Assign(Select(This()(tpe), name, mutable)(vtpe), rhs),
- semantics)
- }
- js.Function(Nil,
- js.Block(superCtorCall :: fieldDefs)(tree.pos))(tree.pos)
- }
-
- {
- implicit val pos = tree.pos
- val typeVar = encodeClassVar(className)
- val docComment = js.DocComment("@constructor")
- val ctorDef = js.Assign(typeVar, ctorFun)
-
- val chainProto = tree.parent.fold[js.Tree] {
- js.Skip()
- } { parentIdent =>
- js.Block(
- js.Assign(typeVar.prototype,
- js.New(js.DotSelect(envField("h"), parentIdent), Nil)),
- genAddToPrototype(className, js.Ident("constructor"), typeVar)
- )
- }
-
- val inheritableCtorDef = {
- val inheritableCtorVar =
- js.DotSelect(envField("h"), classIdent)
- js.Block(
- js.DocComment("@constructor"),
- js.Assign(inheritableCtorVar, js.Function(Nil, js.Skip())),
- js.Assign(inheritableCtorVar.prototype, typeVar.prototype)
- )
- }
-
- js.Block(docComment, ctorDef, chainProto, inheritableCtorDef)
- }
- }
-
- /** Generates a method. */
- def genMethod(className: String, method: MethodDef): js.Tree = {
- implicit val pos = method.pos
- val methodFun = js.Function(method.args.map(transformParamDef),
- desugarBody(method.body, method.resultType == NoType))
- genAddToPrototype(className, method.name, methodFun)
- }
-
- /** Generates a property. */
- def genProperty(className: String, property: PropertyDef): js.Tree = {
- implicit val pos = property.pos
- val classType = ClassType(className)
-
- // defineProperty method
- val defProp =
- js.BracketSelect(js.VarRef(js.Ident("Object"), false),
- js.StringLiteral("defineProperty"))
-
- // class prototype
- val proto = encodeClassVar(className).prototype
-
- // property name
- val name = property.name match {
- case StringLiteral(value) =>
- js.StringLiteral(value)
- case id: Ident =>
- // We need to work around the closure compiler. Call propertyName to
- // get a string representation of the optimized name
- genCallHelper("propertyName",
- js.ObjectConstr(transformIdent(id) -> js.IntLiteral(0) :: Nil))
- }
-
- // Options passed to the defineProperty method
- val descriptor = js.ObjectConstr {
- // Basic config
- val base =
- js.StringLiteral("enumerable") -> js.BooleanLiteral(true) :: Nil
-
- // Optionally add getter
- val wget =
- if (property.getterBody == EmptyTree) base
- else js.StringLiteral("get") ->
- js.Function(Nil, desugarBody(property.getterBody, isStat = false)) :: base
-
- // Optionally add setter
- if (property.setterBody == EmptyTree) wget
- else js.StringLiteral("set") ->
- js.Function(transformParamDef(property.setterArg) :: Nil,
- desugarBody(property.setterBody, isStat = true)) :: wget
- }
-
- js.Apply(defProp, proto :: name :: descriptor :: Nil)
- }
-
- /** Generate `classVar.prototype.name = value` */
- def genAddToPrototype(className: String, name: js.PropertyName,
- value: js.Tree)(implicit pos: Position): js.Tree = {
- val proto = encodeClassVar(className).prototype
- val select = name match {
- case name: js.Ident => js.DotSelect(proto, name)
- case name: js.StringLiteral => js.BracketSelect(proto, name)
- }
- js.Assign(select, value)
- }
-
- /** Generate `classVar.prototype.name = value` */
- def genAddToPrototype(className: String, name: PropertyName,
- value: js.Tree)(implicit pos: Position): js.Tree = {
- val newName = name match {
- case ident: Ident => transformIdent(ident)
- case StringLiteral(value) => js.StringLiteral(value)
- }
- genAddToPrototype(className, newName, value)
- }
-
- def genInstanceTests(tree: ClassDef): js.Tree = {
- import Definitions._
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- val classIdent = transformIdent(tree.name)
- val className = classIdent.name
- val displayName = decodeClassName(className)
-
- val isAncestorOfString =
- AncestorsOfStringClass.contains(className)
- val isAncestorOfHijackedNumberClass =
- AncestorsOfHijackedNumberClasses.contains(className)
- val isAncestorOfBoxedBooleanClass =
- AncestorsOfBoxedBooleanClass.contains(className)
-
- val objParam = js.ParamDef(Ident("obj"), mutable = false)
- val obj = objParam.ref
-
- val createIsStat = {
- envField("is") DOT classIdent :=
- js.Function(List(objParam), js.Return(className match {
- case Definitions.ObjectClass =>
- js.BinaryOp("!==", obj, js.Null())
-
- case Definitions.StringClass =>
- js.UnaryOp("typeof", obj) === js.StringLiteral("string")
-
- case _ =>
- var test = (obj && (obj DOT "$classData") &&
- (obj DOT "$classData" DOT "ancestors" DOT classIdent))
-
- if (isAncestorOfString)
- test = test || (
- js.UnaryOp("typeof", obj) === js.StringLiteral("string"))
- if (isAncestorOfHijackedNumberClass)
- test = test || (
- js.UnaryOp("typeof", obj) === js.StringLiteral("number"))
- if (isAncestorOfBoxedBooleanClass)
- test = test || (
- js.UnaryOp("typeof", obj) === js.StringLiteral("boolean"))
-
- !(!test)
- }))
- }
-
- val createAsStat = if (semantics.asInstanceOfs == Unchecked) {
- js.Skip()
- } else {
- envField("as") DOT classIdent :=
- js.Function(List(objParam), js.Return(className match {
- case Definitions.ObjectClass =>
- obj
-
- case _ =>
- js.If(js.Apply(envField("is") DOT classIdent, List(obj)) ||
- (obj === js.Null()), {
- obj
- }, {
- genCallHelper("throwClassCastException",
- obj, js.StringLiteral(displayName))
- })
- }))
- }
-
- js.Block(createIsStat, createAsStat)
- }
-
- def genArrayInstanceTests(tree: ClassDef): js.Tree = {
- import Definitions._
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- val classIdent = transformIdent(tree.name)
- val className = classIdent.name
- val displayName = decodeClassName(className)
-
- val objParam = js.ParamDef(Ident("obj"), mutable = false)
- val obj = objParam.ref
-
- val depthParam = js.ParamDef(Ident("depth"), mutable = false)
- val depth = depthParam.ref
-
- val createIsArrayOfStat = {
- envField("isArrayOf") DOT classIdent :=
- js.Function(List(objParam, depthParam), className match {
- case Definitions.ObjectClass =>
- val dataVarDef = js.VarDef(Ident("data"), false, {
- obj && (obj DOT "$classData")
- })
- val data = dataVarDef.ref
- js.Block(
- dataVarDef,
- js.If(!data, {
- js.Return(js.BooleanLiteral(false))
- }, {
- val arrayDepthVarDef = js.VarDef(Ident("arrayDepth"), false, {
- (data DOT "arrayDepth") || js.IntLiteral(0)
- })
- val arrayDepth = arrayDepthVarDef.ref
- js.Block(
- arrayDepthVarDef,
- js.Return {
- // Array[A] </: Array[Array[A]]
- !js.BinaryOp("<", arrayDepth, depth) && (
- // Array[Array[A]] <: Array[Object]
- js.BinaryOp(">", arrayDepth, depth) ||
- // Array[Int] </: Array[Object]
- !js.BracketSelect(data DOT "arrayBase", js.StringLiteral("isPrimitive"))
- )
- })
- }))
-
- case _ =>
- js.Return(!(!(obj && (obj DOT "$classData") &&
- ((obj DOT "$classData" DOT "arrayDepth") === depth) &&
- (obj DOT "$classData" DOT "arrayBase" DOT "ancestors" DOT classIdent))))
- })
- }
-
- val createAsArrayOfStat = if (semantics.asInstanceOfs == Unchecked) {
- js.Skip()
- } else {
- envField("asArrayOf") DOT classIdent :=
- js.Function(List(objParam, depthParam), js.Return {
- js.If(js.Apply(envField("isArrayOf") DOT classIdent, List(obj, depth)) ||
- (obj === js.Null()), {
- obj
- }, {
- genCallHelper("throwArrayCastException",
- obj, js.StringLiteral("L"+displayName+";"), depth)
- })
- })
- }
-
- js.Block(createIsArrayOfStat, createAsArrayOfStat)
- }
-
- def genTypeData(tree: ClassDef): js.Tree = {
- import Definitions._
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- val classIdent = transformIdent(tree.name)
- val className = classIdent.name
- val kind = tree.kind
- assert(kind.isType)
-
- val isObjectClass =
- className == ObjectClass
- val isHijackedBoxedClass =
- HijackedBoxedClasses.contains(className)
- val isAncestorOfHijackedClass =
- AncestorsOfHijackedClasses.contains(className)
-
- val parentData = tree.parent.fold[js.Tree] {
- if (isObjectClass) js.Null()
- else js.Undefined()
- } { parent =>
- envField("d") DOT parent
- }
-
- val ancestorsRecord = js.ObjectConstr(
- for (ancestor <- classIdent :: tree.ancestors.map(transformIdent))
- yield (ancestor, js.IntLiteral(1)))
-
- val typeData = js.New(envField("ClassTypeData"), List(
- js.ObjectConstr(List(classIdent -> js.IntLiteral(0))),
- js.BooleanLiteral(kind == ClassKind.Interface),
- js.StringLiteral(decodeClassName(className)),
- parentData,
- ancestorsRecord
- ) ++ (
- // Optional parameter isInstance
- if (isObjectClass) {
- /* Object has special ScalaJS.is.O *and* ScalaJS.isArrayOf.O. */
- List(
- envField("is") DOT classIdent,
- envField("isArrayOf") DOT classIdent)
- } else if (isHijackedBoxedClass) {
- /* Hijacked boxed classes have a special isInstanceOf test. */
- val xParam = js.ParamDef(Ident("x"), mutable = false)
- List(js.Function(List(xParam), js.Return {
- genIsInstanceOf(xParam.ref, ClassType(className))
- }))
- } else if (isAncestorOfHijackedClass || className == StringClass) {
- /* java.lang.String and ancestors of hijacked classes have a normal
- * ScalaJS.is.pack_Class test but with a non-standard behavior. */
- List(envField("is") DOT classIdent)
- } else {
- // For other classes, the isInstance function can be inferred.
- Nil
- }
- ))
-
- envField("d") DOT classIdent := typeData
- }
-
- def genSetTypeData(tree: ClassDef): js.Tree = {
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- assert(tree.kind.isClass)
-
- encodeClassVar(tree.name.name).prototype DOT "$classData" :=
- envField("d") DOT tree.name
- }
-
- def genModuleAccessor(tree: ClassDef): js.Tree = {
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- val classIdent = transformIdent(tree.name)
- val className = classIdent.name
- val tpe = ClassType(className)
-
- require(tree.kind == ClassKind.ModuleClass,
- s"genModuleAccessor called with non-module class: $className")
- assert(className.endsWith("$"))
-
- val moduleName = className.dropRight(1)
- val moduleIdent = Ident(moduleName)
-
- val moduleInstanceVar = envField("n") DOT moduleIdent
- val accessorVar = envField("m") DOT moduleIdent
-
- val createModuleInstanceField = {
- moduleInstanceVar := js.Undefined()
- }
-
- val createAccessor = {
- accessorVar := js.Function(Nil, js.Block(
- js.If(!(moduleInstanceVar), {
- moduleInstanceVar :=
- js.Apply(js.New(encodeClassVar(className), Nil) DOT js.Ident("init___"), Nil)
- }, js.Skip()),
- js.Return(moduleInstanceVar)
- ))
- }
-
- js.Block(createModuleInstanceField, createAccessor)
- }
-
- def genClassExports(tree: ClassDef): js.Tree = {
- val exports = tree.defs collect {
- case e: ConstructorExportDef =>
- genConstructorExportDef(tree, e)
- case e: ModuleExportDef =>
- genModuleExportDef(tree, e)
- }
-
- js.Block(exports)(tree.pos)
- }
-
- def genConstructorExportDef(cd: ClassDef, tree: ConstructorExportDef): js.Tree = {
- import TreeDSL._
-
- implicit val pos = tree.pos
- val classType = ClassType(cd.name.name)
- val ConstructorExportDef(fullName, args, body) = tree
-
- val baseCtor = envField("c") DOT cd.name
- val (createNamespace, expCtorVar) = genCreateNamespaceInExports(fullName)
-
- js.Block(
- createNamespace,
- js.DocComment("@constructor"),
- expCtorVar := js.Function(args.map(transformParamDef), js.Block(
- js.Apply(js.DotSelect(baseCtor, js.Ident("call")), List(js.This())),
- desugarBody(body, isStat = true)
- )),
- expCtorVar DOT "prototype" := baseCtor DOT "prototype"
- )
- }
-
- def genModuleExportDef(cd: ClassDef, tree: ModuleExportDef): js.Tree = {
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- val baseAccessor =
- envField("m") DOT cd.name.name.dropRight(1)
- val (createNamespace, expAccessorVar) =
- genCreateNamespaceInExports(tree.fullName)
-
- js.Block(
- createNamespace,
- expAccessorVar := baseAccessor
- )
- }
-
- def genTraitImpl(tree: ClassDef): js.Tree = {
- val traitImplName = tree.name.name
- val defs = tree.defs collect {
- case m: MethodDef =>
- genTraitImplMethod(traitImplName, m)
- }
- js.Block(defs)(tree.pos)
- }
-
- def genTraitImplMethod(traitImplName: String, tree: MethodDef): js.Tree = {
- implicit val pos = tree.pos
- val MethodDef(name: Ident, args, resultType, body) = tree
- js.Assign(
- js.DotSelect(envField("i"), name),
- js.Function(args.map(transformParamDef),
- desugarBody(body, resultType == NoType)))
- }
-
- /** Generate a dummy parent. Used by ScalaJSOptimizer */
- def genDummyParent(name: String): js.Tree = {
- implicit val pos = Position.NoPosition
-
- js.Block(
- js.DocComment("@constructor (dummy parent)"))
- js.Assign(js.DotSelect(envField("h"), js.Ident(name)),
- js.Function(Nil, js.Skip())
- )
- }
-
- // Helpers
-
- /** Desugars a function body of the IR into ES5 JavaScript. */
- private def desugarBody(tree: Tree, isStat: Boolean): js.Tree = {
- implicit val pos = tree.pos
- val withReturn =
- if (isStat) tree
- else Return(tree)
- desugarJavaScript(withReturn, semantics) match {
- case js.Block(stats :+ js.Return(js.Undefined())) => js.Block(stats)
- case other => other
- }
- }
-
- /** Gen JS code for assigning an rhs to a qualified name in the exports scope.
- * For example, given the qualified name "foo.bar.Something", generates:
- *
- * ScalaJS.e["foo"] = ScalaJS.e["foo"] || {};
- * ScalaJS.e["foo"]["bar"] = ScalaJS.e["foo"]["bar"] || {};
- *
- * Returns (statements, ScalaJS.e["foo"]["bar"]["Something"])
- */
- private def genCreateNamespaceInExports(qualName: String)(
- implicit pos: Position): (js.Tree, js.Tree) = {
- val parts = qualName.split("\\.")
- val statements = List.newBuilder[js.Tree]
- var namespace = envField("e")
- for (i <- 0 until parts.length-1) {
- namespace = js.BracketSelect(namespace, js.StringLiteral(parts(i)))
- statements +=
- js.Assign(namespace, js.BinaryOp("||", namespace, js.ObjectConstr(Nil)))
- }
- val lhs = js.BracketSelect(namespace, js.StringLiteral(parts.last))
- (js.Block(statements.result()), lhs)
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala
deleted file mode 100644
index 3ac54d8..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala
+++ /dev/null
@@ -1,50 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-import scala.language.implicitConversions
-
-import scala.scalajs.ir.Position
-
-import Trees._
-
-private[javascript] object TreeDSL {
- implicit class TreeOps(val self: Tree) extends AnyVal {
- /** Select a member */
- def DOT(field: Ident)(implicit pos: Position): DotSelect =
- DotSelect(self, field)
-
- /** Select a member */
- def DOT(field: String)(implicit pos: Position): DotSelect =
- DotSelect(self, Ident(field))
-
- // Some operators that we use
-
- def ===(that: Tree)(implicit pos: Position): Tree =
- BinaryOp("===", self, that)
- def ===(that: String)(implicit pos: Position): Tree =
- BinaryOp("===", self, StringLiteral(that))
-
- def unary_!()(implicit pos: Position): Tree =
- UnaryOp("!", self)
- def &&(that: Tree)(implicit pos: Position): Tree =
- BinaryOp("&&", self, that)
- def ||(that: Tree)(implicit pos: Position): Tree =
- BinaryOp("||", self, that)
-
- // Other constructs
-
- def :=(that: Tree)(implicit pos: Position): Tree =
- Assign(self, that)
- }
-
- def typeof(expr: Tree)(implicit pos: Position): Tree =
- UnaryOp("typeof", expr)
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala
deleted file mode 100644
index 0b86d1b..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala
+++ /dev/null
@@ -1,194 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-import scala.annotation.switch
-
-import scala.scalajs.ir
-import ir.Position
-import ir.Position.NoPosition
-
-object Trees {
- import ir.Trees.requireValidIdent
-
- /** AST node of JavaScript. */
- abstract sealed class Tree {
- val pos: Position
-
- def show: String = {
- val writer = new java.io.StringWriter
- val printer = new Printers.JSTreePrinter(writer)
- printer.printTree(this, isStat = true)
- writer.toString()
- }
- }
-
- case object EmptyTree extends Tree {
- val pos = NoPosition
- }
-
- // Comments
-
- case class DocComment(text: String)(implicit val pos: Position) extends Tree
-
- // Identifiers and properties
-
- sealed trait PropertyName {
- def name: String
- def pos: Position
- }
-
- case class Ident(name: String, originalName: Option[String])(
- implicit val pos: Position) extends PropertyName {
- requireValidIdent(name)
- }
-
- object Ident {
- def apply(name: String)(implicit pos: Position): Ident =
- new Ident(name, Some(name))
- }
-
- // Definitions
-
- case class VarDef(name: Ident, mutable: Boolean, rhs: Tree)(implicit val pos: Position) extends Tree {
- def ref(implicit pos: Position): Tree =
- VarRef(name, mutable = mutable)
- }
-
- case class ParamDef(name: Ident, mutable: Boolean)(implicit val pos: Position) extends Tree {
- def ref(implicit pos: Position): Tree =
- VarRef(name, mutable = mutable)
- }
-
- // Control flow constructs
-
- case class Skip()(implicit val pos: Position) extends Tree
-
- class Block private (val stats: List[Tree])(implicit val pos: Position) extends Tree {
- override def toString(): String =
- stats.mkString("Block(", ",", ")")
- }
-
- object Block {
- def apply(stats: List[Tree])(implicit pos: Position): Tree = {
- val flattenedStats = stats flatMap {
- case Skip() => Nil
- case Block(subStats) => subStats
- case other => other :: Nil
- }
- flattenedStats match {
- case Nil => Skip()
- case only :: Nil => only
- case _ => new Block(flattenedStats)
- }
- }
-
- def apply(stats: Tree*)(implicit pos: Position): Tree =
- apply(stats.toList)
-
- def unapply(block: Block): Some[List[Tree]] = Some(block.stats)
- }
-
- case class Labeled(label: Ident, body: Tree)(implicit val pos: Position) extends Tree
-
- case class Assign(lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree {
- require(lhs match {
- case _:VarRef | _:DotSelect | _:BracketSelect => true
- case _ => false
- }, s"Invalid lhs for Assign: $lhs")
- }
-
- case class Return(expr: Tree)(implicit val pos: Position) extends Tree
-
- case class If(cond: Tree, thenp: Tree, elsep: Tree)(implicit val pos: Position) extends Tree
-
- case class While(cond: Tree, body: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree
-
- case class DoWhile(body: Tree, cond: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree
-
- case class Try(block: Tree, errVar: Ident, handler: Tree, finalizer: Tree)(implicit val pos: Position) extends Tree
-
- case class Throw(expr: Tree)(implicit val pos: Position) extends Tree
-
- case class Break(label: Option[Ident] = None)(implicit val pos: Position) extends Tree
-
- case class Continue(label: Option[Ident] = None)(implicit val pos: Position) extends Tree
-
- case class Switch(selector: Tree, cases: List[(Tree, Tree)], default: Tree)(implicit val pos: Position) extends Tree
-
- case class Debugger()(implicit val pos: Position) extends Tree
-
- // Expressions
-
- case class New(ctor: Tree, args: List[Tree])(implicit val pos: Position) extends Tree
-
- case class DotSelect(qualifier: Tree, item: Ident)(implicit val pos: Position) extends Tree
-
- case class BracketSelect(qualifier: Tree, item: Tree)(implicit val pos: Position) extends Tree
-
- /** Syntactic apply.
- * It is a method call if fun is a dot-select or bracket-select. It is a
- * function call otherwise.
- */
- case class Apply(fun: Tree, args: List[Tree])(implicit val pos: Position) extends Tree
-
- case class Delete(prop: Tree)(implicit val pos: Position) extends Tree {
- require(prop match {
- case _:DotSelect | _:BracketSelect => true
- case _ => false
- }, s"Invalid prop for Delete: $prop")
- }
-
- /** Unary operation (always preserves pureness).
- *
- * Operations which do not preserve pureness are not allowed in this tree.
- * These are notably ++ and --
- */
- case class UnaryOp(op: String, lhs: Tree)(implicit val pos: Position) extends Tree
-
- /** Binary operation (always preserves pureness).
- *
- * Operations which do not preserve pureness are not allowed in this tree.
- * These are notably +=, -=, *=, /= and %=
- */
- case class BinaryOp(op: String, lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree
-
- case class ArrayConstr(items: List[Tree])(implicit val pos: Position) extends Tree
-
- case class ObjectConstr(fields: List[(PropertyName, Tree)])(implicit val pos: Position) extends Tree
-
- // Literals
-
- /** Marker for literals. Literals are always pure. */
- sealed trait Literal extends Tree
-
- case class Undefined()(implicit val pos: Position) extends Literal
-
- case class Null()(implicit val pos: Position) extends Literal
-
- case class BooleanLiteral(value: Boolean)(implicit val pos: Position) extends Literal
-
- case class IntLiteral(value: Int)(implicit val pos: Position) extends Literal
-
- case class DoubleLiteral(value: Double)(implicit val pos: Position) extends Literal
-
- case class StringLiteral(value: String)(
- implicit val pos: Position) extends Literal with PropertyName {
- override def name = value
- }
-
- // Atomic expressions
-
- case class VarRef(ident: Ident, mutable: Boolean)(implicit val pos: Position) extends Tree
-
- case class This()(implicit val pos: Position) extends Tree
-
- case class Function(args: List[ParamDef], body: Tree)(implicit val pos: Position) extends Tree
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Exceptions.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Exceptions.scala
deleted file mode 100644
index dd7f635..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Exceptions.scala
+++ /dev/null
@@ -1,59 +0,0 @@
-package scala.scalajs.tools.jsdep
-
-abstract class DependencyException(msg: String) extends Exception(msg)
-
-class MissingDependencyException(
- val originatingLib: FlatJSDependency,
- val missingLib: String
-) extends DependencyException(
- s"The JS dependency ${originatingLib.resourceName} declared " +
- s"from ${originatingLib.origin} has an unmet transitive " +
- s"dependency $missingLib")
-
-class CyclicDependencyException(
- val participants: List[ResolutionInfo]
-) extends DependencyException(
- CyclicDependencyException.mkMsg(participants))
-
-object CyclicDependencyException {
- private def mkMsg(parts: List[ResolutionInfo]) = {
- val lookup = parts.map(p => (p.resourceName, p)).toMap
-
- val msg = new StringBuilder()
- msg.append("There is a loop in the following JS dependencies:\n")
-
- def str(info: ResolutionInfo) =
- s"${info.resourceName} from: ${info.origins.mkString(", ")}"
-
- for (dep <- parts) {
- msg.append(s" ${str(dep)} which depends on\n")
- for (name <- dep.dependencies) {
- val rdep = lookup(name)
- msg.append(s" - ${str(rdep)}\n")
- }
- }
-
- msg.toString()
- }
-}
-
-class ConflictingNameException(
- val participants: List[FlatJSDependency]
-) extends DependencyException(
- ConflictingNameException.mkMsg(participants))
-
-object ConflictingNameException {
- private def mkMsg(parts: List[FlatJSDependency]) = {
- val resName = parts.head.resourceName
-
- val msg = new StringBuilder()
- msg.append(s"Name conflicts in:\n")
-
- for (p <- parts) {
- msg.append(p)
- msg.append('\n')
- }
-
- sys.error(msg.toString())
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/FlatJSDependency.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/FlatJSDependency.scala
deleted file mode 100644
index 0c55e88..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/FlatJSDependency.scala
+++ /dev/null
@@ -1,17 +0,0 @@
-package scala.scalajs.tools.jsdep
-
-import scala.scalajs.ir.Trees.isValidIdentifier
-
-/** The same as a [[JSDependency]] but containing the origin from the containing
- * JSDependencyManifest. This class is used for filtering of dependencies.
- */
-final class FlatJSDependency(
- val origin: Origin,
- val resourceName: String,
- val dependencies: List[String] = Nil,
- val commonJSName: Option[String] = None) {
-
- require(commonJSName.forall(isValidIdentifier),
- "commonJSName must be a valid JavaScript identifier")
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependency.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependency.scala
deleted file mode 100644
index 2e6f8d1..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependency.scala
+++ /dev/null
@@ -1,66 +0,0 @@
-package scala.scalajs.tools.jsdep
-
-import scala.scalajs.tools.json._
-
-import scala.scalajs.ir.Trees.isValidIdentifier
-
-/** Expresses a dependency on a raw JS library and the JS libraries this library
- * itself depends on.
- *
- * Both the [[resourceName]] and each element of [[dependencies]] is the
- * unqualified filename of the library (e.g. "jquery.js").
- *
- * @param resourceName Filename of the JavaScript file to include
- * (e.g. "jquery.js")
- * @param dependencies Filenames of JavaScript files that must be included
- * before this JavaScript file.
- * @param commonJSName A JavaScript variable name this dependency should be
- * required in a commonJS environment (n.b. Node.js). Should only be set if
- * the JavaScript library will register its exports.
- */
-final class JSDependency(
- val resourceName: String,
- val dependencies: List[String] = Nil,
- val commonJSName: Option[String] = None) {
-
- require(commonJSName.forall(isValidIdentifier),
- "commonJSName must be a valid JavaScript identifier")
-
- def dependsOn(names: String*): JSDependency =
- copy(dependencies = dependencies ++ names)
- def commonJSName(name: String): JSDependency =
- copy(commonJSName = Some(name))
- def withOrigin(origin: Origin): FlatJSDependency =
- new FlatJSDependency(origin, resourceName, dependencies, commonJSName)
-
- private def copy(
- resourceName: String = this.resourceName,
- dependencies: List[String] = this.dependencies,
- commonJSName: Option[String] = this.commonJSName) = {
- new JSDependency(resourceName, dependencies, commonJSName)
- }
-}
-
-object JSDependency {
-
- implicit object JSDepJSONSerializer extends JSONSerializer[JSDependency] {
- def serialize(x: JSDependency): JSON = {
- new JSONObjBuilder()
- .fld("resourceName", x.resourceName)
- .opt("dependencies",
- if (x.dependencies.nonEmpty) Some(x.dependencies) else None)
- .opt("commonJSName", x.commonJSName)
- .toJSON
- }
- }
-
- implicit object JSDepJSONDeserializer extends JSONDeserializer[JSDependency] {
- def deserialize(x: JSON): JSDependency = {
- val obj = new JSONObjExtractor(x)
- new JSDependency(
- obj.fld[String] ("resourceName"),
- obj.opt[List[String]]("dependencies").getOrElse(Nil),
- obj.opt[String] ("commonJSName"))
- }
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala
deleted file mode 100644
index 24491b4..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/JSDependencyManifest.scala
+++ /dev/null
@@ -1,130 +0,0 @@
-package scala.scalajs.tools.jsdep
-
-import scala.scalajs.tools.json._
-import scala.scalajs.tools.io._
-
-import scala.collection.immutable.{Seq, Traversable}
-
-import java.io.{Reader, Writer}
-
-/** The information written to a "JS_DEPENDENCIES" manifest file. */
-final class JSDependencyManifest(
- val origin: Origin,
- val libDeps: List[JSDependency],
- val requiresDOM: Boolean,
- val compliantSemantics: List[String]) {
- def flatten: List[FlatJSDependency] = libDeps.map(_.withOrigin(origin))
-}
-
-object JSDependencyManifest {
-
- final val ManifestFileName = "JS_DEPENDENCIES"
-
- def createIncludeList(
- flatDeps: Traversable[FlatJSDependency]): List[ResolutionInfo] = {
- val jsDeps = mergeManifests(flatDeps)
-
- // Verify all dependencies are met
- for {
- lib <- flatDeps
- dep <- lib.dependencies
- if !jsDeps.contains(dep)
- } throw new MissingDependencyException(lib, dep)
-
- // Sort according to dependencies and return
-
- // Very simple O(n²) topological sort for elements assumed to be distinct
- // Copied :( from GenJSExports (but different exception)
- @scala.annotation.tailrec
- def loop(coll: List[ResolutionInfo],
- acc: List[ResolutionInfo]): List[ResolutionInfo] = {
-
- if (coll.isEmpty) acc
- else if (coll.tail.isEmpty) coll.head :: acc
- else {
- val (selected, pending) = coll.partition { x =>
- coll forall { y => (x eq y) || !y.dependencies.contains(x.resourceName) }
- }
-
- if (selected.nonEmpty)
- loop(pending, selected ::: acc)
- else
- throw new CyclicDependencyException(pending)
- }
- }
-
- loop(jsDeps.values.toList, Nil)
- }
-
- /** Merges multiple JSDependencyManifests into a map of map:
- * resourceName -> ResolutionInfo
- */
- private def mergeManifests(flatDeps: Traversable[FlatJSDependency]) = {
- @inline
- def hasConflict(x: FlatJSDependency, y: FlatJSDependency) = (
- x.commonJSName.isDefined &&
- y.commonJSName.isDefined &&
- (x.resourceName == y.resourceName ^
- x.commonJSName == y.commonJSName)
- )
-
- val conflicts = flatDeps.filter(x =>
- flatDeps.exists(y => hasConflict(x,y)))
-
- if (conflicts.nonEmpty)
- throw new ConflictingNameException(conflicts.toList)
-
- flatDeps.groupBy(_.resourceName).mapValues { sameName =>
- new ResolutionInfo(
- resourceName = sameName.head.resourceName,
- dependencies = sameName.flatMap(_.dependencies).toSet,
- origins = sameName.map(_.origin).toList,
- commonJSName = sameName.flatMap(_.commonJSName).headOption
- )
- }
- }
-
- implicit object JSDepManJSONSerializer extends JSONSerializer[JSDependencyManifest] {
- @inline def optList[T](x: List[T]): Option[List[T]] =
- if (x.nonEmpty) Some(x) else None
-
- def serialize(x: JSDependencyManifest): JSON = {
- new JSONObjBuilder()
- .fld("origin", x.origin)
- .opt("libDeps", optList(x.libDeps))
- .opt("requiresDOM", if (x.requiresDOM) Some(true) else None)
- .opt("compliantSemantics", optList(x.compliantSemantics))
- .toJSON
- }
- }
-
- implicit object JSDepManJSONDeserializer extends JSONDeserializer[JSDependencyManifest] {
- def deserialize(x: JSON): JSDependencyManifest = {
- val obj = new JSONObjExtractor(x)
- new JSDependencyManifest(
- obj.fld[Origin] ("origin"),
- obj.opt[List[JSDependency]]("libDeps").getOrElse(Nil),
- obj.opt[Boolean] ("requiresDOM").getOrElse(false),
- obj.opt[List[String]] ("compliantSemantics").getOrElse(Nil))
- }
- }
-
- def write(dep: JSDependencyManifest, output: WritableVirtualTextFile): Unit = {
- val writer = output.contentWriter
- try write(dep, writer)
- finally writer.close()
- }
-
- def write(dep: JSDependencyManifest, writer: Writer): Unit =
- writeJSON(dep.toJSON, writer)
-
- def read(file: VirtualTextFile): JSDependencyManifest = {
- val reader = file.reader
- try read(reader)
- finally reader.close()
- }
-
- def read(reader: Reader): JSDependencyManifest =
- fromJSON[JSDependencyManifest](readJSON(reader))
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Origin.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Origin.scala
deleted file mode 100644
index a2c6b2d..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/Origin.scala
+++ /dev/null
@@ -1,28 +0,0 @@
-package scala.scalajs.tools.jsdep
-
-import scala.scalajs.tools.json._
-
-/** The place a JSDependency originated from */
-final class Origin(val moduleName: String, val configuration: String) {
- override def toString(): String = s"$moduleName:$configuration"
-}
-
-object Origin {
- implicit object OriginJSONSerializer extends JSONSerializer[Origin] {
- def serialize(x: Origin): JSON = {
- new JSONObjBuilder()
- .fld("moduleName", x.moduleName)
- .fld("configuration", x.configuration)
- .toJSON
- }
- }
-
- implicit object OriginDeserializer extends JSONDeserializer[Origin] {
- def deserialize(x: JSON): Origin = {
- val obj = new JSONObjExtractor(x)
- new Origin(
- obj.fld[String]("moduleName"),
- obj.fld[String]("configuration"))
- }
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/ResolutionInfo.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/ResolutionInfo.scala
deleted file mode 100644
index 2aa177e..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/jsdep/ResolutionInfo.scala
+++ /dev/null
@@ -1,21 +0,0 @@
-package scala.scalajs.tools.jsdep
-
-import scala.scalajs.ir.Trees.isValidIdentifier
-
-/** Information about a resolved JSDependency
- *
- * @param resourceName Filename of the JavaScript file
- * @param dependencies Filenames this dependency depends on
- * @param origins Who declared this dependency
- * @param commonJSName Variable name in commonJS environments
- */
-final class ResolutionInfo(
- val resourceName: String,
- val dependencies: Set[String],
- val origins: List[Origin],
- val commonJSName: Option[String]) {
-
- require(commonJSName.forall(isValidIdentifier),
- "commonJSName must be a valid JavaScript identifier")
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/AbstractJSONImpl.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/AbstractJSONImpl.scala
deleted file mode 100644
index ad5d79e..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/AbstractJSONImpl.scala
+++ /dev/null
@@ -1,32 +0,0 @@
-package scala.scalajs.tools.json
-
-import java.io.{Reader, Writer}
-
-/** A JSON implementation. Has a representation type and methods to convert
- * this type to/from primitives, lists and maps.
- *
- * Further, it can write/read a value of this type to a string.
- */
-private[json] trait AbstractJSONImpl {
-
- type Repr
-
- def fromString(x: String): Repr
- def fromNumber(x: Number): Repr
- def fromBoolean(x: Boolean): Repr
- def fromList(x: List[Repr]): Repr
- def fromMap(x: Map[String, Repr]): Repr
-
- def toString(x: Repr): String
- def toNumber(x: Repr): Number
- def toBoolean(x: Repr): Boolean
- def toList(x: Repr): List[Repr]
- def toMap(x: Repr): Map[String, Repr]
-
- def serialize(x: Repr): String
- def serialize(x: Repr, writer: Writer): Unit
-
- def deserialize(str: String): Repr
- def deserialize(reader: Reader): Repr
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONDeserializer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONDeserializer.scala
deleted file mode 100644
index e854e9a..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONDeserializer.scala
+++ /dev/null
@@ -1,30 +0,0 @@
-package scala.scalajs.tools.json
-
-trait JSONDeserializer[T] {
- def deserialize(x: JSON): T
-}
-
-object JSONDeserializer {
-
- implicit object stringJSON extends JSONDeserializer[String] {
- def deserialize(x: JSON): String = Impl.toString(x)
- }
-
- implicit object intJSON extends JSONDeserializer[Int] {
- def deserialize(x: JSON): Int = Impl.toNumber(x).intValue()
- }
-
- implicit object booleanJSON extends JSONDeserializer[Boolean] {
- def deserialize(x: JSON): Boolean = Impl.toBoolean(x)
- }
-
- implicit def listJSON[T : JSONDeserializer] = new JSONDeserializer[List[T]] {
- def deserialize(x: JSON): List[T] = Impl.toList(x).map(fromJSON[T] _)
- }
-
- implicit def mapJSON[V : JSONDeserializer] = new JSONDeserializer[Map[String, V]] {
- def deserialize(x: JSON): Map[String, V] =
- Impl.toMap(x).mapValues(fromJSON[V] _)
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjBuilder.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjBuilder.scala
deleted file mode 100644
index dd98f49..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjBuilder.scala
+++ /dev/null
@@ -1,20 +0,0 @@
-package scala.scalajs.tools.json
-
-import scala.collection.mutable
-
-class JSONObjBuilder {
-
- private val flds = mutable.Map.empty[String, JSON]
-
- def fld[T : JSONSerializer](name: String, v: T): this.type = {
- flds.put(name, v.toJSON)
- this
- }
-
- def opt[T : JSONSerializer](name: String, v: Option[T]): this.type = {
- v.foreach(v => flds.put(name, v.toJSON))
- this
- }
-
- def toJSON: JSON = Impl.fromMap(flds.toMap)
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjExtractor.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjExtractor.scala
deleted file mode 100644
index e49f7e4..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONObjExtractor.scala
+++ /dev/null
@@ -1,13 +0,0 @@
-package scala.scalajs.tools.json
-
-import scala.collection.mutable
-
-class JSONObjExtractor(rawData: JSON) {
- private val data = Impl.toMap(rawData)
-
- def fld[T : JSONDeserializer](name: String): T =
- fromJSON[T](data(name))
-
- def opt[T : JSONDeserializer](name: String): Option[T] =
- data.get(name).map(fromJSON[T] _)
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONSerializer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONSerializer.scala
deleted file mode 100644
index e26c92a..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/JSONSerializer.scala
+++ /dev/null
@@ -1,32 +0,0 @@
-package scala.scalajs.tools.json
-
-trait JSONSerializer[T] {
- def serialize(x: T): JSON
-}
-
-object JSONSerializer {
-
- implicit object stringJSON extends JSONSerializer[String] {
- def serialize(x: String): JSON = Impl.fromString(x)
- }
-
- implicit object intJSON extends JSONSerializer[Int] {
- def serialize(x: Int): JSON = Impl.fromNumber(x)
- }
-
- implicit object booleanJSON extends JSONSerializer[Boolean] {
- def serialize(x: Boolean): JSON = Impl.fromBoolean(x)
- }
-
- implicit def listJSON[T : JSONSerializer] = new JSONSerializer[List[T]] {
- def serialize(x: List[T]): JSON = Impl.fromList(x.map(_.toJSON))
- }
-
- implicit def mapJSON[V : JSONSerializer] = {
- new JSONSerializer[Map[String, V]] {
- def serialize(x: Map[String, V]): JSON =
- Impl.fromMap(x.mapValues(_.toJSON))
- }
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/package.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/package.scala
deleted file mode 100644
index 551893a..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/json/package.scala
+++ /dev/null
@@ -1,26 +0,0 @@
-package scala.scalajs.tools
-
-import java.io.{Reader, Writer}
-
-/** Some type-class lightweight wrappers around simple-json.
- *
- * They allow to write [[xyz.toJSON]] to obtain classes that can be
- * serialized by simple-json and [[fromJSON[T](xyz)]] to get an
- * object back.
- */
-package object json {
- type JSON = Impl.Repr
-
- implicit class JSONPimp[T : JSONSerializer](x: T) {
- def toJSON: JSON = implicitly[JSONSerializer[T]].serialize(x)
- }
-
- def fromJSON[T](x: JSON)(implicit d: JSONDeserializer[T]): T =
- d.deserialize(x)
-
- def writeJSON(x: JSON, writer: Writer): Unit = Impl.serialize(x, writer)
- def jsonToString(x: JSON): String = Impl.serialize(x)
- def readJSON(str: String): JSON = Impl.deserialize(str)
- def readJSON(reader: Reader): JSON = Impl.deserialize(reader)
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Level.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Level.scala
deleted file mode 100644
index fbbf39d..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Level.scala
+++ /dev/null
@@ -1,24 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.logging
-
-import scala.math.Ordered
-
-abstract sealed class Level extends Ordered[Level] { x =>
- protected val order: Int
- def compare(y: Level) = x.order - y.order
-}
-
-object Level {
- case object Error extends Level { protected val order = 4 }
- case object Warn extends Level { protected val order = 3 }
- case object Info extends Level { protected val order = 2 }
- case object Debug extends Level { protected val order = 1 }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Logger.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Logger.scala
deleted file mode 100644
index 3664f51..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/Logger.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.logging
-
-/** Abstract logger for our tools. Designed after sbt's Loggers. */
-trait Logger {
- def log(level: Level, message: => String): Unit
- def success(message: => String): Unit
- def trace(t: => Throwable): Unit
-
- def error(message: => String): Unit = log(Level.Error, message)
- def warn(message: => String): Unit = log(Level.Warn, message)
- def info(message: => String): Unit = log(Level.Info, message)
- def debug(message: => String): Unit = log(Level.Debug, message)
-
- def time(title: String, nanos: Long): Unit =
- debug(s"$title: ${nanos / 1000} us")
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/NullLogger.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/NullLogger.scala
deleted file mode 100644
index 0e36f89..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/NullLogger.scala
+++ /dev/null
@@ -1,7 +0,0 @@
-package scala.scalajs.tools.logging
-
-object NullLogger extends Logger {
- def log(level: Level, message: => String): Unit = {}
- def success(message: => String): Unit = {}
- def trace(t: => Throwable): Unit = {}
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/ScalaConsoleLogger.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/ScalaConsoleLogger.scala
deleted file mode 100644
index e2c9efc..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/logging/ScalaConsoleLogger.scala
+++ /dev/null
@@ -1,15 +0,0 @@
-package scala.scalajs.tools.logging
-
-class ScalaConsoleLogger(minLevel: Level = Level.Debug) extends Logger {
-
- def log(level: Level, message: =>String): Unit = if (level >= minLevel) {
- if (level == Level.Warn || level == Level.Error)
- scala.Console.err.println(message)
- else
- scala.Console.out.println(message)
- }
- def success(message: => String): Unit = info(message)
- def trace(t: => Throwable): Unit =
- // This is error level, so no checking
- t.printStackTrace()
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/Analyzer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/Analyzer.scala
deleted file mode 100644
index 9cdd764..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/Analyzer.scala
+++ /dev/null
@@ -1,587 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.optimizer
-
-import scala.annotation.tailrec
-
-import scala.collection.mutable
-
-import scala.scalajs.ir
-import ir.{ClassKind, Definitions, Infos}
-
-import scala.scalajs.tools.sem._
-import scala.scalajs.tools.javascript.LongImpl
-import scala.scalajs.tools.logging._
-
-import ScalaJSOptimizer._
-
-class Analyzer(logger0: Logger, semantics: Semantics,
- allData: Seq[Infos.ClassInfo], globalWarnEnabled: Boolean,
- isBeforeOptimizer: Boolean) {
- /* Set this to true to debug the DCE analyzer.
- * We don't rely on config to disable 'debug' messages because we want
- * to use 'debug' for displaying more stack trace info that the user can
- * see with the 'last' command.
- */
- val DebugAnalyzer = false
-
- object logger extends Logger {
- var indentation: String = ""
-
- def indent(): Unit = indentation += " "
- def undent(): Unit = indentation = indentation.substring(2)
-
- def log(level: Level, message: => String) =
- logger0.log(level, indentation+message)
- def success(message: => String) =
- logger0.success(indentation+message)
- def trace(t: => Throwable) =
- logger0.trace(t)
-
- def indented[A](body: => A): A = {
- indent()
- try body
- finally undent()
- }
-
- def debugIndent[A](message: => String)(body: => A): A = {
- if (DebugAnalyzer) {
- debug(message)
- indented(body)
- } else {
- body
- }
- }
-
- def temporarilyNotIndented[A](body: => A): A = {
- val savedIndent = indentation
- indentation = ""
- try body
- finally indentation = savedIndent
- }
- }
-
- sealed trait From
- case class FromMethod(methodInfo: MethodInfo) extends From
- case object FromCore extends From
- case object FromExports extends From
- case object FromManual extends From
-
- var allAvailable: Boolean = true
-
- val classInfos: mutable.Map[String, ClassInfo] = {
- val cs = for (classData <- allData)
- yield (classData.encodedName, new ClassInfo(classData))
- mutable.Map.empty[String, ClassInfo] ++ cs
- }
-
- def lookupClass(encodedName: String): ClassInfo = {
- classInfos.get(encodedName) match {
- case Some(info) => info
- case None =>
- val c = new ClassInfo(createMissingClassInfo(encodedName))
- classInfos += encodedName -> c
- c.nonExistent = true
- c.linkClasses()
- c
- }
- }
-
- def lookupModule(encodedName: String): ClassInfo = {
- lookupClass(encodedName+"$")
- }
-
- linkClasses()
-
- def linkClasses(): Unit = {
- if (!classInfos.contains(ir.Definitions.ObjectClass))
- sys.error("Fatal error: could not find java.lang.Object on the classpath")
- for (classInfo <- classInfos.values.toList)
- classInfo.linkClasses()
- }
-
- def computeReachability(manuallyReachable: Seq[ManualReachability],
- noWarnMissing: Seq[NoWarnMissing]): Unit = {
- // Stuff reachable from core symbols always should warn
- reachCoreSymbols()
-
- // Disable warnings as requested
- noWarnMissing.foreach(disableWarning _)
-
- // Reach all user stuff
- manuallyReachable.foreach(reachManually _)
- for (classInfo <- classInfos.values)
- classInfo.reachExports()
- }
-
- /** Reach symbols used directly by scalajsenv.js. */
- def reachCoreSymbols(): Unit = {
- import semantics._
- import CheckedBehavior._
-
- implicit val from = FromCore
-
- def instantiateClassWith(className: String, constructor: String): ClassInfo = {
- val info = lookupClass(className)
- info.instantiated()
- info.callMethod(constructor)
- info
- }
-
- val ObjectClass = instantiateClassWith("O", "init___")
- ObjectClass.callMethod("toString__T")
- ObjectClass.callMethod("equals__O__Z")
-
- instantiateClassWith("jl_NullPointerException", "init___")
-
- if (asInstanceOfs != Unchecked)
- instantiateClassWith("jl_ClassCastException", "init___T")
-
- if (asInstanceOfs == Fatal)
- instantiateClassWith("sjsr_UndefinedBehaviorError", "init___jl_Throwable")
-
- instantiateClassWith("jl_Class", "init___jl_ScalaJSClassData")
-
- val RTStringModuleClass = lookupClass("sjsr_RuntimeString$")
- RTStringModuleClass.accessModule()
- RTStringModuleClass.callMethod("hashCode__T__I")
-
- val RTLongClass = lookupClass(LongImpl.RuntimeLongClass)
- RTLongClass.instantiated()
- for (method <- LongImpl.AllConstructors ++ LongImpl.AllMethods)
- RTLongClass.callMethod(method)
-
- if (isBeforeOptimizer) {
- for (method <- LongImpl.AllIntrinsicMethods)
- RTLongClass.callMethod(method)
- }
-
- val RTLongModuleClass = lookupClass(LongImpl.RuntimeLongModuleClass)
- RTLongModuleClass.accessModule()
- for (method <- LongImpl.AllModuleMethods)
- RTLongModuleClass.callMethod(method)
-
- if (isBeforeOptimizer) {
- for (hijacked <- Definitions.HijackedClasses)
- lookupClass(hijacked).instantiated()
- } else {
- for (hijacked <- Definitions.HijackedClasses)
- lookupClass(hijacked).accessData()
- }
-
- if (semantics.strictFloats) {
- val RuntimePackage = lookupClass("sjsr_package$")
- RuntimePackage.accessModule()
- RuntimePackage.callMethod("froundPolyfill__D__D")
- }
-
- val BitsModuleClass = lookupClass("sjsr_Bits$")
- BitsModuleClass.accessModule()
- BitsModuleClass.callMethod("numberHashCode__D__I")
- }
-
- def reachManually(info: ManualReachability) = {
- implicit val from = FromManual
-
- // Don't lookupClass here, since we don't want to create any
- // symbols. If a symbol doesn't exist, we fail.
- info match {
- case ReachObject(name) => classInfos(name + "$").accessModule()
- case Instantiate(name) => classInfos(name).instantiated()
- case ReachMethod(className, methodName, static) =>
- classInfos(className).callMethod(methodName, static)
- }
- }
-
- def disableWarning(noWarn: NoWarnMissing) = noWarn match {
- case NoWarnClass(className) =>
- lookupClass(className).warnEnabled = false
- case NoWarnMethod(className, methodName) =>
- lookupClass(className).lookupMethod(methodName).warnEnabled = false
- }
-
- class ClassInfo(data: Infos.ClassInfo) {
- val encodedName = data.encodedName
- val ancestorCount = data.ancestorCount
- val isStaticModule = data.kind == ClassKind.ModuleClass
- val isInterface = data.kind == ClassKind.Interface
- val isImplClass = data.kind == ClassKind.TraitImpl
- val isRawJSType = data.kind == ClassKind.RawJSType
- val isHijackedClass = data.kind == ClassKind.HijackedClass
- val isClass = !isInterface && !isImplClass && !isRawJSType
- val isExported = data.isExported
-
- val hasData = !isImplClass
- val hasMoreThanData = isClass && !isHijackedClass
-
- var superClass: ClassInfo = _
- val ancestors = mutable.ListBuffer.empty[ClassInfo]
- val descendants = mutable.ListBuffer.empty[ClassInfo]
-
- var nonExistent: Boolean = false
- var warnEnabled: Boolean = true
-
- def linkClasses(): Unit = {
- if (data.superClass != "")
- superClass = lookupClass(data.superClass)
- ancestors ++= data.ancestors.map(lookupClass)
- for (ancestor <- ancestors)
- ancestor.descendants += this
- }
-
- lazy val descendentClasses = descendants.filter(_.isClass)
-
- def optimizerHints: Infos.OptimizerHints = data.optimizerHints
-
- var isInstantiated: Boolean = false
- var isAnySubclassInstantiated: Boolean = false
- var isModuleAccessed: Boolean = false
- var isDataAccessed: Boolean = false
-
- var instantiatedFrom: Option[From] = None
-
- val delayedCalls = mutable.Map.empty[String, From]
-
- def isNeededAtAll =
- isDataAccessed ||
- isAnySubclassInstantiated ||
- (isImplClass && methodInfos.values.exists(_.isReachable))
-
- lazy val methodInfos: mutable.Map[String, MethodInfo] = {
- val ms = for (methodData <- data.methods)
- yield (methodData.encodedName, new MethodInfo(this, methodData))
- mutable.Map.empty[String, MethodInfo] ++ ms
- }
-
- def lookupMethod(methodName: String): MethodInfo = {
- tryLookupMethod(methodName).getOrElse {
- val syntheticData = createMissingMethodInfo(methodName)
- val m = new MethodInfo(this, syntheticData)
- m.nonExistent = true
- methodInfos += methodName -> m
- m
- }
- }
-
- def tryLookupMethod(methodName: String): Option[MethodInfo] = {
- assert(isClass || isImplClass,
- s"Cannot call lookupMethod($methodName) on non-class $this")
- @tailrec
- def loop(ancestorInfo: ClassInfo): Option[MethodInfo] = {
- if (ancestorInfo ne null) {
- ancestorInfo.methodInfos.get(methodName) match {
- case Some(m) if !m.isAbstract => Some(m)
- case _ => loop(ancestorInfo.superClass)
- }
- } else {
- None
- }
- }
- loop(this)
- }
-
- override def toString(): String = encodedName
-
- /** Start reachability algorithm with the exports for that class. */
- def reachExports(): Unit = {
- implicit val from = FromExports
-
- // Myself
- if (isExported) {
- assert(!isImplClass, "An implementation class must not be exported")
- if (isStaticModule) accessModule()
- else instantiated()
- }
-
- // My methods
- for (methodInfo <- methodInfos.values) {
- if (methodInfo.isExported)
- callMethod(methodInfo.encodedName)
- }
- }
-
- def accessModule()(implicit from: From): Unit = {
- assert(isStaticModule, s"Cannot call accessModule() on non-module $this")
- if (!isModuleAccessed) {
- logger.debugIndent(s"$this.isModuleAccessed = true") {
- isModuleAccessed = true
- instantiated()
- callMethod("init___")
- }
- }
- }
-
- def instantiated()(implicit from: From): Unit = {
- if (!isInstantiated && isClass) {
- logger.debugIndent(s"$this.isInstantiated = true") {
- isInstantiated = true
- instantiatedFrom = Some(from)
- ancestors.foreach(_.subclassInstantiated())
- }
-
- for ((methodName, from) <- delayedCalls)
- delayedCallMethod(methodName)(from)
- }
- }
-
- private def subclassInstantiated()(implicit from: From): Unit = {
- if (!isAnySubclassInstantiated && isClass) {
- logger.debugIndent(s"$this.isAnySubclassInstantiated = true") {
- isAnySubclassInstantiated = true
- if (instantiatedFrom.isEmpty)
- instantiatedFrom = Some(from)
- accessData()
- methodInfos.get("__init__").foreach(_.reachStatic())
- }
- }
- }
-
- def accessData()(implicit from: From): Unit = {
- if (!isDataAccessed && hasData) {
- checkExistent()
- if (DebugAnalyzer)
- logger.debug(s"$this.isDataAccessed = true")
- isDataAccessed = true
- }
- }
-
- def checkExistent()(implicit from: From): Unit = {
- if (nonExistent) {
- if (warnEnabled && globalWarnEnabled) {
- logger.warn(s"Referring to non-existent class $encodedName")
- warnCallStack()
- }
- nonExistent = false
- allAvailable = false
- }
- }
-
- def callMethod(methodName: String, static: Boolean = false)(
- implicit from: From): Unit = {
- logger.debugIndent(s"calling${if (static) " static" else ""} $this.$methodName") {
- if (isImplClass) {
- // methods in impl classes are always implicitly called statically
- lookupMethod(methodName).reachStatic()
- } else if (isConstructorName(methodName)) {
- // constructors are always implicitly called statically
- lookupMethod(methodName).reachStatic()
- } else if (static) {
- assert(!isReflProxyName(methodName),
- s"Trying to call statically refl proxy $this.$methodName")
- lookupMethod(methodName).reachStatic()
- } else {
- for (descendentClass <- descendentClasses) {
- if (descendentClass.isInstantiated)
- descendentClass.delayedCallMethod(methodName)
- else
- descendentClass.delayedCalls += ((methodName, from))
- }
- }
- }
- }
-
- private def delayedCallMethod(methodName: String)(implicit from: From): Unit = {
- if (isReflProxyName(methodName)) {
- tryLookupMethod(methodName).foreach(_.reach(this))
- } else {
- lookupMethod(methodName).reach(this)
- }
- }
- }
-
- class MethodInfo(val owner: ClassInfo, data: Infos.MethodInfo) {
-
- val encodedName = data.encodedName
- val isAbstract = data.isAbstract
- val isExported = data.isExported
- val isReflProxy = isReflProxyName(encodedName)
-
- def optimizerHints: Infos.OptimizerHints = data.optimizerHints
-
- var isReachable: Boolean = false
-
- var calledFrom: Option[From] = None
- var instantiatedSubclass: Option[ClassInfo] = None
-
- var nonExistent: Boolean = false
- var warnEnabled: Boolean = true
-
- override def toString(): String = s"$owner.$encodedName"
-
- def reachStatic()(implicit from: From): Unit = {
- assert(!isAbstract,
- s"Trying to reach statically the abstract method $this")
-
- checkExistent()
-
- if (!isReachable) {
- logger.debugIndent(s"$this.isReachable = true") {
- isReachable = true
- calledFrom = Some(from)
- doReach()
- }
- }
- }
-
- def reach(inClass: ClassInfo)(implicit from: From): Unit = {
- assert(owner.isClass,
- s"Trying to reach dynamically the non-class method $this")
- assert(!isConstructorName(encodedName),
- s"Trying to reach dynamically the constructor $this")
-
- checkExistent()
-
- if (!isReachable) {
- logger.debugIndent(s"$this.isReachable = true") {
- isReachable = true
- calledFrom = Some(from)
- instantiatedSubclass = Some(inClass)
- doReach()
- }
- }
- }
-
- private def checkExistent()(implicit from: From) = {
- if (nonExistent) {
- if (warnEnabled && owner.warnEnabled && globalWarnEnabled) {
- logger.temporarilyNotIndented {
- logger.warn(s"Referring to non-existent method $this")
- warnCallStack()
- }
- }
- allAvailable = false
- }
- }
-
- private[this] def doReach(): Unit = {
- logger.debugIndent(s"$this.doReach()") {
- implicit val from = FromMethod(this)
-
- if (owner.isImplClass)
- owner.checkExistent()
-
- for (moduleName <- data.accessedModules) {
- lookupModule(moduleName).accessModule()
- }
-
- for (className <- data.instantiatedClasses) {
- lookupClass(className).instantiated()
- }
-
- for (className <- data.accessedClassData) {
- lookupClass(className).accessData()
- }
-
- for ((className, methods) <- data.calledMethods) {
- val classInfo = lookupClass(className)
- for (methodName <- methods)
- classInfo.callMethod(methodName)
- }
-
- for ((className, methods) <- data.calledMethodsStatic) {
- val classInfo = lookupClass(className)
- for (methodName <- methods)
- classInfo.callMethod(methodName, static = true)
- }
- }
- }
- }
-
- def isReflProxyName(encodedName: String): Boolean = {
- encodedName.endsWith("__") &&
- (encodedName != "init___") && (encodedName != "__init__")
- }
-
- def isConstructorName(encodedName: String): Boolean =
- encodedName.startsWith("init___") || (encodedName == "__init__")
-
- private def createMissingClassInfo(encodedName: String): Infos.ClassInfo = {
- val kind =
- if (encodedName.endsWith("$")) ClassKind.ModuleClass
- else if (encodedName.endsWith("$class")) ClassKind.TraitImpl
- else ClassKind.Class
- Infos.ClassInfo(
- name = s"<$encodedName>",
- encodedName = encodedName,
- isExported = false,
- ancestorCount = if (kind.isClass) 1 else 0,
- kind = kind,
- superClass = if (kind.isClass) "O" else "",
- ancestors = List(encodedName, "O"),
- methods = List(
- createMissingMethodInfo("__init__"),
- createMissingMethodInfo("init___"))
- )
- }
-
- private def createMissingMethodInfo(encodedName: String,
- isAbstract: Boolean = false): Infos.MethodInfo = {
- Infos.MethodInfo(encodedName = encodedName, isAbstract = isAbstract)
- }
-
- def warnCallStack()(implicit from: From): Unit = {
- val seenInfos = mutable.Set.empty[AnyRef]
-
- def rec(level: Level, optFrom: Option[From],
- verb: String = "called"): Unit = {
- val involvedClasses = new mutable.ListBuffer[ClassInfo]
-
- def onlyOnce(info: AnyRef): Boolean = {
- if (seenInfos.add(info)) {
- true
- } else {
- logger.log(level, " (already seen, not repeating call stack)")
- false
- }
- }
-
- @tailrec
- def loopTrace(optFrom: Option[From], verb: String = "called"): Unit = {
- optFrom match {
- case None =>
- logger.log(level, s"$verb from ... er ... nowhere!? (this is a bug in dce)")
- case Some(from) =>
- from match {
- case FromMethod(methodInfo) =>
- logger.log(level, s"$verb from $methodInfo")
- if (onlyOnce(methodInfo)) {
- methodInfo.instantiatedSubclass.foreach(involvedClasses += _)
- loopTrace(methodInfo.calledFrom)
- }
- case FromCore =>
- logger.log(level, s"$verb from scalajs-corejslib.js")
- case FromExports =>
- logger.log(level, "exported to JavaScript with @JSExport")
- case FromManual =>
- logger.log(level, "manually made reachable")
- }
- }
- }
-
- logger.indented {
- loopTrace(optFrom, verb = verb)
- }
-
- if (involvedClasses.nonEmpty) {
- logger.log(level, "involving instantiated classes:")
- logger.indented {
- for (classInfo <- involvedClasses.result().distinct) {
- logger.log(level, s"$classInfo")
- if (onlyOnce(classInfo))
- rec(Level.Debug, classInfo.instantiatedFrom, verb = "instantiated")
- // recurse with Debug log level not to overwhelm the user
- }
- }
- }
- }
-
- rec(Level.Warn, Some(from))
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala
deleted file mode 100644
index 47e1f87..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/GenIncOptimizer.scala
+++ /dev/null
@@ -1,921 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.optimizer
-
-import language.higherKinds
-
-import scala.annotation.{switch, tailrec}
-
-import scala.collection.{GenMap, GenTraversableOnce, GenIterable, GenIterableLike}
-import scala.collection.mutable
-
-import scala.scalajs.ir._
-import Definitions.isConstructorName
-import Infos.OptimizerHints
-import Trees._
-import Types._
-
-import scala.scalajs.tools.sem._
-
-import scala.scalajs.tools.javascript
-import javascript.Trees.{Tree => JSTree}
-import javascript.ScalaJSClassEmitter
-
-import scala.scalajs.tools.logging._
-
-/** Incremental optimizer.
- * An incremental optimizer consumes the reachability analysis produced by
- * an [[Analyzer]], as well as trees for classes, trait impls, etc., and
- * optimizes them in an incremental way.
- * It maintains state between runs to do a minimal amount of work on every
- * run, based on detecting what parts of the program must be re-optimized,
- * and keeping optimized results from previous runs for the rest.
- */
-abstract class GenIncOptimizer(semantics: Semantics) {
- import GenIncOptimizer._
-
- protected val CollOps: AbsCollOps
-
- private val classEmitter = new ScalaJSClassEmitter(semantics)
-
- private var logger: Logger = _
-
- /** Are we in batch mode? I.e., are we running from scratch?
- * Various parts of the algorithm can be skipped entirely when running in
- * batch mode.
- */
- private var batchMode: Boolean = false
-
- /** Should positions be considered when comparing tree hashes */
- private var considerPositions: Boolean = _
-
- private var objectClass: Class = _
- private val classes = CollOps.emptyMap[String, Class]
- private val traitImpls = CollOps.emptyParMap[String, TraitImpl]
-
- protected def getInterface(encodedName: String): InterfaceType
-
- /** Schedule a method for processing in the PROCESS PASS */
- protected def scheduleMethod(method: MethodImpl): Unit
-
- protected def newMethodImpl(owner: MethodContainer,
- encodedName: String): MethodImpl
-
- def findTraitImpl(encodedName: String): TraitImpl = traitImpls(encodedName)
- def findClass(encodedName: String): Class = classes(encodedName)
-
- def getTraitImpl(encodedName: String): Option[TraitImpl] = traitImpls.get(encodedName)
- def getClass(encodedName: String): Option[Class] = classes.get(encodedName)
-
- type GetClassTreeIfChanged =
- (String, Option[String]) => Option[(ClassDef, Option[String])]
-
- private def withLogger[A](logger: Logger)(body: => A): A = {
- assert(this.logger == null)
- this.logger = logger
- try body
- finally this.logger = null
- }
-
- /** Update the incremental analyzer with a new run. */
- def update(analyzer: Analyzer,
- getClassTreeIfChanged: GetClassTreeIfChanged, considerPositions: Boolean,
- logger: Logger): Unit = withLogger(logger) {
-
- batchMode = objectClass == null
- this.considerPositions = considerPositions
- logger.debug(s"Optimizer batch mode: $batchMode")
-
- logTime(logger, "Incremental part of inc. optimizer") {
- /* UPDATE PASS */
- updateAndTagEverything(analyzer, getClassTreeIfChanged)
- }
-
- logTime(logger, "Optimizer part of inc. optimizer") {
- /* PROCESS PASS */
- processAllTaggedMethods()
- }
- }
-
- /** Incremental part: update state and detect what needs to be re-optimized.
- * UPDATE PASS ONLY. (This IS the update pass).
- */
- private def updateAndTagEverything(analyzer: Analyzer,
- getClassTreeIfChanged: GetClassTreeIfChanged): Unit = {
-
- val neededClasses = CollOps.emptyParMap[String, analyzer.ClassInfo]
- val neededTraitImpls = CollOps.emptyParMap[String, analyzer.ClassInfo]
- for {
- classInfo <- analyzer.classInfos.values
- if classInfo.isNeededAtAll
- } {
- if (classInfo.isClass && classInfo.isAnySubclassInstantiated)
- CollOps.put(neededClasses, classInfo.encodedName, classInfo)
- else if (classInfo.isImplClass)
- CollOps.put(neededTraitImpls, classInfo.encodedName, classInfo)
- }
-
- /* Remove deleted trait impls, and update existing trait impls.
- * We don't even have to notify callers in case of additions or removals
- * because callers have got to be invalidated by themselves.
- * Only changed methods need to trigger notifications.
- *
- * Non-batch mode only.
- */
- assert(!batchMode || traitImpls.isEmpty)
- if (!batchMode) {
- CollOps.retain(traitImpls) { (traitImplName, traitImpl) =>
- CollOps.remove(neededTraitImpls, traitImplName).fold {
- /* Deleted trait impl. Mark all its methods as deleted, and remove it
- * from known trait impls.
- */
- traitImpl.methods.values.foreach(_.delete())
-
- false
- } { traitImplInfo =>
- /* Existing trait impl. Update it. */
- val (added, changed, removed) =
- traitImpl.updateWith(traitImplInfo, getClassTreeIfChanged)
- for (method <- changed)
- traitImpl.myInterface.tagStaticCallersOf(method)
-
- true
- }
- }
- }
-
- /* Add new trait impls.
- * Easy, we don't have to notify anyone.
- */
- for (traitImplInfo <- neededTraitImpls.values) {
- val traitImpl = new TraitImpl(traitImplInfo.encodedName)
- CollOps.put(traitImpls, traitImpl.encodedName, traitImpl)
- traitImpl.updateWith(traitImplInfo, getClassTreeIfChanged)
- }
-
- if (!batchMode) {
- /* Class removals:
- * * If a class is deleted or moved, delete its entire subtree (because
- * all its descendants must also be deleted or moved).
- * * If an existing class was instantiated but is no more, notify callers
- * of its methods.
- *
- * Non-batch mode only.
- */
- val objectClassStillExists =
- objectClass.walkClassesForDeletions(neededClasses.get(_))
- assert(objectClassStillExists, "Uh oh, java.lang.Object was deleted!")
-
- /* Class changes:
- * * Delete removed methods, update existing ones, add new ones
- * * Update the list of ancestors
- * * Class newly instantiated
- *
- * Non-batch mode only.
- */
- objectClass.walkForChanges(
- CollOps.remove(neededClasses, _).get,
- getClassTreeIfChanged,
- Set.empty)
- }
-
- /* Class additions:
- * * Add new classes (including those that have moved from elsewhere).
- * In batch mode, we avoid doing notifications.
- */
-
- // Group children by (immediate) parent
- val newChildrenByParent = CollOps.emptyAccMap[String, Analyzer#ClassInfo]
-
- for (classInfo <- neededClasses.values) {
- val superInfo = classInfo.superClass
- if (superInfo == null) {
- assert(batchMode, "Trying to add java.lang.Object in incremental mode")
- objectClass = new Class(None, classInfo.encodedName)
- classes += classInfo.encodedName -> objectClass
- objectClass.setupAfterCreation(classInfo, getClassTreeIfChanged)
- } else {
- CollOps.acc(newChildrenByParent, superInfo.encodedName, classInfo)
- }
- }
-
- val getNewChildren =
- (name: String) => CollOps.getAcc(newChildrenByParent, name)
-
- // Walk the tree to add children
- if (batchMode) {
- objectClass.walkForAdditions(getNewChildren, getClassTreeIfChanged)
- } else {
- val existingParents =
- CollOps.parFlatMapKeys(newChildrenByParent)(classes.get)
- for (parent <- existingParents)
- parent.walkForAdditions(getNewChildren, getClassTreeIfChanged)
- }
-
- }
-
- /** Optimizer part: process all methods that need reoptimizing.
- * PROCESS PASS ONLY. (This IS the process pass).
- */
- protected def processAllTaggedMethods(): Unit
-
- protected def logProcessingMethods(count: Int): Unit =
- logger.debug(s"Optimizing $count methods.")
-
- /** Base class for [[Class]] and [[TraitImpl]]. */
- abstract class MethodContainer(val encodedName: String) {
- def thisType: Type
-
- val myInterface = getInterface(encodedName)
-
- val methods = mutable.Map.empty[String, MethodImpl]
-
- var lastVersion: Option[String] = None
-
- private def reachableMethodsOf(info: Analyzer#ClassInfo): Set[String] = {
- (for {
- methodInfo <- info.methodInfos.values
- if methodInfo.isReachable && !methodInfo.isAbstract
- } yield {
- methodInfo.encodedName
- }).toSet
- }
-
- /** UPDATE PASS ONLY. Global concurrency safe but not on same instance */
- def updateWith(info: Analyzer#ClassInfo,
- getClassTreeIfChanged: GetClassTreeIfChanged): (Set[String], Set[String], Set[String]) = {
- myInterface.ancestors = info.ancestors.map(_.encodedName).toList
-
- val addedMethods = Set.newBuilder[String]
- val changedMethods = Set.newBuilder[String]
- val deletedMethods = Set.newBuilder[String]
-
- val reachableMethods = reachableMethodsOf(info)
- val methodSetChanged = methods.keySet != reachableMethods
- if (methodSetChanged) {
- // Remove deleted methods
- methods retain { (methodName, method) =>
- if (reachableMethods.contains(methodName)) {
- true
- } else {
- deletedMethods += methodName
- method.delete()
- false
- }
- }
- // Clear lastVersion if there are new methods
- if (reachableMethods.exists(!methods.contains(_)))
- lastVersion = None
- }
- for ((tree, version) <- getClassTreeIfChanged(encodedName, lastVersion)) {
- lastVersion = version
- this match {
- case cls: Class =>
- cls.isModuleClass = tree.kind == ClassKind.ModuleClass
- cls.fields = for (field @ VarDef(_, _, _, _) <- tree.defs) yield field
- case _ =>
- }
- tree.defs.foreach {
- case methodDef: MethodDef if methodDef.name.isInstanceOf[Ident] &&
- reachableMethods.contains(methodDef.name.name) =>
- val methodName = methodDef.name.name
-
- val methodInfo = info.methodInfos(methodName)
- methods.get(methodName).fold {
- addedMethods += methodName
- val method = newMethodImpl(this, methodName)
- method.updateWith(methodInfo, methodDef)
- methods(methodName) = method
- method
- } { method =>
- if (method.updateWith(methodInfo, methodDef))
- changedMethods += methodName
- method
- }
-
- case _ => // ignore
- }
- }
-
- (addedMethods.result(), changedMethods.result(), deletedMethods.result())
- }
- }
-
- /** Class in the class hierarchy (not an interface).
- * A class may be a module class.
- * A class knows its superclass and the interfaces it implements. It also
- * maintains a list of its direct subclasses, so that the instances of
- * [[Class]] form a tree of the class hierarchy.
- */
- class Class(val superClass: Option[Class],
- _encodedName: String) extends MethodContainer(_encodedName) {
- if (encodedName == Definitions.ObjectClass) {
- assert(superClass.isEmpty)
- assert(objectClass == null)
- } else {
- assert(superClass.isDefined)
- }
-
- /** Parent chain from this to Object. */
- val parentChain: List[Class] =
- this :: superClass.fold[List[Class]](Nil)(_.parentChain)
-
- /** Reverse parent chain from Object to this. */
- val reverseParentChain: List[Class] =
- parentChain.reverse
-
- def thisType: Type = ClassType(encodedName)
-
- var interfaces: Set[InterfaceType] = Set.empty
- var subclasses: CollOps.ParIterable[Class] = CollOps.emptyParIterable
- var isInstantiated: Boolean = false
-
- var isModuleClass: Boolean = false
- var hasElidableModuleAccessor: Boolean = false
-
- var fields: List[VarDef] = Nil
- var isInlineable: Boolean = false
- var tryNewInlineable: Option[RecordValue] = None
-
- override def toString(): String =
- encodedName
-
- /** Walk the class hierarchy tree for deletions.
- * This includes "deleting" classes that were previously instantiated but
- * are no more.
- * UPDATE PASS ONLY. Not concurrency safe on same instance.
- */
- def walkClassesForDeletions(
- getClassInfoIfNeeded: String => Option[Analyzer#ClassInfo]): Boolean = {
- def sameSuperClass(info: Analyzer#ClassInfo): Boolean =
- if (info.superClass == null) superClass.isEmpty
- else superClass.exists(_.encodedName == info.superClass.encodedName)
-
- getClassInfoIfNeeded(encodedName) match {
- case Some(classInfo) if sameSuperClass(classInfo) =>
- // Class still exists. Recurse.
- subclasses = subclasses.filter(
- _.walkClassesForDeletions(getClassInfoIfNeeded))
- if (isInstantiated && !classInfo.isInstantiated)
- notInstantiatedAnymore()
- true
- case _ =>
- // Class does not exist or has been moved. Delete the entire subtree.
- deleteSubtree()
- false
- }
- }
-
- /** Delete this class and all its subclasses. UPDATE PASS ONLY. */
- def deleteSubtree(): Unit = {
- delete()
- for (subclass <- subclasses)
- subclass.deleteSubtree()
- }
-
- /** UPDATE PASS ONLY. */
- private def delete(): Unit = {
- if (isInstantiated)
- notInstantiatedAnymore()
- for (method <- methods.values)
- method.delete()
- classes -= encodedName
- /* Note: no need to tag methods that call *statically* one of the methods
- * of the deleted classes, since they've got to be invalidated by
- * themselves.
- */
- }
-
- /** UPDATE PASS ONLY. */
- def notInstantiatedAnymore(): Unit = {
- assert(isInstantiated)
- isInstantiated = false
- for (intf <- interfaces) {
- intf.removeInstantiatedSubclass(this)
- for (methodName <- allMethods().keys)
- intf.tagDynamicCallersOf(methodName)
- }
- }
-
- /** UPDATE PASS ONLY. */
- def walkForChanges(
- getClassInfo: String => Analyzer#ClassInfo,
- getClassTreeIfChanged: GetClassTreeIfChanged,
- parentMethodAttributeChanges: Set[String]): Unit = {
-
- val classInfo = getClassInfo(encodedName)
-
- val (addedMethods, changedMethods, deletedMethods) =
- updateWith(classInfo, getClassTreeIfChanged)
-
- val oldInterfaces = interfaces
- val newInterfaces =
- classInfo.ancestors.map(info => getInterface(info.encodedName)).toSet
- interfaces = newInterfaces
-
- val methodAttributeChanges =
- (parentMethodAttributeChanges -- methods.keys ++
- addedMethods ++ changedMethods ++ deletedMethods)
-
- // Tag callers with dynamic calls
- val wasInstantiated = isInstantiated
- isInstantiated = classInfo.isInstantiated
- assert(!(wasInstantiated && !isInstantiated),
- "(wasInstantiated && !isInstantiated) should have been handled "+
- "during deletion phase")
-
- if (isInstantiated) {
- if (wasInstantiated) {
- val existingInterfaces = oldInterfaces.intersect(newInterfaces)
- for {
- intf <- existingInterfaces
- methodName <- methodAttributeChanges
- } {
- intf.tagDynamicCallersOf(methodName)
- }
- if (newInterfaces.size != oldInterfaces.size ||
- newInterfaces.size != existingInterfaces.size) {
- val allMethodNames = allMethods().keys
- for {
- intf <- oldInterfaces ++ newInterfaces -- existingInterfaces
- methodName <- allMethodNames
- } {
- intf.tagDynamicCallersOf(methodName)
- }
- }
- } else {
- val allMethodNames = allMethods().keys
- for (intf <- interfaces) {
- intf.addInstantiatedSubclass(this)
- for (methodName <- allMethodNames)
- intf.tagDynamicCallersOf(methodName)
- }
- }
- }
-
- // Tag callers with static calls
- for (methodName <- methodAttributeChanges)
- myInterface.tagStaticCallersOf(methodName)
-
- // Module class specifics
- updateHasElidableModuleAccessor()
-
- // Inlineable class
- if (updateIsInlineable(classInfo)) {
- for (method <- methods.values; if isConstructorName(method.encodedName))
- myInterface.tagStaticCallersOf(method.encodedName)
- }
-
- // Recurse in subclasses
- for (cls <- subclasses)
- cls.walkForChanges(getClassInfo, getClassTreeIfChanged,
- methodAttributeChanges)
- }
-
- /** UPDATE PASS ONLY. */
- def walkForAdditions(
- getNewChildren: String => GenIterable[Analyzer#ClassInfo],
- getClassTreeIfChanged: GetClassTreeIfChanged): Unit = {
-
- val subclassAcc = CollOps.prepAdd(subclasses)
-
- for (classInfo <- getNewChildren(encodedName)) {
- val cls = new Class(Some(this), classInfo.encodedName)
- CollOps.add(subclassAcc, cls)
- classes += classInfo.encodedName -> cls
- cls.setupAfterCreation(classInfo, getClassTreeIfChanged)
- cls.walkForAdditions(getNewChildren, getClassTreeIfChanged)
- }
-
- subclasses = CollOps.finishAdd(subclassAcc)
- }
-
- /** UPDATE PASS ONLY. */
- def updateHasElidableModuleAccessor(): Unit = {
- hasElidableModuleAccessor =
- isAdHocElidableModuleAccessor(encodedName) ||
- (isModuleClass && lookupMethod("init___").exists(isElidableModuleConstructor))
- }
-
- /** UPDATE PASS ONLY. */
- def updateIsInlineable(classInfo: Analyzer#ClassInfo): Boolean = {
- val oldTryNewInlineable = tryNewInlineable
- isInlineable = classInfo.optimizerHints.hasInlineAnnot
- if (!isInlineable) {
- tryNewInlineable = None
- } else {
- val allFields = reverseParentChain.flatMap(_.fields)
- val (fieldValues, fieldTypes) = (for {
- VarDef(Ident(name, originalName), tpe, mutable, rhs) <- allFields
- } yield {
- (rhs, RecordType.Field(name, originalName, tpe, mutable))
- }).unzip
- tryNewInlineable = Some(
- RecordValue(RecordType(fieldTypes), fieldValues)(Position.NoPosition))
- }
- tryNewInlineable != oldTryNewInlineable
- }
-
- /** UPDATE PASS ONLY. */
- def setupAfterCreation(classInfo: Analyzer#ClassInfo,
- getClassTreeIfChanged: GetClassTreeIfChanged): Unit = {
-
- updateWith(classInfo, getClassTreeIfChanged)
- interfaces =
- classInfo.ancestors.map(info => getInterface(info.encodedName)).toSet
-
- isInstantiated = classInfo.isInstantiated
-
- if (batchMode) {
- if (isInstantiated) {
- /* Only add the class to all its ancestor interfaces */
- for (intf <- interfaces)
- intf.addInstantiatedSubclass(this)
- }
- } else {
- val allMethodNames = allMethods().keys
-
- if (isInstantiated) {
- /* Add the class to all its ancestor interfaces + notify all callers
- * of any of the methods.
- * TODO: be more selective on methods that are notified: it is not
- * necessary to modify callers of methods defined in a parent class
- * that already existed in the previous run.
- */
- for (intf <- interfaces) {
- intf.addInstantiatedSubclass(this)
- for (methodName <- allMethodNames)
- intf.tagDynamicCallersOf(methodName)
- }
- }
-
- /* Tag static callers because the class could have been *moved*,
- * not just added.
- */
- for (methodName <- allMethodNames)
- myInterface.tagStaticCallersOf(methodName)
- }
-
- updateHasElidableModuleAccessor()
- updateIsInlineable(classInfo)
- }
-
- /** UPDATE PASS ONLY. */
- private def isElidableModuleConstructor(impl: MethodImpl): Boolean = {
- def isTriviallySideEffectFree(tree: Tree): Boolean = tree match {
- case _:VarRef | _:Literal | _:This => true
- case _ => false
- }
- def isElidableStat(tree: Tree): Boolean = tree match {
- case Block(stats) =>
- stats.forall(isElidableStat)
- case Assign(Select(This(), _, _), rhs) =>
- isTriviallySideEffectFree(rhs)
- case TraitImplApply(ClassType(traitImpl), methodName, List(This())) =>
- traitImpls(traitImpl).methods(methodName.name).originalDef.body match {
- case Skip() => true
- case _ => false
- }
- case StaticApply(This(), ClassType(cls), methodName, args) =>
- Definitions.isConstructorName(methodName.name) &&
- args.forall(isTriviallySideEffectFree) &&
- impl.owner.asInstanceOf[Class].superClass.exists { superCls =>
- superCls.encodedName == cls &&
- superCls.lookupMethod(methodName.name).exists(isElidableModuleConstructor)
- }
- case StoreModule(_, _) =>
- true
- case _ =>
- isTriviallySideEffectFree(tree)
- }
- isElidableStat(impl.originalDef.body)
- }
-
- /** All the methods of this class, including inherited ones.
- * It has () so we remember this is an expensive operation.
- * UPDATE PASS ONLY.
- */
- def allMethods(): scala.collection.Map[String, MethodImpl] = {
- val result = mutable.Map.empty[String, MethodImpl]
- for (parent <- reverseParentChain)
- result ++= parent.methods
- result
- }
-
- /** BOTH PASSES. */
- @tailrec
- final def lookupMethod(methodName: String): Option[MethodImpl] = {
- methods.get(methodName) match {
- case Some(impl) => Some(impl)
- case none =>
- superClass match {
- case Some(p) => p.lookupMethod(methodName)
- case none => None
- }
- }
- }
- }
-
- /** Trait impl. */
- class TraitImpl(_encodedName: String) extends MethodContainer(_encodedName) {
- def thisType: Type = NoType
- }
-
- /** Thing from which a [[MethodImpl]] can unregister itself from. */
- trait Unregisterable {
- /** UPDATE PASS ONLY. */
- def unregisterDependee(dependee: MethodImpl): Unit
- }
-
- /** Type of a class or interface.
- * Types are created on demand when a method is called on a given
- * [[ClassType]].
- *
- * Fully concurrency safe unless otherwise noted.
- */
- abstract class InterfaceType(val encodedName: String) extends Unregisterable {
-
- override def toString(): String =
- s"intf $encodedName"
-
- /** PROCESS PASS ONLY. Concurrency safe except with
- * [[addInstantiatedSubclass]] and [[removeInstantiatedSubclass]]
- */
- def instantiatedSubclasses: Iterable[Class]
-
- /** UPDATE PASS ONLY. Concurrency safe except with
- * [[instantiatedSubclasses]]
- */
- def addInstantiatedSubclass(x: Class): Unit
-
- /** UPDATE PASS ONLY. Concurrency safe except with
- * [[instantiatedSubclasses]]
- */
- def removeInstantiatedSubclass(x: Class): Unit
-
- /** PROCESS PASS ONLY. Concurrency safe except with [[ancestors_=]] */
- def ancestors: List[String]
-
- /** UPDATE PASS ONLY. Not concurrency safe. */
- def ancestors_=(v: List[String]): Unit
-
- /** PROCESS PASS ONLY. Concurrency safe except with [[ancestors_=]]. */
- def registerAskAncestors(asker: MethodImpl): Unit
-
- /** PROCESS PASS ONLY. */
- def registerDynamicCaller(methodName: String, caller: MethodImpl): Unit
-
- /** PROCESS PASS ONLY. */
- def registerStaticCaller(methodName: String, caller: MethodImpl): Unit
-
- /** UPDATE PASS ONLY. */
- def tagDynamicCallersOf(methodName: String): Unit
-
- /** UPDATE PASS ONLY. */
- def tagStaticCallersOf(methodName: String): Unit
- }
-
- /** A method implementation.
- * It must be concrete, and belong either to a [[Class]] or a [[TraitImpl]].
- *
- * A single instance is **not** concurrency safe (unless otherwise noted in
- * a method comment). However, the global state modifications are
- * concurrency safe.
- */
- abstract class MethodImpl(val owner: MethodContainer,
- val encodedName: String) extends OptimizerCore.MethodImpl
- with OptimizerCore.AbstractMethodID
- with Unregisterable {
- private[this] var _deleted: Boolean = false
-
- var optimizerHints: OptimizerHints = OptimizerHints.empty
- var originalDef: MethodDef = _
- var desugaredDef: JSTree = _
- var preciseInfo: Infos.MethodInfo = _
-
- def thisType: Type = owner.thisType
- def deleted: Boolean = _deleted
-
- override def toString(): String =
- s"$owner.$encodedName"
-
- /** PROCESS PASS ONLY. */
- def registerBodyAsker(asker: MethodImpl): Unit
-
- /** UPDATE PASS ONLY. */
- def tagBodyAskers(): Unit
-
- /** PROCESS PASS ONLY. */
- private def registerAskAncestors(intf: InterfaceType): Unit = {
- intf.registerAskAncestors(this)
- registeredTo(intf)
- }
-
- /** PROCESS PASS ONLY. */
- private def registerDynamicCall(intf: InterfaceType,
- methodName: String): Unit = {
- intf.registerDynamicCaller(methodName, this)
- registeredTo(intf)
- }
-
- /** PROCESS PASS ONLY. */
- private def registerStaticCall(intf: InterfaceType,
- methodName: String): Unit = {
- intf.registerStaticCaller(methodName, this)
- registeredTo(intf)
- }
-
- /** PROCESS PASS ONLY. */
- def registerAskBody(target: MethodImpl): Unit = {
- target.registerBodyAsker(this)
- registeredTo(target)
- }
-
- /** PROCESS PASS ONLY. */
- protected def registeredTo(intf: Unregisterable): Unit
-
- /** UPDATE PASS ONLY. */
- protected def unregisterFromEverywhere(): Unit
-
- /** Return true iff this is the first time this method is called since the
- * last reset (via [[resetTag]]).
- * UPDATE PASS ONLY.
- */
- protected def protectTag(): Boolean
-
- /** PROCESS PASS ONLY. */
- protected def resetTag(): Unit
-
- /** Returns true if the method's attributes changed.
- * Attributes are whether it is inlineable, and whether it is a trait
- * impl forwarder. Basically this is what is declared in
- * [[OptimizerCore.AbstractMethodID]].
- * In the process, tags all the body askers if the body changes.
- * UPDATE PASS ONLY. Not concurrency safe on same instance.
- */
- def updateWith(methodInfo: Analyzer#MethodInfo,
- methodDef: MethodDef): Boolean = {
- assert(!_deleted, "updateWith() called on a deleted method")
-
- val bodyChanged = {
- originalDef == null ||
- (methodDef.hash zip originalDef.hash).forall {
- case (h1, h2) => !Hashers.hashesEqual(h1, h2, considerPositions)
- }
- }
-
- if (bodyChanged)
- tagBodyAskers()
-
- val hints = methodInfo.optimizerHints
- val changed = hints != optimizerHints || bodyChanged
- if (changed) {
- val oldAttributes = (inlineable, isTraitImplForwarder)
-
- optimizerHints = hints
- originalDef = methodDef
- desugaredDef = null
- preciseInfo = null
- updateInlineable()
- tag()
-
- val newAttributes = (inlineable, isTraitImplForwarder)
- newAttributes != oldAttributes
- } else {
- false
- }
- }
-
- /** UPDATE PASS ONLY. Not concurrency safe on same instance. */
- def delete(): Unit = {
- assert(!_deleted, "delete() called twice")
- _deleted = true
- if (protectTag())
- unregisterFromEverywhere()
- }
-
- /** Concurrency safe with itself and [[delete]] on the same instance
- *
- * [[tag]] can be called concurrently with [[delete]] when methods in
- * traits/classes are updated.
- *
- * UPDATE PASS ONLY.
- */
- def tag(): Unit = if (protectTag()) {
- scheduleMethod(this)
- unregisterFromEverywhere()
- }
-
- /** PROCESS PASS ONLY. */
- def process(): Unit = if (!_deleted) {
- val (optimizedDef, info) = new Optimizer().optimize(thisType, originalDef)
- desugaredDef =
- if (owner.isInstanceOf[Class])
- classEmitter.genMethod(owner.encodedName, optimizedDef)
- else
- classEmitter.genTraitImplMethod(owner.encodedName, optimizedDef)
- preciseInfo = info
- resetTag()
- }
-
- /** All methods are PROCESS PASS ONLY */
- private class Optimizer extends OptimizerCore(semantics) {
- type MethodID = MethodImpl
-
- val myself: MethodImpl.this.type = MethodImpl.this
-
- protected def getMethodBody(method: MethodID): MethodDef = {
- MethodImpl.this.registerAskBody(method)
- method.originalDef
- }
-
- protected def dynamicCall(intfName: String,
- methodName: String): List[MethodID] = {
- val intf = getInterface(intfName)
- MethodImpl.this.registerDynamicCall(intf, methodName)
- intf.instantiatedSubclasses.flatMap(_.lookupMethod(methodName)).toList
- }
-
- protected def staticCall(className: String,
- methodName: String): Option[MethodID] = {
- val clazz = classes(className)
- MethodImpl.this.registerStaticCall(clazz.myInterface, methodName)
- clazz.lookupMethod(methodName)
- }
-
- protected def traitImplCall(traitImplName: String,
- methodName: String): Option[MethodID] = {
- val traitImpl = traitImpls(traitImplName)
- registerStaticCall(traitImpl.myInterface, methodName)
- traitImpl.methods.get(methodName)
- }
-
- protected def getAncestorsOf(intfName: String): List[String] = {
- val intf = getInterface(intfName)
- registerAskAncestors(intf)
- intf.ancestors
- }
-
- protected def hasElidableModuleAccessor(moduleClassName: String): Boolean =
- classes(moduleClassName).hasElidableModuleAccessor
-
- protected def tryNewInlineableClass(className: String): Option[RecordValue] =
- classes(className).tryNewInlineable
- }
- }
-
-}
-
-object GenIncOptimizer {
-
- private val isAdHocElidableModuleAccessor =
- Set("s_Predef$")
-
- private[optimizer] def logTime[A](logger: Logger,
- title: String)(body: => A): A = {
- val startTime = System.nanoTime()
- val result = body
- val endTime = System.nanoTime()
- val elapsedTime = endTime - startTime
- logger.time(title, elapsedTime)
- result
- }
-
- private[optimizer] trait AbsCollOps {
- type Map[K, V] <: mutable.Map[K, V]
- type ParMap[K, V] <: GenMap[K, V]
- type AccMap[K, V]
- type ParIterable[V] <: GenIterableLike[V, ParIterable[V]]
- type Addable[V]
-
- def emptyAccMap[K, V]: AccMap[K, V]
- def emptyMap[K, V]: Map[K, V]
- def emptyParMap[K, V]: ParMap[K, V]
- def emptyParIterable[V]: ParIterable[V]
-
- // Operations on ParMap
- def put[K, V](map: ParMap[K, V], k: K, v: V): Unit
- def remove[K, V](map: ParMap[K, V], k: K): Option[V]
- def retain[K, V](map: ParMap[K, V])(p: (K, V) => Boolean): Unit
-
- // Operations on AccMap
- def acc[K, V](map: AccMap[K, V], k: K, v: V): Unit
- def getAcc[K, V](map: AccMap[K, V], k: K): GenIterable[V]
- def parFlatMapKeys[A, B](map: AccMap[A, _])(
- f: A => GenTraversableOnce[B]): GenIterable[B]
-
- // Operations on ParIterable
- def prepAdd[V](it: ParIterable[V]): Addable[V]
- def add[V](addable: Addable[V], v: V): Unit
- def finishAdd[V](addable: Addable[V]): ParIterable[V]
-
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IRChecker.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IRChecker.scala
deleted file mode 100644
index 6329826..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IRChecker.scala
+++ /dev/null
@@ -1,854 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.optimizer
-
-import scala.language.implicitConversions
-
-import scala.annotation.switch
-
-import scala.collection.mutable
-
-import scala.scalajs.ir._
-import Definitions._
-import Trees._
-import Types._
-
-import scala.scalajs.tools.logging._
-
-/** Checker for the validity of the IR. */
-class IRChecker(analyzer: Analyzer, allClassDefs: Seq[ClassDef], logger: Logger) {
- import IRChecker._
-
- private var _errorCount: Int = 0
- def errorCount: Int = _errorCount
-
- private val classes: mutable.Map[String, CheckedClass] = {
- mutable.Map.empty[String, CheckedClass] ++=
- allClassDefs.map(new CheckedClass(_)).map(c => c.name -> c)
- }
-
- def check(): Boolean = {
- for {
- classDef <- allClassDefs
- if analyzer.classInfos(classDef.name.name).isNeededAtAll
- } {
- classDef.kind match {
- case ClassKind.Class | ClassKind.ModuleClass => checkClass(classDef)
- case ClassKind.TraitImpl => checkTraitImpl(classDef)
- case _ =>
- }
- }
- errorCount == 0
- }
-
- def checkClass(classDef: ClassDef): Unit = {
- if (!analyzer.classInfos(classDef.name.name).isAnySubclassInstantiated)
- return
-
- for (member <- classDef.defs) {
- implicit val ctx = ErrorContext(member)
- member match {
- // Scala declarations
- case v @ VarDef(_, _, _, _) =>
- checkFieldDef(v, classDef)
- case m: MethodDef if m.name.isInstanceOf[Ident] =>
- checkMethodDef(m, classDef)
-
- // Exports
- case m: MethodDef if m.name.isInstanceOf[StringLiteral] =>
- checkExportedMethodDef(m, classDef)
- case member @ PropertyDef(_: StringLiteral, _, _, _) =>
- checkExportedPropertyDef(member, classDef)
- case member @ ConstructorExportDef(_, _, _) =>
- checkConstructorExportDef(member, classDef)
- case member @ ModuleExportDef(_) =>
- checkModuleExportDef(member, classDef)
-
- // Anything else is illegal
- case _ =>
- reportError(s"Illegal class member of type ${member.getClass.getName}")
- }
- }
- }
-
- def checkTraitImpl(classDef: ClassDef): Unit = {
- for (member <- classDef.defs) {
- implicit val ctx = ErrorContext(member)
- member match {
- case m: MethodDef =>
- checkMethodDef(m, classDef)
- case _ =>
- reportError(s"Invalid member for a TraitImpl")
- }
- }
- }
-
- def checkFieldDef(fieldDef: VarDef, classDef: ClassDef): Unit = {
- val VarDef(name, tpe, mutable, rhs) = fieldDef
- implicit val ctx = ErrorContext(fieldDef)
-
- if (tpe == NoType)
- reportError(s"VarDef cannot have type NoType")
- else
- typecheckExpect(rhs, Env.empty, tpe)
- }
-
- def checkMethodDef(methodDef: MethodDef, classDef: ClassDef): Unit = {
- val MethodDef(Ident(name, _), params, resultType, body) = methodDef
- implicit val ctx = ErrorContext(methodDef)
-
- if (!analyzer.classInfos(classDef.name.name).methodInfos(name).isReachable)
- return
-
- for (ParamDef(name, tpe, _) <- params)
- if (tpe == NoType)
- reportError(s"Parameter $name has type NoType")
-
- val resultTypeForSig =
- if (isConstructorName(name)) NoType
- else resultType
-
- val advertizedSig = (params.map(_.ptpe), resultTypeForSig)
- val sigFromName = inferMethodType(name,
- inTraitImpl = classDef.kind == ClassKind.TraitImpl)
- if (advertizedSig != sigFromName) {
- reportError(
- s"The signature of ${classDef.name.name}.$name, which is "+
- s"$advertizedSig, does not match its name (should be $sigFromName).")
- }
-
- val thisType =
- if (!classDef.kind.isClass) NoType
- else ClassType(classDef.name.name)
- val bodyEnv = Env.fromSignature(thisType, params, resultType)
- if (resultType == NoType)
- typecheckStat(body, bodyEnv)
- else
- typecheckExpect(body, bodyEnv, resultType)
- }
-
- def checkExportedMethodDef(methodDef: MethodDef, classDef: ClassDef): Unit = {
- val MethodDef(_, params, resultType, body) = methodDef
- implicit val ctx = ErrorContext(methodDef)
-
- if (!classDef.kind.isClass) {
- reportError(s"Exported method def can only appear in a class")
- return
- }
-
- for (ParamDef(name, tpe, _) <- params) {
- if (tpe == NoType)
- reportError(s"Parameter $name has type NoType")
- else if (tpe != AnyType)
- reportError(s"Parameter $name of exported method def has type $tpe, "+
- "but must be Any")
- }
-
- if (resultType != AnyType) {
- reportError(s"Result type of exported method def is $resultType, "+
- "but must be Any")
- }
-
- val thisType = ClassType(classDef.name.name)
- val bodyEnv = Env.fromSignature(thisType, params, resultType)
- .withArgumentsVar(methodDef.pos)
- typecheckExpect(body, bodyEnv, resultType)
- }
-
- def checkExportedPropertyDef(propDef: PropertyDef, classDef: ClassDef): Unit = {
- val PropertyDef(_, getterBody, setterArg, setterBody) = propDef
- implicit val ctx = ErrorContext(propDef)
-
- if (!classDef.kind.isClass) {
- reportError(s"Exported property def can only appear in a class")
- return
- }
-
- val thisType = ClassType(classDef.name.name)
-
- if (getterBody != EmptyTree) {
- val getterBodyEnv = Env.fromSignature(thisType, Nil, AnyType)
- typecheckExpect(getterBody, getterBodyEnv, AnyType)
- }
-
- if (setterBody != EmptyTree) {
- if (setterArg.ptpe != AnyType)
- reportError("Setter argument of exported property def has type "+
- s"${setterArg.ptpe}, but must be Any")
-
- val setterBodyEnv = Env.fromSignature(thisType, List(setterArg), NoType)
- typecheckStat(setterBody, setterBodyEnv)
- }
- }
-
- def checkConstructorExportDef(ctorDef: ConstructorExportDef,
- classDef: ClassDef): Unit = {
- val ConstructorExportDef(_, params, body) = ctorDef
- implicit val ctx = ErrorContext(ctorDef)
-
- if (!classDef.kind.isClass) {
- reportError(s"Exported constructor def can only appear in a class")
- return
- }
-
- for (ParamDef(name, tpe, _) <- params) {
- if (tpe == NoType)
- reportError(s"Parameter $name has type NoType")
- else if (tpe != AnyType)
- reportError(s"Parameter $name of exported constructor def has type "+
- s"$tpe, but must be Any")
- }
-
- val thisType = ClassType(classDef.name.name)
- val bodyEnv = Env.fromSignature(thisType, params, NoType)
- .withArgumentsVar(ctorDef.pos)
- typecheckStat(body, bodyEnv)
- }
-
- def checkModuleExportDef(moduleDef: ModuleExportDef,
- classDef: ClassDef): Unit = {
- implicit val ctx = ErrorContext(moduleDef)
-
- if (classDef.kind != ClassKind.ModuleClass)
- reportError(s"Exported module def can only appear in a module class")
- }
-
- def typecheckStat(tree: Tree, env: Env): Env = {
- implicit val ctx = ErrorContext(tree)
-
- tree match {
- case VarDef(ident, vtpe, mutable, rhs) =>
- typecheckExpect(rhs, env, vtpe)
- env.withLocal(LocalDef(ident.name, vtpe, mutable)(tree.pos))
-
- case Skip() =>
- env
-
- case Assign(select, rhs) =>
- select match {
- case Select(_, Ident(name, _), false) =>
- /* TODO In theory this case would verify that we never assign to
- * an immutable field. But we cannot do that because we *do* emit
- * such assigns in constructors.
- * In the future we might want to check that only these legal
- * special cases happen, and nothing else. But it seems non-trivial
- * to do so, so currently we trust scalac not to make us emit
- * illegal assigns.
- */
- //reportError(s"Assignment to immutable field $name.")
- case VarRef(Ident(name, _), false) =>
- reportError(s"Assignment to immutable variable $name.")
- case _ =>
- }
- val lhsTpe = typecheckExpr(select, env)
- val expectedRhsTpe = select match {
- case _:JSDotSelect | _:JSBracketSelect => AnyType
- case _ => lhsTpe
- }
- typecheckExpect(rhs, env, expectedRhsTpe)
- env
-
- case StoreModule(cls, value) =>
- if (!cls.className.endsWith("$"))
- reportError("StoreModule of non-module class $cls")
- typecheckExpect(value, env, ClassType(cls.className))
- env
-
- case Block(stats) =>
- (env /: stats) { (prevEnv, stat) =>
- typecheckStat(stat, prevEnv)
- }
- env
-
- case Labeled(label, NoType, body) =>
- typecheckStat(body, env.withLabeledReturnType(label.name, AnyType))
- env
-
- case If(cond, thenp, elsep) =>
- typecheckExpect(cond, env, BooleanType)
- typecheckStat(thenp, env)
- typecheckStat(elsep, env)
- env
-
- case While(cond, body, label) =>
- typecheckExpect(cond, env, BooleanType)
- typecheckStat(body, env)
- env
-
- case DoWhile(body, cond, label) =>
- typecheckStat(body, env)
- typecheckExpect(cond, env, BooleanType)
- env
-
- case Try(block, errVar, handler, finalizer) =>
- typecheckStat(block, env)
- if (handler != EmptyTree) {
- val handlerEnv =
- env.withLocal(LocalDef(errVar.name, AnyType, false)(errVar.pos))
- typecheckStat(handler, handlerEnv)
- }
- if (finalizer != EmptyTree) {
- typecheckStat(finalizer, env)
- }
- env
-
- case Match(selector, cases, default) =>
- typecheckExpr(selector, env)
- for ((alts, body) <- cases) {
- alts.foreach(typecheckExpr(_, env))
- typecheckStat(body, env)
- }
- typecheckStat(default, env)
- env
-
- case Debugger() =>
- env
-
- case JSDelete(JSDotSelect(obj, prop)) =>
- typecheckExpr(obj, env)
- env
-
- case JSDelete(JSBracketSelect(obj, prop)) =>
- typecheckExpr(obj, env)
- typecheckExpr(prop, env)
- env
-
- case _ =>
- typecheck(tree, env)
- env
- }
- }
-
- def typecheckExpect(tree: Tree, env: Env, expectedType: Type)(
- implicit ctx: ErrorContext): Unit = {
- val tpe = typecheckExpr(tree, env)
- if (!isSubtype(tpe, expectedType))
- reportError(s"$expectedType expected but $tpe found "+
- s"for tree of type ${tree.getClass.getName}")
- }
-
- def typecheckExpr(tree: Tree, env: Env): Type = {
- implicit val ctx = ErrorContext(tree)
- if (tree.tpe == NoType)
- reportError(s"Expression tree has type NoType")
- typecheck(tree, env)
- }
-
- def typecheck(tree: Tree, env: Env): Type = {
- implicit val ctx = ErrorContext(tree)
-
- def checkApplyGeneric(methodName: String, methodFullName: String,
- args: List[Tree], inTraitImpl: Boolean): Unit = {
- val (methodParams, resultType) = inferMethodType(methodName, inTraitImpl)
- if (args.size != methodParams.size)
- reportError(s"Arity mismatch: ${methodParams.size} expected but "+
- s"${args.size} found")
- for ((actual, formal) <- args zip methodParams) {
- typecheckExpect(actual, env, formal)
- }
- if (!isConstructorName(methodName) && tree.tpe != resultType)
- reportError(s"Call to $methodFullName of type $resultType "+
- s"typed as ${tree.tpe}")
- }
-
- tree match {
- // Control flow constructs
-
- case Block(statsAndExpr) =>
- val stats :+ expr = statsAndExpr
- val envAfterStats = (env /: stats) { (prevEnv, stat) =>
- typecheckStat(stat, prevEnv)
- }
- typecheckExpr(expr, envAfterStats)
-
- case Labeled(label, tpe, body) =>
- typecheckExpect(body, env.withLabeledReturnType(label.name, tpe), tpe)
-
- case Return(expr, label) =>
- env.returnTypes.get(label.map(_.name)).fold[Unit] {
- reportError(s"Cannot return to label $label.")
- typecheckExpr(expr, env)
- } { returnType =>
- typecheckExpect(expr, env, returnType)
- }
-
- case If(cond, thenp, elsep) =>
- val tpe = tree.tpe
- typecheckExpect(cond, env, BooleanType)
- typecheckExpect(thenp, env, tpe)
- typecheckExpect(elsep, env, tpe)
-
- case While(BooleanLiteral(true), body, label) if tree.tpe == NothingType =>
- typecheckStat(body, env)
-
- case Try(block, errVar, handler, finalizer) =>
- val tpe = tree.tpe
- typecheckExpect(block, env, tpe)
- if (handler != EmptyTree) {
- val handlerEnv =
- env.withLocal(LocalDef(errVar.name, AnyType, false)(errVar.pos))
- typecheckExpect(handler, handlerEnv, tpe)
- }
- if (finalizer != EmptyTree) {
- typecheckStat(finalizer, env)
- }
-
- case Throw(expr) =>
- typecheckExpr(expr, env)
-
- case Continue(label) =>
- /* Here we could check that it is indeed legal to break to the
- * specified label. However, if we do anything illegal here, it will
- * result in a SyntaxError in JavaScript anyway, so we do not really
- * care.
- */
-
- case Match(selector, cases, default) =>
- val tpe = tree.tpe
- typecheckExpr(selector, env)
- for ((alts, body) <- cases) {
- alts.foreach(typecheckExpr(_, env))
- typecheckExpect(body, env, tpe)
- }
- typecheckExpect(default, env, tpe)
-
- // Scala expressions
-
- case New(cls, ctor, args) =>
- val clazz = lookupClass(cls)
- if (!clazz.kind.isClass)
- reportError(s"new $cls which is not a class")
- checkApplyGeneric(ctor.name, s"$cls.$ctor", args,
- inTraitImpl = false)
-
- case LoadModule(cls) =>
- if (!cls.className.endsWith("$"))
- reportError("LoadModule of non-module class $cls")
-
- case Select(qualifier, Ident(item, _), mutable) =>
- val qualType = typecheckExpr(qualifier, env)
- qualType match {
- case ClassType(cls) =>
- val clazz = lookupClass(cls)
- if (!clazz.kind.isClass) {
- reportError(s"Cannot select $item of non-class $cls")
- } else {
- clazz.lookupField(item).fold[Unit] {
- reportError(s"Class $cls does not have a field $item")
- } { fieldDef =>
- if (fieldDef.tpe != tree.tpe)
- reportError(s"Select $cls.$item of type "+
- s"${fieldDef.tpe} typed as ${tree.tpe}")
- if (fieldDef.mutable != mutable)
- reportError(s"Select $cls.$item with "+
- s"mutable=${fieldDef.mutable} marked as mutable=$mutable")
- }
- }
- case NullType | NothingType =>
- // always ok
- case _ =>
- reportError(s"Cannot select $item of non-class type $qualType")
- }
-
- case Apply(receiver, Ident(method, _), args) =>
- val receiverType = typecheckExpr(receiver, env)
- checkApplyGeneric(method, s"$receiverType.$method", args,
- inTraitImpl = false)
-
- case StaticApply(receiver, cls, Ident(method, _), args) =>
- typecheckExpect(receiver, env, cls)
- checkApplyGeneric(method, s"$cls.$method", args, inTraitImpl = false)
-
- case TraitImplApply(impl, Ident(method, _), args) =>
- val clazz = lookupClass(impl)
- if (clazz.kind != ClassKind.TraitImpl)
- reportError(s"Cannot trait-impl apply method of non-trait-impl $impl")
- checkApplyGeneric(method, s"$impl.$method", args, inTraitImpl = true)
-
- case UnaryOp(op, lhs) =>
- import UnaryOp._
- (op: @switch) match {
- case `typeof` =>
- typecheckExpr(lhs, env)
- case IntToLong =>
- typecheckExpect(lhs, env, IntType)
- case LongToInt | LongToDouble =>
- typecheckExpect(lhs, env, LongType)
- case DoubleToInt | DoubleToFloat | DoubleToLong =>
- typecheckExpect(lhs, env, DoubleType)
- case Boolean_! =>
- typecheckExpect(lhs, env, BooleanType)
- }
-
- case BinaryOp(op, lhs, rhs) =>
- import BinaryOp._
- (op: @switch) match {
- case === | !== | String_+ =>
- typecheckExpr(lhs, env)
- typecheckExpr(rhs, env)
- case `in` =>
- typecheckExpect(lhs, env, ClassType(StringClass))
- typecheckExpr(rhs, env)
- case `instanceof` =>
- typecheckExpr(lhs, env)
- typecheckExpr(rhs, env)
- case Int_+ | Int_- | Int_* | Int_/ | Int_% |
- Int_| | Int_& | Int_^ | Int_<< | Int_>>> | Int_>> =>
- typecheckExpect(lhs, env, IntType)
- typecheckExpect(rhs, env, IntType)
- case Float_+ | Float_- | Float_* | Float_/ | Float_% =>
- typecheckExpect(lhs, env, FloatType)
- typecheckExpect(lhs, env, FloatType)
- case Long_+ | Long_- | Long_* | Long_/ | Long_% |
- Long_| | Long_& | Long_^ |
- Long_== | Long_!= | Long_< | Long_<= | Long_> | Long_>= =>
- typecheckExpect(lhs, env, LongType)
- typecheckExpect(rhs, env, LongType)
- case Long_<< | Long_>>> | Long_>> =>
- typecheckExpect(lhs, env, LongType)
- typecheckExpect(rhs, env, IntType)
- case Double_+ | Double_- | Double_* | Double_/ | Double_% |
- Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>= =>
- typecheckExpect(lhs, env, DoubleType)
- typecheckExpect(lhs, env, DoubleType)
- case Boolean_== | Boolean_!= | Boolean_| | Boolean_& =>
- typecheckExpect(lhs, env, BooleanType)
- typecheckExpect(rhs, env, BooleanType)
- }
-
- case NewArray(tpe, lengths) =>
- for (length <- lengths)
- typecheckExpect(length, env, IntType)
-
- case ArrayValue(tpe, elems) =>
- val elemType = arrayElemType(tpe)
- for (elem <- elems)
- typecheckExpect(elem, env, elemType)
-
- case ArrayLength(array) =>
- val arrayType = typecheckExpr(array, env)
- if (!arrayType.isInstanceOf[ArrayType])
- reportError(s"Array type expected but $arrayType found")
-
- case ArraySelect(array, index) =>
- typecheckExpect(index, env, IntType)
- typecheckExpr(array, env) match {
- case arrayType: ArrayType =>
- if (tree.tpe != arrayElemType(arrayType))
- reportError(s"Array select of array type $arrayType typed as ${tree.tpe}")
- case arrayType =>
- reportError(s"Array type expected but $arrayType found")
- }
-
- case IsInstanceOf(expr, cls) =>
- typecheckExpr(expr, env)
-
- case AsInstanceOf(expr, cls) =>
- typecheckExpr(expr, env)
-
- case Unbox(expr, _) =>
- typecheckExpr(expr, env)
-
- case GetClass(expr) =>
- typecheckExpr(expr, env)
-
- // JavaScript expressions
-
- case JSNew(ctor, args) =>
- typecheckExpr(ctor, env)
- for (arg <- args)
- typecheckExpr(arg, env)
-
- case JSDotSelect(qualifier, item) =>
- typecheckExpr(qualifier, env)
-
- case JSBracketSelect(qualifier, item) =>
- typecheckExpr(qualifier, env)
- typecheckExpr(item, env)
-
- case JSFunctionApply(fun, args) =>
- typecheckExpr(fun, env)
- for (arg <- args)
- typecheckExpr(arg, env)
-
- case JSDotMethodApply(receiver, method, args) =>
- typecheckExpr(receiver, env)
- for (arg <- args)
- typecheckExpr(arg, env)
-
- case JSBracketMethodApply(receiver, method, args) =>
- typecheckExpr(receiver, env)
- typecheckExpr(method, env)
- for (arg <- args)
- typecheckExpr(arg, env)
-
- case JSUnaryOp(op, lhs) =>
- typecheckExpr(lhs, env)
-
- case JSBinaryOp(op, lhs, rhs) =>
- typecheckExpr(lhs, env)
- typecheckExpr(rhs, env)
-
- case JSArrayConstr(items) =>
- for (item <- items)
- typecheckExpr(item, env)
-
- case JSObjectConstr(fields) =>
- for ((_, value) <- fields)
- typecheckExpr(value, env)
-
- case JSEnvInfo() =>
-
- // Literals
-
- case _: Literal =>
-
- // Atomic expressions
-
- case VarRef(Ident(name, _), mutable) =>
- env.locals.get(name).fold[Unit] {
- reportError(s"Cannot find variable $name in scope")
- } { localDef =>
- if (tree.tpe != localDef.tpe)
- reportError(s"Variable $name of type ${localDef.tpe} "+
- s"typed as ${tree.tpe}")
- if (mutable != localDef.mutable)
- reportError(s"Variable $name with mutable=${localDef.mutable} "+
- s"marked as mutable=$mutable")
- }
-
- case This() =>
- if (!isSubtype(env.thisTpe, tree.tpe))
- reportError(s"this of type ${env.thisTpe} typed as ${tree.tpe}")
-
- case Closure(captureParams, params, body, captureValues) =>
- if (captureParams.size != captureValues.size)
- reportError("Mismatched size for captures: "+
- s"${captureParams.size} params vs ${captureValues.size} values")
-
- for ((ParamDef(name, ctpe, mutable), value) <- captureParams zip captureValues) {
- if (mutable)
- reportError(s"Capture parameter $name cannot be mutable")
- if (ctpe == NoType)
- reportError(s"Parameter $name has type NoType")
- else
- typecheckExpect(value, env, ctpe)
- }
-
- for (ParamDef(name, ptpe, mutable) <- params) {
- if (ptpe == NoType)
- reportError(s"Parameter $name has type NoType")
- else if (ptpe != AnyType)
- reportError(s"Closure parameter $name has type $ptpe instead of any")
- }
-
- val bodyEnv = Env.fromSignature(
- AnyType, captureParams ++ params, AnyType)
- typecheckExpect(body, bodyEnv, AnyType)
-
- case _ =>
- reportError(s"Invalid expression tree")
- }
-
- tree.tpe
- }
-
- def inferMethodType(encodedName: String, inTraitImpl: Boolean)(
- implicit ctx: ErrorContext): (List[Type], Type) = {
- def dropPrivateMarker(params: List[String]): List[String] =
- if (params.nonEmpty && params.head.startsWith("p")) params.tail
- else params
-
- if (isConstructorName(encodedName)) {
- assert(!inTraitImpl, "Trait impl should not have a constructor")
- val params = dropPrivateMarker(
- encodedName.stripPrefix("init___").split("__").toList)
- if (params == List("")) (Nil, NoType)
- else (params.map(decodeType), NoType)
- } else if (isReflProxyName(encodedName)) {
- assert(!inTraitImpl, "Trait impl should not have refl proxy methods")
- val params = dropPrivateMarker(encodedName.split("__").toList.tail)
- (params.map(decodeType), AnyType)
- } else {
- val paramsAndResult0 =
- encodedName.split("__").toList.tail
- val paramsAndResult1 =
- if (inTraitImpl) paramsAndResult0.tail
- else paramsAndResult0
- val paramsAndResult =
- dropPrivateMarker(paramsAndResult1)
- (paramsAndResult.init.map(decodeType), decodeType(paramsAndResult.last))
- }
- }
-
- def decodeType(encodedName: String)(implicit ctx: ErrorContext): Type = {
- if (encodedName.isEmpty) NoType
- else if (encodedName.charAt(0) == 'A') {
- // array type
- val dims = encodedName.indexWhere(_ != 'A')
- val base = encodedName.substring(dims)
- ArrayType(base, dims)
- } else if (encodedName.length == 1) {
- (encodedName.charAt(0): @switch) match {
- case 'V' => NoType
- case 'Z' => BooleanType
- case 'C' | 'B' | 'S' | 'I' => IntType
- case 'J' => LongType
- case 'F' => FloatType
- case 'D' => DoubleType
- case 'O' => AnyType
- case 'T' => ClassType(StringClass) // NOT StringType
- }
- } else if (encodedName == "sr_Nothing$") {
- NothingType
- } else if (encodedName == "sr_Null$") {
- NullType
- } else {
- val clazz = lookupClass(encodedName)
- if (clazz.kind == ClassKind.RawJSType) AnyType
- else ClassType(encodedName)
- }
- }
-
- def arrayElemType(arrayType: ArrayType)(implicit ctx: ErrorContext): Type = {
- if (arrayType.dimensions == 1) decodeType(arrayType.baseClassName)
- else ArrayType(arrayType.baseClassName, arrayType.dimensions-1)
- }
-
- def reportError(msg: String)(implicit ctx: ErrorContext): Unit = {
- logger.error(s"$ctx: $msg")
- _errorCount += 1
- }
-
- def lookupClass(className: String)(implicit ctx: ErrorContext): CheckedClass = {
- classes.getOrElseUpdate(className, {
- reportError(s"Cannot find class $className")
- new CheckedClass(className, ClassKind.Class,
- Some(ObjectClass), Set(ObjectClass))
- })
- }
-
- def lookupClass(classType: ClassType)(implicit ctx: ErrorContext): CheckedClass =
- lookupClass(classType.className)
-
- def isSubclass(lhs: String, rhs: String)(implicit ctx: ErrorContext): Boolean = {
- lookupClass(lhs).isSubclass(lookupClass(rhs))
- }
-
- def isSubtype(lhs: Type, rhs: Type)(implicit ctx: ErrorContext): Boolean = {
- Types.isSubtype(lhs, rhs)(isSubclass)
- }
-
- class Env(
- /** Type of `this`. Can be NoType. */
- val thisTpe: Type,
- /** Local variables in scope (including through closures). */
- val locals: Map[String, LocalDef],
- /** Return types by label. */
- val returnTypes: Map[Option[String], Type]
- ) {
- def withThis(thisTpe: Type): Env =
- new Env(thisTpe, this.locals, this.returnTypes)
-
- def withLocal(localDef: LocalDef): Env =
- new Env(thisTpe, locals + (localDef.name -> localDef), returnTypes)
-
- def withLocals(localDefs: TraversableOnce[LocalDef]): Env =
- new Env(thisTpe, locals ++ localDefs.map(d => d.name -> d), returnTypes)
-
- def withReturnType(returnType: Type): Env =
- new Env(this.thisTpe, this.locals, returnTypes + (None -> returnType))
-
- def withLabeledReturnType(label: String, returnType: Type): Env =
- new Env(this.thisTpe, this.locals, returnTypes + (Some(label) -> returnType))
-
- def withArgumentsVar(pos: Position): Env =
- withLocal(LocalDef("arguments", AnyType, mutable = false)(pos))
- }
-
- object Env {
- val empty: Env = new Env(NoType, Map.empty, Map.empty)
-
- def fromSignature(thisType: Type, params: List[ParamDef],
- resultType: Type): Env = {
- val paramLocalDefs =
- for (p @ ParamDef(name, tpe, mutable) <- params) yield
- name.name -> LocalDef(name.name, tpe, mutable)(p.pos)
- new Env(thisType, paramLocalDefs.toMap,
- Map(None -> (if (resultType == NoType) AnyType else resultType)))
- }
- }
-
- class CheckedClass(
- val name: String,
- val kind: ClassKind,
- val superClassName: Option[String],
- val ancestors: Set[String],
- _fields: TraversableOnce[CheckedField] = Nil) {
-
- val fields = _fields.map(f => f.name -> f).toMap
-
- lazy val superClass = superClassName.map(classes)
-
- def this(classDef: ClassDef) = {
- this(classDef.name.name, classDef.kind,
- classDef.parent.map(_.name),
- classDef.ancestors.map(_.name).toSet,
- CheckedClass.collectFields(classDef))
- }
-
- def isSubclass(that: CheckedClass): Boolean =
- this == that || ancestors.contains(that.name)
-
- def isAncestorOfHijackedClass: Boolean =
- AncestorsOfHijackedClasses.contains(name)
-
- def lookupField(name: String): Option[CheckedField] =
- fields.get(name).orElse(superClass.flatMap(_.lookupField(name)))
- }
-
- object CheckedClass {
- private def collectFields(classDef: ClassDef) = {
- classDef.defs collect {
- case VarDef(Ident(name, _), tpe, mutable, _) =>
- new CheckedField(name, tpe, mutable)
- }
- }
- }
-
- class CheckedField(val name: String, val tpe: Type, val mutable: Boolean)
-}
-
-object IRChecker {
- private final class ErrorContext(val tree: Tree) extends AnyVal {
- override def toString(): String = {
- val pos = tree.pos
- s"${pos.source}(${pos.line+1}:${pos.column+1}:${tree.getClass.getSimpleName})"
- }
-
- def pos: Position = tree.pos
- }
-
- private object ErrorContext {
- implicit def tree2errorContext(tree: Tree): ErrorContext =
- ErrorContext(tree)
-
- def apply(tree: Tree): ErrorContext =
- new ErrorContext(tree)
- }
-
- private def isConstructorName(name: String): Boolean =
- name.startsWith("init___")
-
- private def isReflProxyName(name: String): Boolean =
- name.endsWith("__") && !isConstructorName(name)
-
- case class LocalDef(name: String, tpe: Type, mutable: Boolean)(val pos: Position)
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IncOptimizer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IncOptimizer.scala
deleted file mode 100644
index d115618..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/IncOptimizer.scala
+++ /dev/null
@@ -1,158 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.optimizer
-
-import scala.collection.{GenTraversableOnce, GenIterable}
-import scala.collection.mutable
-
-import scala.scalajs.tools.sem.Semantics
-
-class IncOptimizer(semantics: Semantics) extends GenIncOptimizer(semantics) {
-
- protected object CollOps extends GenIncOptimizer.AbsCollOps {
- type Map[K, V] = mutable.Map[K, V]
- type ParMap[K, V] = mutable.Map[K, V]
- type AccMap[K, V] = mutable.Map[K, mutable.ListBuffer[V]]
- type ParIterable[V] = mutable.ListBuffer[V]
- type Addable[V] = mutable.ListBuffer[V]
-
- def emptyAccMap[K, V]: AccMap[K, V] = mutable.Map.empty
- def emptyMap[K, V]: Map[K, V] = mutable.Map.empty
- def emptyParMap[K, V]: ParMap[K, V] = mutable.Map.empty
- def emptyParIterable[V]: ParIterable[V] = mutable.ListBuffer.empty
-
- // Operations on ParMap
- def put[K, V](map: ParMap[K, V], k: K, v: V): Unit = map.put(k, v)
- def remove[K, V](map: ParMap[K, V], k: K): Option[V] = map.remove(k)
-
- def retain[K, V](map: ParMap[K, V])(p: (K, V) => Boolean): Unit =
- map.retain(p)
-
- // Operations on AccMap
- def acc[K, V](map: AccMap[K, V], k: K, v: V): Unit =
- map.getOrElseUpdate(k, mutable.ListBuffer.empty) += v
-
- def getAcc[K, V](map: AccMap[K, V], k: K): GenIterable[V] =
- map.getOrElse(k, Nil)
-
- def parFlatMapKeys[A, B](map: AccMap[A, _])(
- f: A => GenTraversableOnce[B]): GenIterable[B] =
- map.keys.flatMap(f).toList
-
- // Operations on ParIterable
- def prepAdd[V](it: ParIterable[V]): Addable[V] = it
- def add[V](addable: Addable[V], v: V): Unit = addable += v
- def finishAdd[V](addable: Addable[V]): ParIterable[V] = addable
- }
-
- private val _interfaces = mutable.Map.empty[String, InterfaceType]
- protected def getInterface(encodedName: String): InterfaceType =
- _interfaces.getOrElseUpdate(encodedName, new SeqInterfaceType(encodedName))
-
- private val methodsToProcess = mutable.ListBuffer.empty[MethodImpl]
- protected def scheduleMethod(method: MethodImpl): Unit =
- methodsToProcess += method
-
- protected def newMethodImpl(owner: MethodContainer,
- encodedName: String): MethodImpl = new SeqMethodImpl(owner, encodedName)
-
- protected def processAllTaggedMethods(): Unit = {
- logProcessingMethods(methodsToProcess.count(!_.deleted))
- for (method <- methodsToProcess)
- method.process()
- methodsToProcess.clear()
- }
-
- private class SeqInterfaceType(encName: String) extends InterfaceType(encName) {
- private val ancestorsAskers = mutable.Set.empty[MethodImpl]
- private val dynamicCallers = mutable.Map.empty[String, mutable.Set[MethodImpl]]
- private val staticCallers = mutable.Map.empty[String, mutable.Set[MethodImpl]]
-
- private var _ancestors: List[String] = encodedName :: Nil
-
- private var _instantiatedSubclasses: Set[Class] = Set.empty
-
- def instantiatedSubclasses: Iterable[Class] = _instantiatedSubclasses
-
- def addInstantiatedSubclass(x: Class): Unit =
- _instantiatedSubclasses += x
-
- def removeInstantiatedSubclass(x: Class): Unit =
- _instantiatedSubclasses -= x
-
- def ancestors: List[String] = _ancestors
-
- def ancestors_=(v: List[String]): Unit = {
- if (v != _ancestors) {
- _ancestors = v
- ancestorsAskers.foreach(_.tag())
- ancestorsAskers.clear()
- }
- }
-
- def registerAskAncestors(asker: MethodImpl): Unit =
- ancestorsAskers += asker
-
- def registerDynamicCaller(methodName: String, caller: MethodImpl): Unit =
- dynamicCallers.getOrElseUpdate(methodName, mutable.Set.empty) += caller
-
- def registerStaticCaller(methodName: String, caller: MethodImpl): Unit =
- staticCallers.getOrElseUpdate(methodName, mutable.Set.empty) += caller
-
- def unregisterDependee(dependee: MethodImpl): Unit = {
- ancestorsAskers -= dependee
- dynamicCallers.values.foreach(_ -= dependee)
- staticCallers.values.foreach(_ -= dependee)
- }
-
- def tagDynamicCallersOf(methodName: String): Unit =
- dynamicCallers.remove(methodName).foreach(_.foreach(_.tag()))
-
- def tagStaticCallersOf(methodName: String): Unit =
- staticCallers.remove(methodName).foreach(_.foreach(_.tag()))
- }
-
- private class SeqMethodImpl(owner: MethodContainer,
- encodedName: String) extends MethodImpl(owner, encodedName) {
-
- private val bodyAskers = mutable.Set.empty[MethodImpl]
-
- def registerBodyAsker(asker: MethodImpl): Unit =
- bodyAskers += asker
-
- def unregisterDependee(dependee: MethodImpl): Unit =
- bodyAskers -= dependee
-
- def tagBodyAskers(): Unit = {
- bodyAskers.foreach(_.tag())
- bodyAskers.clear()
- }
-
- private var _registeredTo: List[Unregisterable] = Nil
- private var tagged = false
-
- protected def registeredTo(intf: Unregisterable): Unit =
- _registeredTo ::= intf
-
- protected def unregisterFromEverywhere(): Unit = {
- _registeredTo.foreach(_.unregisterDependee(this))
- _registeredTo = Nil
- }
-
- protected def protectTag(): Boolean = {
- val res = !tagged
- tagged = true
- res
- }
- protected def resetTag(): Unit = tagged = false
-
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/JSTreeBuilder.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/JSTreeBuilder.scala
deleted file mode 100644
index 3d37a56..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/JSTreeBuilder.scala
+++ /dev/null
@@ -1,16 +0,0 @@
-package scala.scalajs.tools.optimizer
-
-import scala.scalajs.ir
-import scala.scalajs.tools.javascript
-
-/** An abstract builder taking IR or JSTrees */
-trait JSTreeBuilder {
- /** Add a JavaScript tree representing a statement.
- * The tree must be a valid JavaScript tree (typically obtained by
- * desugaring a full-fledged IR tree).
- */
- def addJSTree(tree: javascript.Trees.Tree): Unit
-
- /** Completes the builder. */
- def complete(): Unit = ()
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala
deleted file mode 100644
index 364038b..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/OptimizerCore.scala
+++ /dev/null
@@ -1,3572 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.optimizer
-
-import scala.language.implicitConversions
-
-import scala.annotation.{switch, tailrec}
-
-import scala.collection.mutable
-
-import scala.util.control.{NonFatal, ControlThrowable, TailCalls}
-import scala.util.control.TailCalls.{done => _, _} // done is a too generic term
-
-import scala.scalajs.ir._
-import Definitions.{ObjectClass, isConstructorName, isReflProxyName}
-import Infos.OptimizerHints
-import Trees._
-import Types._
-
-import scala.scalajs.tools.sem.Semantics
-import scala.scalajs.tools.javascript.LongImpl
-import scala.scalajs.tools.logging._
-
-/** Optimizer core.
- * Designed to be "mixed in" [[IncOptimizer#MethodImpl#Optimizer]].
- * This is the core of the optimizer. It contains all the smart things the
- * optimizer does. To perform inlining, it relies on abstract protected
- * methods to identify the target of calls.
- */
-private[optimizer] abstract class OptimizerCore(semantics: Semantics) {
- import OptimizerCore._
-
- type MethodID <: AbstractMethodID
-
- val myself: MethodID
-
- /** Returns the body of a method. */
- protected def getMethodBody(method: MethodID): MethodDef
-
- /** Returns the list of possible targets for a dynamically linked call. */
- protected def dynamicCall(intfName: String,
- methodName: String): List[MethodID]
-
- /** Returns the target of a static call. */
- protected def staticCall(className: String,
- methodName: String): Option[MethodID]
-
- /** Returns the target of a trait impl call. */
- protected def traitImplCall(traitImplName: String,
- methodName: String): Option[MethodID]
-
- /** Returns the list of ancestors of a class or interface. */
- protected def getAncestorsOf(encodedName: String): List[String]
-
- /** Tests whether the given module class has an elidable accessor.
- * In other words, whether it is safe to discard a LoadModule of that
- * module class which is not used.
- */
- protected def hasElidableModuleAccessor(moduleClassName: String): Boolean
-
- /** Tests whether the given class is inlineable.
- * @return None if the class is not inlineable, Some(value) if it is, where
- * value is a RecordValue with the initial value of its fields.
- */
- protected def tryNewInlineableClass(className: String): Option[RecordValue]
-
- private val usedLocalNames = mutable.Set.empty[String]
- private val usedLabelNames = mutable.Set.empty[String]
- private var statesInUse: List[State[_]] = Nil
-
- private var disableOptimisticOptimizations: Boolean = false
- private var rollbacksCount: Int = 0
-
- private val attemptedInlining = mutable.ListBuffer.empty[MethodID]
-
- private var curTrampolineId = 0
-
- def optimize(thisType: Type, originalDef: MethodDef): (MethodDef, Infos.MethodInfo) = {
- try {
- val MethodDef(name, params, resultType, body) = originalDef
- val (newParams, newBody) = try {
- transformIsolatedBody(Some(myself), thisType, params, resultType, body)
- } catch {
- case _: TooManyRollbacksException =>
- usedLocalNames.clear()
- usedLabelNames.clear()
- statesInUse = Nil
- disableOptimisticOptimizations = true
- transformIsolatedBody(Some(myself), thisType, params, resultType, body)
- }
- val m = MethodDef(name, newParams, resultType, newBody)(None)(originalDef.pos)
- val info = recreateInfo(m)
- (m, info)
- } catch {
- case NonFatal(cause) =>
- throw new OptimizeException(myself, attemptedInlining.distinct.toList, cause)
- case e: Throwable =>
- // This is a fatal exception. Don't wrap, just output debug info error
- Console.err.println(exceptionMsg(myself, attemptedInlining.distinct.toList))
- throw e
- }
- }
-
- private def withState[A, B](state: State[A])(body: => B): B = {
- statesInUse ::= state
- try body
- finally statesInUse = statesInUse.tail
- }
-
- private def freshLocalName(base: String): String =
- freshNameGeneric(usedLocalNames, base)
-
- private def freshLabelName(base: String): String =
- freshNameGeneric(usedLabelNames, base)
-
- private val isReserved = isKeyword ++ Seq("arguments", "eval", "ScalaJS")
-
- private def freshNameGeneric(usedNames: mutable.Set[String], base: String): String = {
- val result = if (!usedNames.contains(base) && !isReserved(base)) {
- base
- } else {
- var i = 1
- while (usedNames.contains(base + "$" + i))
- i += 1
- base + "$" + i
- }
- usedNames += result
- result
- }
-
- private def tryOrRollback(body: CancelFun => TailRec[Tree])(
- fallbackFun: () => TailRec[Tree]): TailRec[Tree] = {
- if (disableOptimisticOptimizations) {
- fallbackFun()
- } else {
- val trampolineId = curTrampolineId
- val savedUsedLocalNames = usedLocalNames.toSet
- val savedUsedLabelNames = usedLabelNames.toSet
- val savedStates = statesInUse.map(_.makeBackup())
-
- body { () =>
- throw new RollbackException(trampolineId, savedUsedLocalNames,
- savedUsedLabelNames, savedStates, fallbackFun)
- }
- }
- }
-
- private def isSubclass(lhs: String, rhs: String): Boolean =
- getAncestorsOf(lhs).contains(rhs)
-
- private val isSubclassFun = isSubclass _
- private def isSubtype(lhs: Type, rhs: Type): Boolean =
- Types.isSubtype(lhs, rhs)(isSubclassFun)
-
- /** Transforms a statement.
- *
- * For valid expression trees, it is always the case that
- * {{{
- * transformStat(tree)
- * ===
- * pretransformExpr(tree)(finishTransformStat)
- * }}}
- */
- private def transformStat(tree: Tree)(implicit scope: Scope): Tree =
- transform(tree, isStat = true)
-
- /** Transforms an expression.
- *
- * It is always the case that
- * {{{
- * transformExpr(tree)
- * ===
- * pretransformExpr(tree)(finishTransformExpr)
- * }}}
- */
- private def transformExpr(tree: Tree)(implicit scope: Scope): Tree =
- transform(tree, isStat = false)
-
- /** Transforms a tree. */
- private def transform(tree: Tree, isStat: Boolean)(
- implicit scope: Scope): Tree = {
-
- @inline implicit def pos = tree.pos
- val result = tree match {
- // Definitions
-
- case VarDef(_, _, _, rhs) =>
- /* A local var that is last (or alone) in its block is not terribly
- * useful. Get rid of it.
- * (Non-last VarDefs in blocks are handled in transformBlock.)
- */
- transformStat(rhs)
-
- // Control flow constructs
-
- case tree: Block =>
- transformBlock(tree, isStat)
-
- case Labeled(ident @ Ident(label, _), tpe, body) =>
- trampoline {
- returnable(label, if (isStat) NoType else tpe, body, isStat,
- usePreTransform = false)(finishTransform(isStat))
- }
-
- case Assign(lhs, rhs) =>
- val cont = { (preTransLhs: PreTransform) =>
- resolveLocalDef(preTransLhs) match {
- case PreTransRecordTree(lhsTree, lhsOrigType, lhsCancelFun) =>
- val recordType = lhsTree.tpe.asInstanceOf[RecordType]
- pretransformNoLocalDef(rhs) {
- case PreTransRecordTree(rhsTree, rhsOrigType, rhsCancelFun) =>
- if (rhsTree.tpe != recordType || rhsOrigType != lhsOrigType)
- lhsCancelFun()
- TailCalls.done(Assign(lhsTree, rhsTree))
- case _ =>
- lhsCancelFun()
- }
- case PreTransTree(lhsTree, _) =>
- TailCalls.done(Assign(lhsTree, transformExpr(rhs)))
- }
- }
- trampoline {
- lhs match {
- case lhs: Select =>
- pretransformSelectCommon(lhs, isLhsOfAssign = true)(cont)
- case _ =>
- pretransformExpr(lhs)(cont)
- }
- }
-
- case Return(expr, optLabel) =>
- val optInfo = optLabel match {
- case Some(Ident(label, _)) =>
- Some(scope.env.labelInfos(label))
- case None =>
- scope.env.labelInfos.get("")
- }
- optInfo.fold[Tree] {
- Return(transformExpr(expr), None)
- } { info =>
- val newOptLabel = Some(Ident(info.newName, None))
- if (!info.acceptRecords) {
- val newExpr = transformExpr(expr)
- info.returnedTypes.value ::= (newExpr.tpe, RefinedType(newExpr.tpe))
- Return(newExpr, newOptLabel)
- } else trampoline {
- pretransformNoLocalDef(expr) { texpr =>
- texpr match {
- case PreTransRecordTree(newExpr, origType, cancelFun) =>
- info.returnedTypes.value ::= (newExpr.tpe, origType)
- TailCalls.done(Return(newExpr, newOptLabel))
- case PreTransTree(newExpr, tpe) =>
- info.returnedTypes.value ::= (newExpr.tpe, tpe)
- TailCalls.done(Return(newExpr, newOptLabel))
- }
- }
- }
- }
-
- case If(cond, thenp, elsep) =>
- val newCond = transformExpr(cond)
- newCond match {
- case BooleanLiteral(condValue) =>
- if (condValue) transform(thenp, isStat)
- else transform(elsep, isStat)
- case _ =>
- val newThenp = transform(thenp, isStat)
- val newElsep = transform(elsep, isStat)
- val refinedType =
- constrainedLub(newThenp.tpe, newElsep.tpe, tree.tpe)
- foldIf(newCond, newThenp, newElsep)(refinedType)
- }
-
- case While(cond, body, optLabel) =>
- val newCond = transformExpr(cond)
- newCond match {
- case BooleanLiteral(false) => Skip()
- case _ =>
- optLabel match {
- case None =>
- While(newCond, transformStat(body), None)
-
- case Some(labelIdent @ Ident(label, _)) =>
- val newLabel = freshLabelName(label)
- val info = new LabelInfo(newLabel, acceptRecords = false)
- While(newCond, {
- val bodyScope = scope.withEnv(
- scope.env.withLabelInfo(label, info))
- transformStat(body)(bodyScope)
- }, Some(Ident(newLabel, None)(labelIdent.pos)))
- }
- }
-
- case DoWhile(body, cond, None) =>
- val newBody = transformStat(body)
- val newCond = transformExpr(cond)
- newCond match {
- case BooleanLiteral(false) => newBody
- case _ => DoWhile(newBody, newCond, None)
- }
-
- case Try(block, errVar, EmptyTree, finalizer) =>
- val newBlock = transform(block, isStat)
- val newFinalizer = transformStat(finalizer)
- Try(newBlock, errVar, EmptyTree, newFinalizer)(newBlock.tpe)
-
- case Try(block, errVar @ Ident(name, originalName), handler, finalizer) =>
- val newBlock = transform(block, isStat)
-
- val newName = freshLocalName(name)
- val newOriginalName = originalName.orElse(Some(name))
- val localDef = LocalDef(RefinedType(AnyType), true,
- ReplaceWithVarRef(newName, newOriginalName, new SimpleState(true), None))
- val newHandler = {
- val handlerScope = scope.withEnv(scope.env.withLocalDef(name, localDef))
- transform(handler, isStat)(handlerScope)
- }
-
- val newFinalizer = transformStat(finalizer)
-
- val refinedType = constrainedLub(newBlock.tpe, newHandler.tpe, tree.tpe)
- Try(newBlock, Ident(newName, newOriginalName)(errVar.pos),
- newHandler, newFinalizer)(refinedType)
-
- case Throw(expr) =>
- Throw(transformExpr(expr))
-
- case Continue(optLabel) =>
- val newOptLabel = optLabel map { label =>
- Ident(scope.env.labelInfos(label.name).newName, None)(label.pos)
- }
- Continue(newOptLabel)
-
- case Match(selector, cases, default) =>
- val newSelector = transformExpr(selector)
- newSelector match {
- case newSelector: Literal =>
- val body = cases collectFirst {
- case (alts, body) if alts.exists(literal_===(_, newSelector)) => body
- } getOrElse default
- transform(body, isStat)
- case _ =>
- Match(newSelector,
- cases map (c => (c._1, transform(c._2, isStat))),
- transform(default, isStat))(tree.tpe)
- }
-
- // Scala expressions
-
- case New(cls, ctor, args) =>
- New(cls, ctor, args map transformExpr)
-
- case StoreModule(cls, value) =>
- StoreModule(cls, transformExpr(value))
-
- case tree: Select =>
- trampoline {
- pretransformSelectCommon(tree, isLhsOfAssign = false)(
- finishTransform(isStat = false))
- }
-
- case tree: Apply =>
- trampoline {
- pretransformApply(tree, isStat, usePreTransform = false)(
- finishTransform(isStat))
- }
-
- case tree: StaticApply =>
- trampoline {
- pretransformStaticApply(tree, isStat, usePreTransform = false)(
- finishTransform(isStat))
- }
-
- case tree: TraitImplApply =>
- trampoline {
- pretransformTraitImplApply(tree, isStat, usePreTransform = false)(
- finishTransform(isStat))
- }
-
- case tree @ UnaryOp(_, arg) =>
- if (isStat) transformStat(arg)
- else transformUnaryOp(tree)
-
- case tree @ BinaryOp(op, lhs, rhs) =>
- if (isStat) Block(transformStat(lhs), transformStat(rhs))
- else transformBinaryOp(tree)
-
- case NewArray(tpe, lengths) =>
- NewArray(tpe, lengths map transformExpr)
-
- case ArrayValue(tpe, elems) =>
- ArrayValue(tpe, elems map transformExpr)
-
- case ArrayLength(array) =>
- ArrayLength(transformExpr(array))
-
- case ArraySelect(array, index) =>
- ArraySelect(transformExpr(array), transformExpr(index))(tree.tpe)
-
- case RecordValue(tpe, elems) =>
- RecordValue(tpe, elems map transformExpr)
-
- case IsInstanceOf(expr, ClassType(ObjectClass)) =>
- transformExpr(BinaryOp(BinaryOp.!==, expr, Null()))
-
- case IsInstanceOf(expr, tpe) =>
- trampoline {
- pretransformExpr(expr) { texpr =>
- val result = {
- if (isSubtype(texpr.tpe.base, tpe)) {
- if (texpr.tpe.isNullable)
- BinaryOp(BinaryOp.!==, finishTransformExpr(texpr), Null())
- else
- Block(finishTransformStat(texpr), BooleanLiteral(true))
- } else {
- if (texpr.tpe.isExact)
- Block(finishTransformStat(texpr), BooleanLiteral(false))
- else
- IsInstanceOf(finishTransformExpr(texpr), tpe)
- }
- }
- TailCalls.done(result)
- }
- }
-
- case AsInstanceOf(expr, ClassType(ObjectClass)) =>
- transformExpr(expr)
-
- case AsInstanceOf(expr, cls) =>
- trampoline {
- pretransformExpr(tree)(finishTransform(isStat))
- }
-
- case Unbox(arg, charCode) =>
- trampoline {
- pretransformExpr(arg) { targ =>
- foldUnbox(targ, charCode)(finishTransform(isStat))
- }
- }
-
- case GetClass(expr) =>
- GetClass(transformExpr(expr))
-
- // JavaScript expressions
-
- case JSNew(ctor, args) =>
- JSNew(transformExpr(ctor), args map transformExpr)
-
- case JSDotSelect(qualifier, item) =>
- JSDotSelect(transformExpr(qualifier), item)
-
- case JSBracketSelect(qualifier, item) =>
- JSBracketSelect(transformExpr(qualifier), transformExpr(item))
-
- case tree: JSFunctionApply =>
- trampoline {
- pretransformJSFunctionApply(tree, isStat, usePreTransform = false)(
- finishTransform(isStat))
- }
-
- case JSDotMethodApply(receiver, method, args) =>
- JSDotMethodApply(transformExpr(receiver), method,
- args map transformExpr)
-
- case JSBracketMethodApply(receiver, method, args) =>
- JSBracketMethodApply(transformExpr(receiver), transformExpr(method),
- args map transformExpr)
-
- case JSDelete(JSDotSelect(obj, prop)) =>
- JSDelete(JSDotSelect(transformExpr(obj), prop))
-
- case JSDelete(JSBracketSelect(obj, prop)) =>
- JSDelete(JSBracketSelect(transformExpr(obj), transformExpr(prop)))
-
- case JSUnaryOp(op, lhs) =>
- JSUnaryOp(op, transformExpr(lhs))
-
- case JSBinaryOp(op, lhs, rhs) =>
- JSBinaryOp(op, transformExpr(lhs), transformExpr(rhs))
-
- case JSArrayConstr(items) =>
- JSArrayConstr(items map transformExpr)
-
- case JSObjectConstr(fields) =>
- JSObjectConstr(fields map {
- case (name, value) => (name, transformExpr(value))
- })
-
- // Atomic expressions
-
- case _:VarRef | _:This =>
- trampoline {
- pretransformExpr(tree)(finishTransform(isStat))
- }
-
- case Closure(captureParams, params, body, captureValues) =>
- transformClosureCommon(captureParams, params, body,
- captureValues.map(transformExpr))
-
- // Trees that need not be transformed
-
- case _:Skip | _:Debugger | _:LoadModule |
- _:JSEnvInfo | _:Literal | EmptyTree =>
- tree
- }
-
- if (isStat) keepOnlySideEffects(result)
- else result
- }
-
- private def transformClosureCommon(captureParams: List[ParamDef],
- params: List[ParamDef], body: Tree, newCaptureValues: List[Tree])(
- implicit pos: Position): Closure = {
-
- val (allNewParams, newBody) =
- transformIsolatedBody(None, AnyType, captureParams ++ params, AnyType, body)
- val (newCaptureParams, newParams) =
- allNewParams.splitAt(captureParams.size)
-
- Closure(newCaptureParams, newParams, newBody, newCaptureValues)
- }
-
- private def transformBlock(tree: Block, isStat: Boolean)(
- implicit scope: Scope): Tree = {
- def transformList(stats: List[Tree])(
- implicit scope: Scope): Tree = stats match {
- case last :: Nil =>
- transform(last, isStat)
-
- case (VarDef(Ident(name, originalName), vtpe, mutable, rhs)) :: rest =>
- trampoline {
- pretransformExpr(rhs) { trhs =>
- withBinding(Binding(name, originalName, vtpe, mutable, trhs)) {
- (restScope, cont1) =>
- val newRest = transformList(rest)(restScope)
- cont1(PreTransTree(newRest, RefinedType(newRest.tpe)))
- } (finishTransform(isStat))
- }
- }
-
- case stat :: rest =>
- val transformedStat = transformStat(stat)
- if (transformedStat.tpe == NothingType) transformedStat
- else Block(transformedStat, transformList(rest))(stat.pos)
-
- case Nil => // silence the exhaustivity warning in a sensible way
- Skip()(tree.pos)
- }
- transformList(tree.stats)(scope)
- }
-
- /** Pretransforms a list of trees as a list of [[PreTransform]]s.
- * This is a convenience method to use pretransformExpr on a list.
- */
- private def pretransformExprs(trees: List[Tree])(
- cont: List[PreTransform] => TailRec[Tree])(
- implicit scope: Scope): TailRec[Tree] = {
- trees match {
- case first :: rest =>
- pretransformExpr(first) { tfirst =>
- pretransformExprs(rest) { trest =>
- cont(tfirst :: trest)
- }
- }
-
- case Nil =>
- cont(Nil)
- }
- }
-
- /** Pretransforms two trees as a pair of [[PreTransform]]s.
- * This is a convenience method to use pretransformExpr on two trees.
- */
- private def pretransformExprs(tree1: Tree, tree2: Tree)(
- cont: (PreTransform, PreTransform) => TailRec[Tree])(
- implicit scope: Scope): TailRec[Tree] = {
- pretransformExpr(tree1) { ttree1 =>
- pretransformExpr(tree2) { ttree2 =>
- cont(ttree1, ttree2)
- }
- }
- }
-
- /** Pretransforms a tree and a list of trees as [[PreTransform]]s.
- * This is a convenience method to use pretransformExpr.
- */
- private def pretransformExprs(first: Tree, rest: List[Tree])(
- cont: (PreTransform, List[PreTransform]) => TailRec[Tree])(
- implicit scope: Scope): TailRec[Tree] = {
- pretransformExpr(first) { tfirst =>
- pretransformExprs(rest) { trest =>
- cont(tfirst, trest)
- }
- }
- }
-
- /** Pretransforms a tree to get a refined type while avoiding to force
- * things we might be able to optimize by folding and aliasing.
- */
- private def pretransformExpr(tree: Tree)(cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = tailcall {
- @inline implicit def pos = tree.pos
-
- tree match {
- case tree: Block =>
- pretransformBlock(tree)(cont)
-
- case VarRef(Ident(name, _), _) =>
- val localDef = scope.env.localDefs.getOrElse(name,
- sys.error(s"Cannot find local def '$name' at $pos\n" +
- s"While optimizing $myself\n" +
- s"Env is ${scope.env}\nInlining ${scope.implsBeingInlined}"))
- cont(PreTransLocalDef(localDef))
-
- case This() =>
- val localDef = scope.env.localDefs.getOrElse("this",
- sys.error(s"Found invalid 'this' at $pos\n" +
- s"While optimizing $myself\n" +
- s"Env is ${scope.env}\nInlining ${scope.implsBeingInlined}"))
- cont(PreTransLocalDef(localDef))
-
- case If(cond, thenp, elsep) =>
- val newCond = transformExpr(cond)
- newCond match {
- case BooleanLiteral(condValue) =>
- if (condValue) pretransformExpr(thenp)(cont)
- else pretransformExpr(elsep)(cont)
- case _ =>
- tryOrRollback { cancelFun =>
- pretransformNoLocalDef(thenp) { tthenp =>
- pretransformNoLocalDef(elsep) { telsep =>
- (tthenp, telsep) match {
- case (PreTransRecordTree(thenTree, thenOrigType, thenCancelFun),
- PreTransRecordTree(elseTree, elseOrigType, elseCancelFun)) =>
- val commonType =
- if (thenTree.tpe == elseTree.tpe &&
- thenOrigType == elseOrigType) thenTree.tpe
- else cancelFun()
- val refinedOrigType =
- constrainedLub(thenOrigType, elseOrigType, tree.tpe)
- cont(PreTransRecordTree(
- If(newCond, thenTree, elseTree)(commonType),
- refinedOrigType,
- cancelFun))
-
- case (PreTransRecordTree(thenTree, thenOrigType, thenCancelFun), _)
- if telsep.tpe.isNothingType =>
- cont(PreTransRecordTree(
- If(newCond, thenTree, finishTransformExpr(telsep))(thenTree.tpe),
- thenOrigType,
- thenCancelFun))
-
- case (_, PreTransRecordTree(elseTree, elseOrigType, elseCancelFun))
- if tthenp.tpe.isNothingType =>
- cont(PreTransRecordTree(
- If(newCond, finishTransformExpr(tthenp), elseTree)(elseTree.tpe),
- elseOrigType,
- elseCancelFun))
-
- case _ =>
- val newThenp = finishTransformExpr(tthenp)
- val newElsep = finishTransformExpr(telsep)
- val refinedType =
- constrainedLub(newThenp.tpe, newElsep.tpe, tree.tpe)
- cont(PreTransTree(
- foldIf(newCond, newThenp, newElsep)(refinedType)))
- }
- }
- }
- } { () =>
- val newThenp = transformExpr(thenp)
- val newElsep = transformExpr(elsep)
- val refinedType =
- constrainedLub(newThenp.tpe, newElsep.tpe, tree.tpe)
- cont(PreTransTree(
- foldIf(newCond, newThenp, newElsep)(refinedType)))
- }
- }
-
- case Match(selector, cases, default) =>
- val newSelector = transformExpr(selector)
- newSelector match {
- case newSelector: Literal =>
- val body = cases collectFirst {
- case (alts, body) if alts.exists(literal_===(_, newSelector)) => body
- } getOrElse default
- pretransformExpr(body)(cont)
- case _ =>
- cont(PreTransTree(Match(newSelector,
- cases map (c => (c._1, transformExpr(c._2))),
- transformExpr(default))(tree.tpe)))
- }
-
- case Labeled(ident @ Ident(label, _), tpe, body) =>
- returnable(label, tpe, body, isStat = false, usePreTransform = true)(cont)
-
- case New(cls @ ClassType(className), ctor, args) =>
- tryNewInlineableClass(className) match {
- case Some(initialValue) =>
- pretransformExprs(args) { targs =>
- tryOrRollback { cancelFun =>
- inlineClassConstructor(
- new AllocationSite(tree),
- cls, initialValue, ctor, targs, cancelFun)(cont)
- } { () =>
- cont(PreTransTree(
- New(cls, ctor, targs.map(finishTransformExpr)),
- RefinedType(cls, isExact = true, isNullable = false)))
- }
- }
- case None =>
- cont(PreTransTree(
- New(cls, ctor, args.map(transformExpr)),
- RefinedType(cls, isExact = true, isNullable = false)))
- }
-
- case tree: Select =>
- pretransformSelectCommon(tree, isLhsOfAssign = false)(cont)
-
- case tree: Apply =>
- pretransformApply(tree, isStat = false,
- usePreTransform = true)(cont)
-
- case tree: StaticApply =>
- pretransformStaticApply(tree, isStat = false,
- usePreTransform = true)(cont)
-
- case tree: TraitImplApply =>
- pretransformTraitImplApply(tree, isStat = false,
- usePreTransform = true)(cont)
-
- case tree: JSFunctionApply =>
- pretransformJSFunctionApply(tree, isStat = false,
- usePreTransform = true)(cont)
-
- case AsInstanceOf(expr, tpe) =>
- pretransformExpr(expr) { texpr =>
- tpe match {
- case ClassType(ObjectClass) =>
- cont(texpr)
- case _ =>
- if (isSubtype(texpr.tpe.base, tpe)) {
- cont(texpr)
- } else {
- cont(PreTransTree(
- AsInstanceOf(finishTransformExpr(texpr), tpe)))
- }
- }
- }
-
- case Closure(captureParams, params, body, captureValues) =>
- pretransformExprs(captureValues) { tcaptureValues =>
- tryOrRollback { cancelFun =>
- val captureBindings = for {
- (ParamDef(Ident(name, origName), tpe, mutable), value) <-
- captureParams zip tcaptureValues
- } yield {
- Binding(name, origName, tpe, mutable, value)
- }
- withNewLocalDefs(captureBindings) { (captureLocalDefs, cont1) =>
- val alreadyUsedState = new SimpleState[Boolean](false)
- withState(alreadyUsedState) {
- val replacement = TentativeClosureReplacement(
- captureParams, params, body, captureLocalDefs,
- alreadyUsedState, cancelFun)
- val localDef = LocalDef(
- RefinedType(AnyType, isExact = false, isNullable = false),
- mutable = false,
- replacement)
- cont1(PreTransLocalDef(localDef))
- }
- } (cont)
- } { () =>
- val newClosure = transformClosureCommon(captureParams, params, body,
- tcaptureValues.map(finishTransformExpr))
- cont(PreTransTree(
- newClosure,
- RefinedType(AnyType, isExact = false, isNullable = false)))
- }
- }
-
- case _ =>
- val result = transformExpr(tree)
- cont(PreTransTree(result, RefinedType(result.tpe)))
- }
- }
-
- private def pretransformBlock(tree: Block)(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- def pretransformList(stats: List[Tree])(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = stats match {
- case last :: Nil =>
- pretransformExpr(last)(cont)
-
- case (VarDef(Ident(name, originalName), vtpe, mutable, rhs)) :: rest =>
- pretransformExpr(rhs) { trhs =>
- withBinding(Binding(name, originalName, vtpe, mutable, trhs)) {
- (restScope, cont1) =>
- pretransformList(rest)(cont1)(restScope)
- } (cont)
- }
-
- case stat :: rest =>
- implicit val pos = tree.pos
- val transformedStat = transformStat(stat)
- transformedStat match {
- case Skip() =>
- pretransformList(rest)(cont)
- case _ =>
- if (transformedStat.tpe == NothingType)
- cont(PreTransTree(transformedStat, RefinedType.Nothing))
- else {
- pretransformList(rest) { trest =>
- cont(PreTransBlock(transformedStat :: Nil, trest))
- }
- }
- }
-
- case Nil => // silence the exhaustivity warning in a sensible way
- TailCalls.done(Skip()(tree.pos))
- }
- pretransformList(tree.stats)(cont)(scope)
- }
-
- private def pretransformSelectCommon(tree: Select, isLhsOfAssign: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- val Select(qualifier, item, mutable) = tree
- pretransformExpr(qualifier) { preTransQual =>
- pretransformSelectCommon(tree.tpe, preTransQual, item, mutable,
- isLhsOfAssign)(cont)(scope, tree.pos)
- }
- }
-
- private def pretransformSelectCommon(expectedType: Type,
- preTransQual: PreTransform, item: Ident, mutable: Boolean,
- isLhsOfAssign: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = {
- preTransQual match {
- case PreTransLocalDef(LocalDef(_, _,
- InlineClassBeingConstructedReplacement(fieldLocalDefs, cancelFun))) =>
- val fieldLocalDef = fieldLocalDefs(item.name)
- if (!isLhsOfAssign || fieldLocalDef.mutable) {
- cont(PreTransLocalDef(fieldLocalDef))
- } else {
- /* This is an assignment to an immutable field of a inlineable class
- * being constructed, but that does not appear at the "top-level" of
- * one of its constructors. We cannot handle those, so we cancel.
- * (Assignments at the top-level are normal initializations of these
- * fields, and are transformed as vals in inlineClassConstructor.)
- */
- cancelFun()
- }
- case PreTransLocalDef(LocalDef(_, _,
- InlineClassInstanceReplacement(_, fieldLocalDefs, cancelFun))) =>
- val fieldLocalDef = fieldLocalDefs(item.name)
- if (!isLhsOfAssign || fieldLocalDef.mutable) {
- cont(PreTransLocalDef(fieldLocalDef))
- } else {
- /* In an ideal world, this should not happen (assigning to an
- * immutable field of an already constructed object). However, since
- * we cannot IR-check that this does not happen (see #1021), this is
- * effectively allowed by the IR spec. We are therefore not allowed
- * to crash. We cancel instead. This will become an actual field
- * (rather than an optimized local val) which is not considered pure
- * (for that same reason).
- */
- cancelFun()
- }
- case _ =>
- resolveLocalDef(preTransQual) match {
- case PreTransRecordTree(newQual, origType, cancelFun) =>
- val recordType = newQual.tpe.asInstanceOf[RecordType]
- val field = recordType.findField(item.name)
- val sel = Select(newQual, item, mutable)(field.tpe)
- sel.tpe match {
- case _: RecordType =>
- cont(PreTransRecordTree(sel, RefinedType(expectedType), cancelFun))
- case _ =>
- cont(PreTransTree(sel, RefinedType(sel.tpe)))
- }
-
- case PreTransTree(newQual, _) =>
- cont(PreTransTree(Select(newQual, item, mutable)(expectedType),
- RefinedType(expectedType)))
- }
- }
- }
-
- /** Resolves any LocalDef in a [[PreTransform]]. */
- private def resolveLocalDef(preTrans: PreTransform): PreTransGenTree = {
- implicit val pos = preTrans.pos
- preTrans match {
- case PreTransBlock(stats, result) =>
- resolveLocalDef(result) match {
- case PreTransRecordTree(tree, tpe, cancelFun) =>
- PreTransRecordTree(Block(stats :+ tree), tpe, cancelFun)
- case PreTransTree(tree, tpe) =>
- PreTransTree(Block(stats :+ tree), tpe)
- }
-
- case PreTransLocalDef(localDef @ LocalDef(tpe, mutable, replacement)) =>
- replacement match {
- case ReplaceWithRecordVarRef(name, originalName,
- recordType, used, cancelFun) =>
- used.value = true
- PreTransRecordTree(
- VarRef(Ident(name, originalName), mutable)(recordType),
- tpe, cancelFun)
-
- case InlineClassInstanceReplacement(recordType, fieldLocalDefs, cancelFun) =>
- if (!isImmutableType(recordType))
- cancelFun()
- PreTransRecordTree(
- RecordValue(recordType, recordType.fields.map(
- f => fieldLocalDefs(f.name).newReplacement)),
- tpe, cancelFun)
-
- case _ =>
- PreTransTree(localDef.newReplacement, localDef.tpe)
- }
-
- case preTrans: PreTransGenTree =>
- preTrans
- }
- }
-
- /** Combines pretransformExpr and resolveLocalDef in one convenience method. */
- private def pretransformNoLocalDef(tree: Tree)(
- cont: PreTransGenTree => TailRec[Tree])(
- implicit scope: Scope): TailRec[Tree] = {
- pretransformExpr(tree) { ttree =>
- cont(resolveLocalDef(ttree))
- }
- }
-
- /** Finishes a pretransform, either a statement or an expression. */
- private def finishTransform(isStat: Boolean): PreTransCont = { preTrans =>
- TailCalls.done {
- if (isStat) finishTransformStat(preTrans)
- else finishTransformExpr(preTrans)
- }
- }
-
- /** Finishes an expression pretransform to get a normal [[Tree]].
- * This method (together with finishTransformStat) must not be called more
- * than once per pretransform and per translation.
- * By "per translation", we mean in an alternative path through
- * `tryOrRollback`. It could still be called several times as long as
- * it is once in the 'try' part and once in the 'fallback' part.
- */
- private def finishTransformExpr(preTrans: PreTransform): Tree = {
- implicit val pos = preTrans.pos
- preTrans match {
- case PreTransBlock(stats, result) =>
- Block(stats :+ finishTransformExpr(result))
- case PreTransLocalDef(localDef) =>
- localDef.newReplacement
- case PreTransRecordTree(_, _, cancelFun) =>
- cancelFun()
- case PreTransTree(tree, _) =>
- tree
- }
- }
-
- /** Finishes a statement pretransform to get a normal [[Tree]].
- * This method (together with finishTransformExpr) must not be called more
- * than once per pretransform and per translation.
- * By "per translation", we mean in an alternative path through
- * `tryOrRollback`. It could still be called several times as long as
- * it is once in the 'try' part and once in the 'fallback' part.
- */
- private def finishTransformStat(stat: PreTransform): Tree = stat match {
- case PreTransBlock(stats, result) =>
- Block(stats :+ finishTransformStat(result))(stat.pos)
- case PreTransLocalDef(_) =>
- Skip()(stat.pos)
- case PreTransRecordTree(tree, _, _) =>
- keepOnlySideEffects(tree)
- case PreTransTree(tree, _) =>
- keepOnlySideEffects(tree)
- }
-
- /** Keeps only the side effects of a Tree (overapproximation). */
- private def keepOnlySideEffects(stat: Tree): Tree = stat match {
- case _:VarRef | _:This | _:Literal =>
- Skip()(stat.pos)
- case Block(init :+ last) =>
- Block(init :+ keepOnlySideEffects(last))(stat.pos)
- case LoadModule(ClassType(moduleClassName)) =>
- if (hasElidableModuleAccessor(moduleClassName)) Skip()(stat.pos)
- else stat
- case Select(LoadModule(ClassType(moduleClassName)), _, _) =>
- if (hasElidableModuleAccessor(moduleClassName)) Skip()(stat.pos)
- else stat
- case Closure(_, _, _, captureValues) =>
- Block(captureValues.map(keepOnlySideEffects))(stat.pos)
- case UnaryOp(_, arg) =>
- keepOnlySideEffects(arg)
- case If(cond, thenp, elsep) =>
- (keepOnlySideEffects(thenp), keepOnlySideEffects(elsep)) match {
- case (Skip(), Skip()) => keepOnlySideEffects(cond)
- case (newThenp, newElsep) => If(cond, newThenp, newElsep)(NoType)(stat.pos)
- }
- case BinaryOp(_, lhs, rhs) =>
- Block(keepOnlySideEffects(lhs), keepOnlySideEffects(rhs))(stat.pos)
- case RecordValue(_, elems) =>
- Block(elems.map(keepOnlySideEffects))(stat.pos)
- case _ =>
- stat
- }
-
- private def pretransformApply(tree: Apply, isStat: Boolean,
- usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- val Apply(receiver, methodIdent @ Ident(methodName, _), args) = tree
- implicit val pos = tree.pos
-
- pretransformExpr(receiver) { treceiver =>
- def treeNotInlined0(transformedArgs: List[Tree]) =
- cont(PreTransTree(Apply(finishTransformExpr(treceiver), methodIdent,
- transformedArgs)(tree.tpe)(tree.pos), RefinedType(tree.tpe)))
-
- def treeNotInlined = treeNotInlined0(args.map(transformExpr))
-
- treceiver.tpe.base match {
- case NothingType =>
- cont(treceiver)
- case NullType =>
- cont(PreTransTree(Block(
- finishTransformStat(treceiver),
- CallHelper("throwNullPointerException")(NothingType))))
- case _ =>
- if (isReflProxyName(methodName)) {
- // Never inline reflective proxies
- treeNotInlined
- } else {
- val cls = boxedClassForType(treceiver.tpe.base)
- val impls =
- if (treceiver.tpe.isExact) staticCall(cls, methodName).toList
- else dynamicCall(cls, methodName)
- val allocationSite = treceiver.tpe.allocationSite
- if (impls.isEmpty || impls.exists(impl =>
- scope.implsBeingInlined((allocationSite, impl)))) {
- // isEmpty could happen, have to leave it as is for the TypeError
- treeNotInlined
- } else if (impls.size == 1) {
- val target = impls.head
- pretransformExprs(args) { targs =>
- val intrinsicCode = getIntrinsicCode(target)
- if (intrinsicCode >= 0) {
- callIntrinsic(intrinsicCode, Some(treceiver), targs,
- isStat, usePreTransform)(cont)
- } else if (target.inlineable || shouldInlineBecauseOfArgs(treceiver :: targs)) {
- inline(allocationSite, Some(treceiver), targs, target,
- isStat, usePreTransform)(cont)
- } else {
- treeNotInlined0(targs.map(finishTransformExpr))
- }
- }
- } else {
- if (impls.forall(_.isTraitImplForwarder)) {
- val reference = impls.head
- val TraitImplApply(ClassType(traitImpl), Ident(methodName, _), _) =
- getMethodBody(reference).body
- if (!impls.tail.forall(getMethodBody(_).body match {
- case TraitImplApply(ClassType(`traitImpl`),
- Ident(`methodName`, _), _) => true
- case _ => false
- })) {
- // Not all calling the same method in the same trait impl
- treeNotInlined
- } else {
- pretransformExprs(args) { targs =>
- inline(allocationSite, Some(treceiver), targs, reference,
- isStat, usePreTransform)(cont)
- }
- }
- } else {
- // TODO? Inline multiple non-trait-impl-forwarder with the exact same body?
- treeNotInlined
- }
- }
- }
- }
- }
- }
-
- private def boxedClassForType(tpe: Type): String = (tpe: @unchecked) match {
- case ClassType(cls) => cls
- case AnyType => Definitions.ObjectClass
- case UndefType => Definitions.BoxedUnitClass
- case BooleanType => Definitions.BoxedBooleanClass
- case IntType => Definitions.BoxedIntegerClass
- case LongType => Definitions.BoxedLongClass
- case FloatType => Definitions.BoxedFloatClass
- case DoubleType => Definitions.BoxedDoubleClass
- case StringType => Definitions.StringClass
- case ArrayType(_, _) => Definitions.ObjectClass
- }
-
- private def pretransformStaticApply(tree: StaticApply, isStat: Boolean,
- usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- val StaticApply(receiver, clsType @ ClassType(cls),
- methodIdent @ Ident(methodName, _), args) = tree
- implicit val pos = tree.pos
-
- def treeNotInlined0(transformedReceiver: Tree, transformedArgs: List[Tree]) =
- cont(PreTransTree(StaticApply(transformedReceiver, clsType,
- methodIdent, transformedArgs)(tree.tpe), RefinedType(tree.tpe)))
-
- def treeNotInlined =
- treeNotInlined0(transformExpr(receiver), args.map(transformExpr))
-
- if (isReflProxyName(methodName)) {
- // Never inline reflective proxies
- treeNotInlined
- } else {
- val optTarget = staticCall(cls, methodName)
- if (optTarget.isEmpty) {
- // just in case
- treeNotInlined
- } else {
- val target = optTarget.get
- pretransformExprs(receiver, args) { (treceiver, targs) =>
- val intrinsicCode = getIntrinsicCode(target)
- if (intrinsicCode >= 0) {
- callIntrinsic(intrinsicCode, Some(treceiver), targs,
- isStat, usePreTransform)(cont)
- } else {
- val shouldInline =
- target.inlineable || shouldInlineBecauseOfArgs(treceiver :: targs)
- val allocationSite = treceiver.tpe.allocationSite
- val beingInlined =
- scope.implsBeingInlined((allocationSite, target))
-
- if (shouldInline && !beingInlined) {
- inline(allocationSite, Some(treceiver), targs, target,
- isStat, usePreTransform)(cont)
- } else {
- treeNotInlined0(finishTransformExpr(treceiver),
- targs.map(finishTransformExpr))
- }
- }
- }
- }
- }
- }
-
- private def pretransformTraitImplApply(tree: TraitImplApply, isStat: Boolean,
- usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- val TraitImplApply(implType @ ClassType(impl),
- methodIdent @ Ident(methodName, _), args) = tree
- implicit val pos = tree.pos
-
- def treeNotInlined0(transformedArgs: List[Tree]) =
- cont(PreTransTree(TraitImplApply(implType, methodIdent,
- transformedArgs)(tree.tpe), RefinedType(tree.tpe)))
-
- def treeNotInlined = treeNotInlined0(args.map(transformExpr))
-
- val optTarget = traitImplCall(impl, methodName)
- if (optTarget.isEmpty) {
- // just in case
- treeNotInlined
- } else {
- val target = optTarget.get
- pretransformExprs(args) { targs =>
- val intrinsicCode = getIntrinsicCode(target)
- if (intrinsicCode >= 0) {
- callIntrinsic(intrinsicCode, None, targs,
- isStat, usePreTransform)(cont)
- } else {
- val shouldInline =
- target.inlineable || shouldInlineBecauseOfArgs(targs)
- val allocationSite = targs.headOption.flatMap(_.tpe.allocationSite)
- val beingInlined =
- scope.implsBeingInlined((allocationSite, target))
-
- if (shouldInline && !beingInlined) {
- inline(allocationSite, None, targs, target,
- isStat, usePreTransform)(cont)
- } else {
- treeNotInlined0(targs.map(finishTransformExpr))
- }
- }
- }
- }
- }
-
- private def pretransformJSFunctionApply(tree: JSFunctionApply,
- isStat: Boolean, usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = {
- val JSFunctionApply(fun, args) = tree
- implicit val pos = tree.pos
-
- pretransformExpr(fun) { tfun =>
- tfun match {
- case PreTransLocalDef(LocalDef(_, false,
- closure @ TentativeClosureReplacement(
- captureParams, params, body, captureLocalDefs,
- alreadyUsed, cancelFun))) if !alreadyUsed.value =>
- alreadyUsed.value = true
- pretransformExprs(args) { targs =>
- inlineBody(
- Some(PreTransTree(Undefined())), // `this` is `undefined`
- captureParams ++ params, AnyType, body,
- captureLocalDefs.map(PreTransLocalDef(_)) ++ targs, isStat,
- usePreTransform)(cont)
- }
-
- case _ =>
- cont(PreTransTree(
- JSFunctionApply(finishTransformExpr(tfun), args.map(transformExpr))))
- }
- }
- }
-
- private def shouldInlineBecauseOfArgs(
- receiverAndArgs: List[PreTransform]): Boolean = {
- def isLikelyOptimizable(arg: PreTransform): Boolean = arg match {
- case PreTransBlock(_, result) =>
- isLikelyOptimizable(result)
-
- case PreTransLocalDef(localDef) =>
- localDef.replacement match {
- case TentativeClosureReplacement(_, _, _, _, _, _) => true
- case ReplaceWithRecordVarRef(_, _, _, _, _) => true
- case InlineClassBeingConstructedReplacement(_, _) => true
- case InlineClassInstanceReplacement(_, _, _) => true
- case _ => false
- }
-
- case PreTransRecordTree(_, _, _) =>
- true
-
- case _ =>
- arg.tpe.base match {
- case ClassType("s_Predef$$less$colon$less" | "s_Predef$$eq$colon$eq") =>
- true
- case _ =>
- false
- }
- }
- receiverAndArgs.exists(isLikelyOptimizable)
- }
-
- private def inline(allocationSite: Option[AllocationSite],
- optReceiver: Option[PreTransform],
- args: List[PreTransform], target: MethodID, isStat: Boolean,
- usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = {
-
- attemptedInlining += target
-
- val MethodDef(_, formals, resultType, body) = getMethodBody(target)
-
- body match {
- case Skip() =>
- assert(isStat, "Found Skip() in expression position")
- cont(PreTransTree(
- Block((optReceiver ++: args).map(finishTransformStat)),
- RefinedType.NoRefinedType))
-
- case _: Literal =>
- cont(PreTransTree(
- Block((optReceiver ++: args).map(finishTransformStat) :+ body),
- RefinedType(body.tpe)))
-
- case This() if args.isEmpty =>
- assert(optReceiver.isDefined,
- "There was a This(), there should be a receiver")
- cont(optReceiver.get)
-
- case Select(This(), field, mutable) if formals.isEmpty =>
- assert(optReceiver.isDefined,
- "There was a This(), there should be a receiver")
- pretransformSelectCommon(body.tpe, optReceiver.get, field, mutable,
- isLhsOfAssign = false)(cont)
-
- case Assign(lhs @ Select(This(), field, mutable), VarRef(Ident(rhsName, _), _))
- if formals.size == 1 && formals.head.name.name == rhsName =>
- assert(isStat, "Found Assign in expression position")
- assert(optReceiver.isDefined,
- "There was a This(), there should be a receiver")
- pretransformSelectCommon(lhs.tpe, optReceiver.get, field, mutable,
- isLhsOfAssign = true) { preTransLhs =>
- // TODO Support assignment of record
- cont(PreTransTree(
- Assign(finishTransformExpr(preTransLhs),
- finishTransformExpr(args.head)),
- RefinedType.NoRefinedType))
- }
-
- case _ =>
- val targetID = (allocationSite, target)
- inlineBody(optReceiver, formals, resultType, body, args, isStat,
- usePreTransform)(cont)(scope.inlining(targetID), pos)
- }
- }
-
- private def inlineBody(optReceiver: Option[PreTransform],
- formals: List[ParamDef], resultType: Type, body: Tree,
- args: List[PreTransform], isStat: Boolean,
- usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = tailcall {
-
- val optReceiverBinding = optReceiver map { receiver =>
- Binding("this", None, receiver.tpe.base, false, receiver)
- }
-
- val argsBindings = for {
- (ParamDef(Ident(name, originalName), tpe, mutable), arg) <- formals zip args
- } yield {
- Binding(name, originalName, tpe, mutable, arg)
- }
-
- withBindings(optReceiverBinding ++: argsBindings) { (bodyScope, cont1) =>
- returnable("", resultType, body, isStat, usePreTransform)(
- cont1)(bodyScope, pos)
- } (cont) (scope.withEnv(OptEnv.Empty))
- }
-
- private def callIntrinsic(code: Int, optTReceiver: Option[PreTransform],
- targs: List[PreTransform], isStat: Boolean, usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit pos: Position): TailRec[Tree] = {
-
- import Intrinsics._
-
- implicit def string2ident(s: String): Ident = Ident(s, None)
-
- lazy val newArgs = targs.map(finishTransformExpr)
-
- @inline def contTree(result: Tree) = cont(PreTransTree(result))
-
- @inline def StringClassType = ClassType(Definitions.StringClass)
-
- def asRTLong(arg: Tree): Tree =
- AsInstanceOf(arg, ClassType(LongImpl.RuntimeLongClass))
- def firstArgAsRTLong: Tree =
- asRTLong(newArgs.head)
-
- (code: @switch) match {
- // java.lang.System
-
- case ArrayCopy =>
- assert(isStat, "System.arraycopy must be used in statement position")
- contTree(CallHelper("systemArraycopy", newArgs)(NoType))
- case IdentityHashCode =>
- contTree(CallHelper("systemIdentityHashCode", newArgs)(IntType))
-
- // scala.scalajs.runtime package object
-
- case PropertiesOf =>
- contTree(CallHelper("propertiesOf", newArgs)(AnyType))
-
- // java.lang.Long
-
- case LongToString =>
- contTree(Apply(firstArgAsRTLong, "toString__T", Nil)(StringClassType))
- case LongCompare =>
- contTree(Apply(firstArgAsRTLong, "compareTo__sjsr_RuntimeLong__I",
- List(asRTLong(newArgs(1))))(IntType))
- case LongBitCount =>
- contTree(Apply(firstArgAsRTLong, LongImpl.bitCount, Nil)(IntType))
- case LongSignum =>
- contTree(Apply(firstArgAsRTLong, LongImpl.signum, Nil)(LongType))
- case LongLeading0s =>
- contTree(Apply(firstArgAsRTLong, LongImpl.numberOfLeadingZeros, Nil)(IntType))
- case LongTrailing0s =>
- contTree(Apply(firstArgAsRTLong, LongImpl.numberOfTrailingZeros, Nil)(IntType))
- case LongToBinStr =>
- contTree(Apply(firstArgAsRTLong, LongImpl.toBinaryString, Nil)(StringClassType))
- case LongToHexStr =>
- contTree(Apply(firstArgAsRTLong, LongImpl.toHexString, Nil)(StringClassType))
- case LongToOctalStr =>
- contTree(Apply(firstArgAsRTLong, LongImpl.toOctalString, Nil)(StringClassType))
-
- // TypedArray conversions
-
- case ByteArrayToInt8Array =>
- contTree(CallHelper("byteArray2TypedArray", newArgs)(AnyType))
- case ShortArrayToInt16Array =>
- contTree(CallHelper("shortArray2TypedArray", newArgs)(AnyType))
- case CharArrayToUint16Array =>
- contTree(CallHelper("charArray2TypedArray", newArgs)(AnyType))
- case IntArrayToInt32Array =>
- contTree(CallHelper("intArray2TypedArray", newArgs)(AnyType))
- case FloatArrayToFloat32Array =>
- contTree(CallHelper("floatArray2TypedArray", newArgs)(AnyType))
- case DoubleArrayToFloat64Array =>
- contTree(CallHelper("doubleArray2TypedArray", newArgs)(AnyType))
-
- case Int8ArrayToByteArray =>
- contTree(CallHelper("typedArray2ByteArray", newArgs)(AnyType))
- case Int16ArrayToShortArray =>
- contTree(CallHelper("typedArray2ShortArray", newArgs)(AnyType))
- case Uint16ArrayToCharArray =>
- contTree(CallHelper("typedArray2CharArray", newArgs)(AnyType))
- case Int32ArrayToIntArray =>
- contTree(CallHelper("typedArray2IntArray", newArgs)(AnyType))
- case Float32ArrayToFloatArray =>
- contTree(CallHelper("typedArray2FloatArray", newArgs)(AnyType))
- case Float64ArrayToDoubleArray =>
- contTree(CallHelper("typedArray2DoubleArray", newArgs)(AnyType))
- }
- }
-
- private def inlineClassConstructor(allocationSite: AllocationSite,
- cls: ClassType, initialValue: RecordValue,
- ctor: Ident, args: List[PreTransform], cancelFun: CancelFun)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = {
-
- val RecordValue(recordType, initialFieldValues) = initialValue
-
- pretransformExprs(initialFieldValues) { tinitialFieldValues =>
- val initialFieldBindings = for {
- (RecordType.Field(name, originalName, tpe, mutable), value) <-
- recordType.fields zip tinitialFieldValues
- } yield {
- Binding(name, originalName, tpe, mutable, value)
- }
-
- withNewLocalDefs(initialFieldBindings) { (initialFieldLocalDefList, cont1) =>
- val fieldNames = initialValue.tpe.fields.map(_.name)
- val initialFieldLocalDefs =
- Map(fieldNames zip initialFieldLocalDefList: _*)
-
- inlineClassConstructorBody(allocationSite, initialFieldLocalDefs,
- cls, cls, ctor, args, cancelFun) { (finalFieldLocalDefs, cont2) =>
- cont2(PreTransLocalDef(LocalDef(
- RefinedType(cls, isExact = true, isNullable = false,
- allocationSite = Some(allocationSite)),
- mutable = false,
- InlineClassInstanceReplacement(recordType, finalFieldLocalDefs, cancelFun))))
- } (cont1)
- } (cont)
- }
- }
-
- private def inlineClassConstructorBody(
- allocationSite: AllocationSite,
- inputFieldsLocalDefs: Map[String, LocalDef], cls: ClassType,
- ctorClass: ClassType, ctor: Ident, args: List[PreTransform],
- cancelFun: CancelFun)(
- buildInner: (Map[String, LocalDef], PreTransCont) => TailRec[Tree])(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = tailcall {
-
- val target = staticCall(ctorClass.className, ctor.name).getOrElse(cancelFun())
- val targetID = (Some(allocationSite), target)
- if (scope.implsBeingInlined.contains(targetID))
- cancelFun()
-
- val MethodDef(_, formals, _, BlockOrAlone(stats, This())) =
- getMethodBody(target)
-
- val argsBindings = for {
- (ParamDef(Ident(name, originalName), tpe, mutable), arg) <- formals zip args
- } yield {
- Binding(name, originalName, tpe, mutable, arg)
- }
-
- withBindings(argsBindings) { (bodyScope, cont1) =>
- val thisLocalDef = LocalDef(
- RefinedType(cls, isExact = true, isNullable = false), false,
- InlineClassBeingConstructedReplacement(inputFieldsLocalDefs, cancelFun))
- val statsScope = bodyScope.inlining(targetID).withEnv(
- bodyScope.env.withLocalDef("this", thisLocalDef))
- inlineClassConstructorBodyList(allocationSite, thisLocalDef,
- inputFieldsLocalDefs, cls, stats, cancelFun)(
- buildInner)(cont1)(statsScope)
- } (cont) (scope.withEnv(OptEnv.Empty))
- }
-
- private def inlineClassConstructorBodyList(
- allocationSite: AllocationSite,
- thisLocalDef: LocalDef, inputFieldsLocalDefs: Map[String, LocalDef],
- cls: ClassType, stats: List[Tree], cancelFun: CancelFun)(
- buildInner: (Map[String, LocalDef], PreTransCont) => TailRec[Tree])(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- stats match {
- case This() :: rest =>
- inlineClassConstructorBodyList(allocationSite, thisLocalDef,
- inputFieldsLocalDefs, cls, rest, cancelFun)(buildInner)(cont)
-
- case Assign(s @ Select(ths: This,
- Ident(fieldName, fieldOrigName), false), value) :: rest =>
- pretransformExpr(value) { tvalue =>
- withNewLocalDef(Binding(fieldName, fieldOrigName, s.tpe, false,
- tvalue)) { (localDef, cont1) =>
- if (localDef.contains(thisLocalDef)) {
- /* Uh oh, there is a `val x = ...this...`. We can't keep it,
- * because this field will not be updated with `newThisLocalDef`.
- */
- cancelFun()
- }
- val newFieldsLocalDefs =
- inputFieldsLocalDefs.updated(fieldName, localDef)
- val newThisLocalDef = LocalDef(
- RefinedType(cls, isExact = true, isNullable = false), false,
- InlineClassBeingConstructedReplacement(newFieldsLocalDefs, cancelFun))
- val restScope = scope.withEnv(scope.env.withLocalDef(
- "this", newThisLocalDef))
- inlineClassConstructorBodyList(allocationSite,
- newThisLocalDef, newFieldsLocalDefs, cls, rest, cancelFun)(
- buildInner)(cont1)(restScope)
- } (cont)
- }
-
- /* if (cond)
- * throw e
- * else
- * this.outer = value
- *
- * becomes
- *
- * this.outer =
- * if (cond) throw e
- * else value
- *
- * Typical shape of initialization of outer pointer of inner classes.
- */
- case If(cond, th: Throw,
- Assign(Select(This(), _, false), value)) :: rest =>
- // work around a bug of the compiler (these should be @-bindings)
- val stat = stats.head.asInstanceOf[If]
- val ass = stat.elsep.asInstanceOf[Assign]
- val lhs = ass.lhs
- inlineClassConstructorBodyList(allocationSite, thisLocalDef,
- inputFieldsLocalDefs, cls,
- Assign(lhs, If(cond, th, value)(lhs.tpe)(stat.pos))(ass.pos) :: rest,
- cancelFun)(buildInner)(cont)
-
- case StaticApply(ths: This, superClass, superCtor, args) :: rest
- if isConstructorName(superCtor.name) =>
- pretransformExprs(args) { targs =>
- inlineClassConstructorBody(allocationSite, inputFieldsLocalDefs,
- cls, superClass, superCtor, targs,
- cancelFun) { (outputFieldsLocalDefs, cont1) =>
- val newThisLocalDef = LocalDef(
- RefinedType(cls, isExact = true, isNullable = false), false,
- InlineClassBeingConstructedReplacement(outputFieldsLocalDefs, cancelFun))
- val restScope = scope.withEnv(scope.env.withLocalDef(
- "this", newThisLocalDef))
- inlineClassConstructorBodyList(allocationSite,
- newThisLocalDef, outputFieldsLocalDefs,
- cls, rest, cancelFun)(buildInner)(cont1)(restScope)
- } (cont)
- }
-
- case VarDef(Ident(name, originalName), tpe, mutable, rhs) :: rest =>
- pretransformExpr(rhs) { trhs =>
- withBinding(Binding(name, originalName, tpe, mutable, trhs)) { (restScope, cont1) =>
- inlineClassConstructorBodyList(allocationSite,
- thisLocalDef, inputFieldsLocalDefs,
- cls, rest, cancelFun)(buildInner)(cont1)(restScope)
- } (cont)
- }
-
- case stat :: rest =>
- val transformedStat = transformStat(stat)
- transformedStat match {
- case Skip() =>
- inlineClassConstructorBodyList(allocationSite,
- thisLocalDef, inputFieldsLocalDefs,
- cls, rest, cancelFun)(buildInner)(cont)
- case _ =>
- if (transformedStat.tpe == NothingType)
- cont(PreTransTree(transformedStat, RefinedType.Nothing))
- else {
- inlineClassConstructorBodyList(allocationSite,
- thisLocalDef, inputFieldsLocalDefs,
- cls, rest, cancelFun) { (outputFieldsLocalDefs, cont1) =>
- buildInner(outputFieldsLocalDefs, { tinner =>
- cont1(PreTransBlock(transformedStat :: Nil, tinner))
- })
- }(cont)
- }
- }
-
- case Nil =>
- buildInner(inputFieldsLocalDefs, cont)
- }
- }
-
- private def foldIf(cond: Tree, thenp: Tree, elsep: Tree)(tpe: Type)(
- implicit pos: Position): Tree = {
- import BinaryOp._
-
- @inline def default = If(cond, thenp, elsep)(tpe)
- cond match {
- case BooleanLiteral(v) =>
- if (v) thenp
- else elsep
-
- case _ =>
- @inline def negCond = foldUnaryOp(UnaryOp.Boolean_!, cond)
- if (thenp.tpe == BooleanType && elsep.tpe == BooleanType) {
- (cond, thenp, elsep) match {
- case (_, BooleanLiteral(t), BooleanLiteral(e)) =>
- if (t == e) Block(keepOnlySideEffects(cond), thenp)
- else if (t) cond
- else negCond
-
- case (_, BooleanLiteral(false), _) =>
- foldIf(negCond, elsep, BooleanLiteral(false))(tpe) // canonical && form
- case (_, _, BooleanLiteral(true)) =>
- foldIf(negCond, BooleanLiteral(true), thenp)(tpe) // canonical || form
-
- /* if (lhs === null) rhs === null else lhs === rhs
- * -> lhs === rhs
- * This is the typical shape of a lhs == rhs test where
- * the equals() method has been inlined as a reference
- * equality test.
- */
- case (BinaryOp(BinaryOp.===, VarRef(lhsIdent, _), Null()),
- BinaryOp(BinaryOp.===, VarRef(rhsIdent, _), Null()),
- BinaryOp(BinaryOp.===, VarRef(lhsIdent2, _), VarRef(rhsIdent2, _)))
- if lhsIdent2 == lhsIdent && rhsIdent2 == rhsIdent =>
- elsep
-
- // Example: (x > y) || (x == y) -> (x >= y)
- case (BinaryOp(op1 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l1, r1),
- BooleanLiteral(true),
- BinaryOp(op2 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l2, r2))
- if ((l1.isInstanceOf[Literal] || l1.isInstanceOf[VarRef]) &&
- (r1.isInstanceOf[Literal] || r1.isInstanceOf[VarRef]) &&
- (l1 == l2 && r1 == r2)) =>
- val canBeEqual =
- ((op1 == Num_==) || (op1 == Num_<=) || (op1 == Num_>=)) ||
- ((op2 == Num_==) || (op2 == Num_<=) || (op2 == Num_>=))
- val canBeLessThan =
- ((op1 == Num_!=) || (op1 == Num_<) || (op1 == Num_<=)) ||
- ((op2 == Num_!=) || (op2 == Num_<) || (op2 == Num_<=))
- val canBeGreaterThan =
- ((op1 == Num_!=) || (op1 == Num_>) || (op1 == Num_>=)) ||
- ((op2 == Num_!=) || (op2 == Num_>) || (op2 == Num_>=))
-
- fold3WayComparison(canBeEqual, canBeLessThan, canBeGreaterThan, l1, r1)
-
- // Example: (x >= y) && (x <= y) -> (x == y)
- case (BinaryOp(op1 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l1, r1),
- BinaryOp(op2 @ (Num_== | Num_!= | Num_< | Num_<= | Num_> | Num_>=), l2, r2),
- BooleanLiteral(false))
- if ((l1.isInstanceOf[Literal] || l1.isInstanceOf[VarRef]) &&
- (r1.isInstanceOf[Literal] || r1.isInstanceOf[VarRef]) &&
- (l1 == l2 && r1 == r2)) =>
- val canBeEqual =
- ((op1 == Num_==) || (op1 == Num_<=) || (op1 == Num_>=)) &&
- ((op2 == Num_==) || (op2 == Num_<=) || (op2 == Num_>=))
- val canBeLessThan =
- ((op1 == Num_!=) || (op1 == Num_<) || (op1 == Num_<=)) &&
- ((op2 == Num_!=) || (op2 == Num_<) || (op2 == Num_<=))
- val canBeGreaterThan =
- ((op1 == Num_!=) || (op1 == Num_>) || (op1 == Num_>=)) &&
- ((op2 == Num_!=) || (op2 == Num_>) || (op2 == Num_>=))
-
- fold3WayComparison(canBeEqual, canBeLessThan, canBeGreaterThan, l1, r1)
-
- case _ => default
- }
- } else {
- (thenp, elsep) match {
- case (Skip(), Skip()) => keepOnlySideEffects(cond)
- case (Skip(), _) => foldIf(negCond, elsep, thenp)(tpe)
-
- case _ => default
- }
- }
- }
- }
-
- private def transformUnaryOp(tree: UnaryOp)(implicit scope: Scope): Tree = {
- import UnaryOp._
-
- implicit val pos = tree.pos
- val UnaryOp(op, arg) = tree
-
- (op: @switch) match {
- case LongToInt =>
- trampoline {
- pretransformExpr(arg) { (targ) =>
- TailCalls.done {
- foldUnaryOp(op, finishTransformOptLongExpr(targ))
- }
- }
- }
-
- case _ =>
- foldUnaryOp(op, transformExpr(arg))
- }
- }
-
- private def transformBinaryOp(tree: BinaryOp)(implicit scope: Scope): Tree = {
- import BinaryOp._
-
- implicit val pos = tree.pos
- val BinaryOp(op, lhs, rhs) = tree
-
- (op: @switch) match {
- case === | !== =>
- trampoline {
- pretransformExprs(lhs, rhs) { (tlhs, trhs) =>
- TailCalls.done(foldReferenceEquality(tlhs, trhs, op == ===))
- }
- }
-
- case Long_== | Long_!= | Long_< | Long_<= | Long_> | Long_>= =>
- trampoline {
- pretransformExprs(lhs, rhs) { (tlhs, trhs) =>
- TailCalls.done {
- if (isLiteralOrOptimizableLong(tlhs) &&
- isLiteralOrOptimizableLong(trhs)) {
- foldBinaryOp(op, finishTransformOptLongExpr(tlhs),
- finishTransformOptLongExpr(trhs))
- } else {
- foldBinaryOp(op, finishTransformExpr(tlhs),
- finishTransformExpr(trhs))
- }
- }
- }
- }
-
- case _ =>
- foldBinaryOp(op, transformExpr(lhs), transformExpr(rhs))
- }
- }
-
- private def isLiteralOrOptimizableLong(texpr: PreTransform): Boolean = {
- texpr match {
- case PreTransTree(LongLiteral(_), _) =>
- true
- case PreTransLocalDef(LocalDef(_, _, replacement)) =>
- replacement match {
- case ReplaceWithVarRef(_, _, _, Some(_)) => true
- case ReplaceWithConstant(LongLiteral(_)) => true
- case _ => false
- }
- case _ =>
- false
- }
- }
-
- private def finishTransformOptLongExpr(targ: PreTransform): Tree = targ match {
- case PreTransLocalDef(LocalDef(tpe, false,
- ReplaceWithVarRef(_, _, _, Some(argValue)))) =>
- argValue()
- case _ =>
- finishTransformExpr(targ)
- }
-
- private def foldUnaryOp(op: UnaryOp.Code, arg: Tree)(
- implicit pos: Position): Tree = {
- import UnaryOp._
- @inline def default = UnaryOp(op, arg)
- (op: @switch) match {
- case Boolean_! =>
- arg match {
- case BooleanLiteral(v) => BooleanLiteral(!v)
- case UnaryOp(Boolean_!, x) => x
-
- case BinaryOp(innerOp, l, r) =>
- val newOp = (innerOp: @switch) match {
- case BinaryOp.=== => BinaryOp.!==
- case BinaryOp.!== => BinaryOp.===
-
- case BinaryOp.Num_== => BinaryOp.Num_!=
- case BinaryOp.Num_!= => BinaryOp.Num_==
- case BinaryOp.Num_< => BinaryOp.Num_>=
- case BinaryOp.Num_<= => BinaryOp.Num_>
- case BinaryOp.Num_> => BinaryOp.Num_<=
- case BinaryOp.Num_>= => BinaryOp.Num_<
-
- case BinaryOp.Long_== => BinaryOp.Long_!=
- case BinaryOp.Long_!= => BinaryOp.Long_==
- case BinaryOp.Long_< => BinaryOp.Long_>=
- case BinaryOp.Long_<= => BinaryOp.Long_>
- case BinaryOp.Long_> => BinaryOp.Long_<=
- case BinaryOp.Long_>= => BinaryOp.Long_<
-
- case BinaryOp.Boolean_== => BinaryOp.Boolean_!=
- case BinaryOp.Boolean_!= => BinaryOp.Boolean_==
-
- case _ => -1
- }
- if (newOp == -1) default
- else BinaryOp(newOp, l, r)
-
- case _ => default
- }
-
- case IntToLong =>
- arg match {
- case IntLiteral(v) => LongLiteral(v.toLong)
- case _ => default
- }
-
- case LongToInt =>
- arg match {
- case LongLiteral(v) => IntLiteral(v.toInt)
- case UnaryOp(IntToLong, x) => x
-
- case BinaryOp(BinaryOp.Long_+, x, y) =>
- foldBinaryOp(BinaryOp.Int_+,
- foldUnaryOp(LongToInt, x),
- foldUnaryOp(LongToInt, y))
- case BinaryOp(BinaryOp.Long_-, x, y) =>
- foldBinaryOp(BinaryOp.Int_-,
- foldUnaryOp(LongToInt, x),
- foldUnaryOp(LongToInt, y))
-
- case _ => default
- }
-
- case LongToDouble =>
- arg match {
- case LongLiteral(v) => DoubleLiteral(v.toDouble)
- case _ => default
- }
- case DoubleToInt =>
- arg match {
- case _ if arg.tpe == IntType => arg
- case NumberLiteral(v) => IntLiteral(v.toInt)
- case _ => default
- }
- case DoubleToFloat =>
- arg match {
- case _ if arg.tpe == FloatType => arg
- case NumberLiteral(v) => FloatLiteral(v.toFloat)
- case _ => default
- }
- case DoubleToLong =>
- arg match {
- case _ if arg.tpe == IntType => foldUnaryOp(IntToLong, arg)
- case NumberLiteral(v) => LongLiteral(v.toLong)
- case _ => default
- }
- case _ =>
- default
- }
- }
-
- /** Performs === for two literals.
- * The result is always known statically.
- */
- private def literal_===(lhs: Literal, rhs: Literal): Boolean = {
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => l == r
- case (FloatLiteral(l), FloatLiteral(r)) => l == r
- case (NumberLiteral(l), NumberLiteral(r)) => l == r
- case (LongLiteral(l), LongLiteral(r)) => l == r
- case (BooleanLiteral(l), BooleanLiteral(r)) => l == r
- case (StringLiteral(l), StringLiteral(r)) => l == r
- case (Undefined(), Undefined()) => true
- case (Null(), Null()) => true
- case _ => false
- }
- }
-
- private def foldBinaryOp(op: BinaryOp.Code, lhs: Tree, rhs: Tree)(
- implicit pos: Position): Tree = {
- import BinaryOp._
- @inline def default = BinaryOp(op, lhs, rhs)
- (op: @switch) match {
- case === | !== =>
- val positive = (op == ===)
- (lhs, rhs) match {
- case (lhs: Literal, rhs: Literal) =>
- BooleanLiteral(literal_===(lhs, rhs) == positive)
-
- case (_: Literal, _) => foldBinaryOp(op, rhs, lhs)
- case _ => default
- }
-
- case Int_+ =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l + r)
- case (_, IntLiteral(_)) => foldBinaryOp(Int_+, rhs, lhs)
- case (IntLiteral(0), _) => rhs
-
- case (IntLiteral(x),
- BinaryOp(innerOp @ (Int_+ | Int_-), IntLiteral(y), z)) =>
- foldBinaryOp(innerOp, IntLiteral(x+y), z)
-
- case _ => default
- }
-
- case Int_- =>
- (lhs, rhs) match {
- case (_, IntLiteral(r)) => foldBinaryOp(Int_+, lhs, IntLiteral(-r))
-
- case (IntLiteral(x), BinaryOp(Int_+, IntLiteral(y), z)) =>
- foldBinaryOp(Int_-, IntLiteral(x-y), z)
- case (IntLiteral(x), BinaryOp(Int_-, IntLiteral(y), z)) =>
- foldBinaryOp(Int_+, IntLiteral(x-y), z)
-
- case (_, BinaryOp(Int_-, IntLiteral(0), x)) =>
- foldBinaryOp(Int_+, lhs, x)
-
- case _ => default
- }
-
- case Int_* =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l * r)
- case (_, IntLiteral(_)) => foldBinaryOp(Int_*, rhs, lhs)
-
- case (IntLiteral(1), _) => rhs
- case (IntLiteral(-1), _) => foldBinaryOp(Int_-, IntLiteral(0), lhs)
-
- case _ => default
- }
-
- case Int_/ =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) if r != 0 => IntLiteral(l / r)
-
- case (_, IntLiteral(1)) => lhs
- case (_, IntLiteral(-1)) => foldBinaryOp(Int_-, IntLiteral(0), lhs)
-
- case _ => default
- }
-
- case Int_% =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) if r != 0 => IntLiteral(l % r)
- case (_, IntLiteral(1 | -1)) =>
- Block(keepOnlySideEffects(lhs), IntLiteral(0))
- case _ => default
- }
-
- case Int_| =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l | r)
- case (_, IntLiteral(_)) => foldBinaryOp(Int_|, rhs, lhs)
- case (IntLiteral(0), _) => rhs
-
- case (IntLiteral(x), BinaryOp(Int_|, IntLiteral(y), z)) =>
- foldBinaryOp(Int_|, IntLiteral(x | y), z)
-
- case _ => default
- }
-
- case Int_& =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l & r)
- case (_, IntLiteral(_)) => foldBinaryOp(Int_&, rhs, lhs)
- case (IntLiteral(-1), _) => rhs
-
- case (IntLiteral(x), BinaryOp(Int_&, IntLiteral(y), z)) =>
- foldBinaryOp(Int_&, IntLiteral(x & y), z)
-
- case _ => default
- }
-
- case Int_^ =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l ^ r)
- case (_, IntLiteral(_)) => foldBinaryOp(Int_^, rhs, lhs)
- case (IntLiteral(0), _) => rhs
-
- case (IntLiteral(x), BinaryOp(Int_^, IntLiteral(y), z)) =>
- foldBinaryOp(Int_^, IntLiteral(x ^ y), z)
-
- case _ => default
- }
-
- case Int_<< =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l << r)
- case (_, IntLiteral(x)) if x % 32 == 0 => lhs
- case _ => default
- }
-
- case Int_>>> =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l >>> r)
- case (_, IntLiteral(x)) if x % 32 == 0 => lhs
- case _ => default
- }
-
- case Int_>> =>
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) => IntLiteral(l >> r)
- case (_, IntLiteral(x)) if x % 32 == 0 => lhs
- case _ => default
- }
-
- case Long_+ =>
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l + r)
- case (_, LongLiteral(_)) => foldBinaryOp(Long_+, rhs, lhs)
- case (LongLiteral(0), _) => rhs
-
- case (LongLiteral(x),
- BinaryOp(innerOp @ (Long_+ | Long_-), LongLiteral(y), z)) =>
- foldBinaryOp(innerOp, LongLiteral(x+y), z)
-
- case _ => default
- }
-
- case Long_- =>
- (lhs, rhs) match {
- case (_, LongLiteral(r)) => foldBinaryOp(Long_+, LongLiteral(-r), lhs)
-
- case (LongLiteral(x), BinaryOp(Long_+, LongLiteral(y), z)) =>
- foldBinaryOp(Long_-, LongLiteral(x-y), z)
- case (LongLiteral(x), BinaryOp(Long_-, LongLiteral(y), z)) =>
- foldBinaryOp(Long_+, LongLiteral(x-y), z)
-
- case (_, BinaryOp(BinaryOp.Long_-, LongLiteral(0L), x)) =>
- foldBinaryOp(Long_+, lhs, x)
-
- case _ => default
- }
-
- case Long_* =>
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l * r)
- case (_, LongLiteral(_)) => foldBinaryOp(Long_*, rhs, lhs)
-
- case (LongLiteral(1), _) => rhs
- case (LongLiteral(-1), _) => foldBinaryOp(Long_-, LongLiteral(0), lhs)
-
- case _ => default
- }
-
- case Long_/ =>
- (lhs, rhs) match {
- case (_, LongLiteral(0)) => default
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l / r)
-
- case (_, LongLiteral(1)) => lhs
- case (_, LongLiteral(-1)) => foldBinaryOp(Long_-, LongLiteral(0), lhs)
-
- case (LongFromInt(x), LongFromInt(y: IntLiteral)) if y.value != -1 =>
- LongFromInt(foldBinaryOp(Int_/, x, y))
-
- case _ => default
- }
-
- case Long_% =>
- (lhs, rhs) match {
- case (_, LongLiteral(0)) => default
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l % r)
-
- case (_, LongLiteral(1L | -1L)) =>
- Block(keepOnlySideEffects(lhs), LongLiteral(0L))
-
- case (LongFromInt(x), LongFromInt(y)) =>
- LongFromInt(foldBinaryOp(Int_%, x, y))
-
- case _ => default
- }
-
- case Long_| =>
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l | r)
- case (_, LongLiteral(_)) => foldBinaryOp(Long_|, rhs, lhs)
- case (LongLiteral(0), _) => rhs
-
- case (LongLiteral(x), BinaryOp(Long_|, LongLiteral(y), z)) =>
- foldBinaryOp(Long_|, LongLiteral(x | y), z)
-
- case _ => default
- }
-
- case Long_& =>
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l & r)
- case (_, LongLiteral(_)) => foldBinaryOp(Long_&, rhs, lhs)
- case (LongLiteral(-1), _) => rhs
-
- case (LongLiteral(x), BinaryOp(Long_&, LongLiteral(y), z)) =>
- foldBinaryOp(Long_&, LongLiteral(x & y), z)
-
- case _ => default
- }
-
- case Long_^ =>
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) => LongLiteral(l ^ r)
- case (_, LongLiteral(_)) => foldBinaryOp(Long_^, rhs, lhs)
- case (LongLiteral(0), _) => rhs
-
- case (LongLiteral(x), BinaryOp(Long_^, LongLiteral(y), z)) =>
- foldBinaryOp(Long_^, LongLiteral(x ^ y), z)
-
- case _ => default
- }
-
- case Long_<< =>
- (lhs, rhs) match {
- case (LongLiteral(l), IntLiteral(r)) => LongLiteral(l << r)
- case (_, IntLiteral(x)) if x % 64 == 0 => lhs
- case _ => default
- }
-
- case Long_>>> =>
- (lhs, rhs) match {
- case (LongLiteral(l), IntLiteral(r)) => LongLiteral(l >>> r)
- case (_, IntLiteral(x)) if x % 64 == 0 => lhs
- case _ => default
- }
-
- case Long_>> =>
- (lhs, rhs) match {
- case (LongLiteral(l), IntLiteral(r)) => LongLiteral(l >> r)
- case (_, IntLiteral(x)) if x % 64 == 0 => lhs
- case _ => default
- }
-
- case Long_== | Long_!= =>
- val positive = (op == Long_==)
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) =>
- BooleanLiteral((l == r) == positive)
-
- case (LongFromInt(x), LongFromInt(y)) =>
- foldBinaryOp(if (positive) === else !==, x, y)
- case (LongFromInt(x), LongLiteral(y)) =>
- assert(y > Int.MaxValue || y < Int.MinValue)
- Block(keepOnlySideEffects(x), BooleanLiteral(!positive))
-
- case (BinaryOp(Long_+, LongLiteral(x), y), LongLiteral(z)) =>
- foldBinaryOp(op, y, LongLiteral(z-x))
- case (BinaryOp(Long_-, LongLiteral(x), y), LongLiteral(z)) =>
- foldBinaryOp(op, y, LongLiteral(x-z))
-
- case (LongLiteral(_), _) => foldBinaryOp(op, rhs, lhs)
- case _ => default
- }
-
- case Long_< | Long_<= | Long_> | Long_>= =>
- def flippedOp = (op: @switch) match {
- case Long_< => Long_>
- case Long_<= => Long_>=
- case Long_> => Long_<
- case Long_>= => Long_<=
- }
-
- def intOp = (op: @switch) match {
- case Long_< => Num_<
- case Long_<= => Num_<=
- case Long_> => Num_>
- case Long_>= => Num_>=
- }
-
- (lhs, rhs) match {
- case (LongLiteral(l), LongLiteral(r)) =>
- val result = (op: @switch) match {
- case Long_< => l < r
- case Long_<= => l <= r
- case Long_> => l > r
- case Long_>= => l >= r
- }
- BooleanLiteral(result)
-
- case (_, LongLiteral(Long.MinValue)) =>
- if (op == Long_< || op == Long_>=)
- Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Long_>=))
- else
- foldBinaryOp(if (op == Long_<=) Long_== else Long_!=, lhs, rhs)
-
- case (_, LongLiteral(Long.MaxValue)) =>
- if (op == Long_> || op == Long_<=)
- Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Long_<=))
- else
- foldBinaryOp(if (op == Long_>=) Long_== else Long_!=, lhs, rhs)
-
- case (LongFromInt(x), LongFromInt(y)) =>
- foldBinaryOp(intOp, x, y)
- case (LongFromInt(x), LongLiteral(y)) =>
- assert(y > Int.MaxValue || y < Int.MinValue)
- val result =
- if (y > Int.MaxValue) op == Long_< || op == Long_<=
- else op == Long_> || op == Long_>=
- Block(keepOnlySideEffects(x), BooleanLiteral(result))
-
- /* x + y.toLong > z
- * -x on both sides
- * requires x + y.toLong not to overflow, and z - x likewise
- * y.toLong > z - x
- */
- case (BinaryOp(Long_+, LongLiteral(x), y @ LongFromInt(_)), LongLiteral(z))
- if canAddLongs(x, Int.MinValue) &&
- canAddLongs(x, Int.MaxValue) &&
- canSubtractLongs(z, x) =>
- foldBinaryOp(op, y, LongLiteral(z-x))
-
- /* x - y.toLong > z
- * -x on both sides
- * requires x - y.toLong not to overflow, and z - x likewise
- * -(y.toLong) > z - x
- */
- case (BinaryOp(Long_-, LongLiteral(x), y @ LongFromInt(_)), LongLiteral(z))
- if canSubtractLongs(x, Int.MinValue) &&
- canSubtractLongs(x, Int.MaxValue) &&
- canSubtractLongs(z, x) =>
- if (z-x != Long.MinValue) {
- // Since -(y.toLong) does not overflow, we can negate both sides
- foldBinaryOp(flippedOp, y, LongLiteral(-(z-x)))
- } else {
- /* -(y.toLong) > Long.MinValue
- * Depending on the operator, this is either always true or
- * always false.
- */
- val result = (op == Long_>) || (op == Long_>=)
- Block(keepOnlySideEffects(y), BooleanLiteral(result))
- }
-
- /* x.toLong + y.toLong > Int.MaxValue.toLong
- *
- * This is basically testing whether x+y overflows in positive.
- * If x <= 0 or y <= 0, this cannot happen -> false.
- * If x > 0 and y > 0, this can be detected with x+y < 0.
- * Therefore, we rewrite as:
- *
- * x > 0 && y > 0 && x+y < 0.
- *
- * This requires to evaluate x and y once.
- */
- case (BinaryOp(Long_+, LongFromInt(x), LongFromInt(y)),
- LongLiteral(Int.MaxValue)) =>
- trampoline {
- withNewLocalDefs(List(
- Binding("x", None, IntType, false, PreTransTree(x)),
- Binding("y", None, IntType, false, PreTransTree(y)))) {
- (tempsLocalDefs, cont) =>
- val List(tempXDef, tempYDef) = tempsLocalDefs
- val tempX = tempXDef.newReplacement
- val tempY = tempYDef.newReplacement
- cont(PreTransTree(
- AndThen(AndThen(
- BinaryOp(Num_>, tempX, IntLiteral(0)),
- BinaryOp(Num_>, tempY, IntLiteral(0))),
- BinaryOp(Num_<, BinaryOp(Int_+, tempX, tempY), IntLiteral(0)))))
- } (finishTransform(isStat = false))
- }
-
- case (LongLiteral(_), _) => foldBinaryOp(flippedOp, rhs, lhs)
- case _ => default
- }
-
- case Float_+ =>
- (lhs, rhs) match {
- case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l + r)
- case (FloatLiteral(0), _) => rhs
- case (_, FloatLiteral(_)) => foldBinaryOp(Float_+, rhs, lhs)
-
- case (FloatLiteral(x),
- BinaryOp(innerOp @ (Float_+ | Float_-), FloatLiteral(y), z)) =>
- foldBinaryOp(innerOp, FloatLiteral(x+y), z)
-
- case _ => default
- }
-
- case Float_- =>
- (lhs, rhs) match {
- case (_, FloatLiteral(r)) => foldBinaryOp(Float_+, lhs, FloatLiteral(-r))
-
- case (FloatLiteral(x), BinaryOp(Float_+, FloatLiteral(y), z)) =>
- foldBinaryOp(Float_-, FloatLiteral(x-y), z)
- case (FloatLiteral(x), BinaryOp(Float_-, FloatLiteral(y), z)) =>
- foldBinaryOp(Float_+, FloatLiteral(x-y), z)
-
- case (_, BinaryOp(BinaryOp.Float_-, FloatLiteral(0), x)) =>
- foldBinaryOp(Float_+, lhs, x)
-
- case _ => default
- }
-
- case Float_* =>
- (lhs, rhs) match {
- case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l * r)
- case (_, FloatLiteral(_)) => foldBinaryOp(Float_*, rhs, lhs)
-
- case (FloatLiteral(1), _) => rhs
- case (FloatLiteral(-1), _) => foldBinaryOp(Float_-, FloatLiteral(0), lhs)
-
- case _ => default
- }
-
- case Float_/ =>
- (lhs, rhs) match {
- case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l / r)
-
- case (_, FloatLiteral(1)) => lhs
- case (_, FloatLiteral(-1)) => foldBinaryOp(Float_-, FloatLiteral(0), lhs)
-
- case _ => default
- }
-
- case Float_% =>
- (lhs, rhs) match {
- case (FloatLiteral(l), FloatLiteral(r)) => FloatLiteral(l % r)
- case _ => default
- }
-
- case Double_+ =>
- (lhs, rhs) match {
- case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l + r)
- case (NumberLiteral(0), _) => rhs
- case (_, NumberLiteral(_)) => foldBinaryOp(Double_+, rhs, lhs)
-
- case (NumberLiteral(x),
- BinaryOp(innerOp @ (Double_+ | Double_-), NumberLiteral(y), z)) =>
- foldBinaryOp(innerOp, DoubleLiteral(x+y), z)
-
- case _ => default
- }
-
- case Double_- =>
- (lhs, rhs) match {
- case (_, NumberLiteral(r)) => foldBinaryOp(Double_+, lhs, DoubleLiteral(-r))
-
- case (NumberLiteral(x), BinaryOp(Double_+, NumberLiteral(y), z)) =>
- foldBinaryOp(Double_-, DoubleLiteral(x-y), z)
- case (NumberLiteral(x), BinaryOp(Double_-, NumberLiteral(y), z)) =>
- foldBinaryOp(Double_+, DoubleLiteral(x-y), z)
-
- case (_, BinaryOp(BinaryOp.Double_-, NumberLiteral(0), x)) =>
- foldBinaryOp(Double_+, lhs, x)
-
- case _ => default
- }
-
- case Double_* =>
- (lhs, rhs) match {
- case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l * r)
- case (_, NumberLiteral(_)) => foldBinaryOp(Double_*, rhs, lhs)
-
- case (NumberLiteral(1), _) => rhs
- case (NumberLiteral(-1), _) => foldBinaryOp(Double_-, DoubleLiteral(0), lhs)
-
- case _ => default
- }
-
- case Double_/ =>
- (lhs, rhs) match {
- case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l / r)
-
- case (_, NumberLiteral(1)) => lhs
- case (_, NumberLiteral(-1)) => foldBinaryOp(Double_-, DoubleLiteral(0), lhs)
-
- case _ => default
- }
-
- case Double_% =>
- (lhs, rhs) match {
- case (NumberLiteral(l), NumberLiteral(r)) => DoubleLiteral(l % r)
- case _ => default
- }
-
- case Boolean_== | Boolean_!= =>
- val positive = (op == Boolean_==)
- (lhs, rhs) match {
- case (BooleanLiteral(l), _) =>
- if (l == positive) rhs
- else foldUnaryOp(UnaryOp.Boolean_!, rhs)
- case (_, BooleanLiteral(r)) =>
- if (r == positive) lhs
- else foldUnaryOp(UnaryOp.Boolean_!, lhs)
- case _ =>
- default
- }
-
- case Boolean_| =>
- (lhs, rhs) match {
- case (_, BooleanLiteral(false)) => lhs
- case (BooleanLiteral(false), _) => rhs
- case _ => default
- }
-
- case Boolean_& =>
- (lhs, rhs) match {
- case (_, BooleanLiteral(true)) => lhs
- case (BooleanLiteral(true), _) => rhs
- case _ => default
- }
-
- case Num_== | Num_!= =>
- val positive = (op == Num_==)
- (lhs, rhs) match {
- case (lhs: Literal, rhs: Literal) =>
- BooleanLiteral(literal_===(lhs, rhs) == positive)
-
- case (BinaryOp(Int_+, IntLiteral(x), y), IntLiteral(z)) =>
- foldBinaryOp(op, y, IntLiteral(z-x))
- case (BinaryOp(Int_-, IntLiteral(x), y), IntLiteral(z)) =>
- foldBinaryOp(op, y, IntLiteral(x-z))
-
- case (_: Literal, _) => foldBinaryOp(op, rhs, lhs)
- case _ => default
- }
-
- case Num_< | Num_<= | Num_> | Num_>= =>
- def flippedOp = (op: @switch) match {
- case Num_< => Num_>
- case Num_<= => Num_>=
- case Num_> => Num_<
- case Num_>= => Num_<=
- }
-
- if (lhs.tpe == IntType && rhs.tpe == IntType) {
- (lhs, rhs) match {
- case (IntLiteral(l), IntLiteral(r)) =>
- val result = (op: @switch) match {
- case Num_< => l < r
- case Num_<= => l <= r
- case Num_> => l > r
- case Num_>= => l >= r
- }
- BooleanLiteral(result)
-
- case (_, IntLiteral(Int.MinValue)) =>
- if (op == Num_< || op == Num_>=)
- Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Num_>=))
- else
- foldBinaryOp(if (op == Num_<=) Num_== else Num_!=, lhs, rhs)
-
- case (_, IntLiteral(Int.MaxValue)) =>
- if (op == Num_> || op == Num_<=)
- Block(keepOnlySideEffects(lhs), BooleanLiteral(op == Num_<=))
- else
- foldBinaryOp(if (op == Num_>=) Num_== else Num_!=, lhs, rhs)
-
- case (IntLiteral(_), _) => foldBinaryOp(flippedOp, rhs, lhs)
- case _ => default
- }
- } else {
- (lhs, rhs) match {
- case (NumberLiteral(l), NumberLiteral(r)) =>
- val result = (op: @switch) match {
- case Num_< => l < r
- case Num_<= => l <= r
- case Num_> => l > r
- case Num_>= => l >= r
- }
- BooleanLiteral(result)
-
- case _ => default
- }
- }
-
- case _ =>
- default
- }
- }
-
- private def fold3WayComparison(canBeEqual: Boolean, canBeLessThan: Boolean,
- canBeGreaterThan: Boolean, lhs: Tree, rhs: Tree)(
- implicit pos: Position): Tree = {
- import BinaryOp._
- if (canBeEqual) {
- if (canBeLessThan) {
- if (canBeGreaterThan)
- Block(keepOnlySideEffects(lhs), keepOnlySideEffects(rhs), BooleanLiteral(true))
- else
- foldBinaryOp(Num_<=, lhs, rhs)
- } else {
- if (canBeGreaterThan)
- foldBinaryOp(Num_>=, lhs, rhs)
- else
- foldBinaryOp(Num_==, lhs, rhs)
- }
- } else {
- if (canBeLessThan) {
- if (canBeGreaterThan)
- foldBinaryOp(Num_!=, lhs, rhs)
- else
- foldBinaryOp(Num_<, lhs, rhs)
- } else {
- if (canBeGreaterThan)
- foldBinaryOp(Num_>, lhs, rhs)
- else
- Block(keepOnlySideEffects(lhs), keepOnlySideEffects(rhs), BooleanLiteral(false))
- }
- }
- }
-
- private def foldUnbox(arg: PreTransform, charCode: Char)(
- cont: PreTransCont): TailRec[Tree] = {
- (charCode: @switch) match {
- case 'Z' if arg.tpe.base == BooleanType => cont(arg)
- case 'I' if arg.tpe.base == IntType => cont(arg)
- case 'F' if arg.tpe.base == FloatType => cont(arg)
- case 'J' if arg.tpe.base == LongType => cont(arg)
- case 'D' if arg.tpe.base == DoubleType ||
- arg.tpe.base == IntType || arg.tpe.base == FloatType => cont(arg)
- case _ =>
- cont(PreTransTree(Unbox(finishTransformExpr(arg), charCode)(arg.pos)))
- }
- }
-
- private def foldReferenceEquality(tlhs: PreTransform, trhs: PreTransform,
- positive: Boolean = true)(implicit pos: Position): Tree = {
- (tlhs, trhs) match {
- case (_, PreTransTree(Null(), _)) if !tlhs.tpe.isNullable =>
- Block(
- finishTransformStat(tlhs),
- BooleanLiteral(!positive))
- case (PreTransTree(Null(), _), _) if !trhs.tpe.isNullable =>
- Block(
- finishTransformStat(trhs),
- BooleanLiteral(!positive))
- case _ =>
- foldBinaryOp(if (positive) BinaryOp.=== else BinaryOp.!==,
- finishTransformExpr(tlhs), finishTransformExpr(trhs))
- }
- }
-
- private def finishTransformCheckNull(preTrans: PreTransform)(
- implicit pos: Position): Tree = {
- if (preTrans.tpe.isNullable) {
- val transformed = finishTransformExpr(preTrans)
- CallHelper("checkNonNull", transformed)(transformed.tpe)
- } else {
- finishTransformExpr(preTrans)
- }
- }
-
- def transformIsolatedBody(optTarget: Option[MethodID],
- thisType: Type, params: List[ParamDef], resultType: Type,
- body: Tree): (List[ParamDef], Tree) = {
- val (paramLocalDefs, newParamDefs) = (for {
- p @ ParamDef(ident @ Ident(name, originalName), ptpe, mutable) <- params
- } yield {
- val newName = freshLocalName(name)
- val newOriginalName = originalName.orElse(Some(newName))
- val localDef = LocalDef(RefinedType(ptpe), mutable,
- ReplaceWithVarRef(newName, newOriginalName, new SimpleState(true), None))
- val newParamDef = ParamDef(
- Ident(newName, newOriginalName)(ident.pos), ptpe, mutable)(p.pos)
- ((name -> localDef), newParamDef)
- }).unzip
-
- val thisLocalDef =
- if (thisType == NoType) None
- else {
- Some("this" -> LocalDef(
- RefinedType(thisType, isExact = false, isNullable = false),
- false, ReplaceWithThis()))
- }
-
- val allLocalDefs = thisLocalDef ++: paramLocalDefs
-
- val scope0 = optTarget.fold(Scope.Empty)(
- target => Scope.Empty.inlining((None, target)))
- val scope = scope0.withEnv(OptEnv.Empty.withLocalDefs(allLocalDefs))
- val newBody =
- transform(body, resultType == NoType)(scope)
-
- (newParamDefs, newBody)
- }
-
- private def returnable(oldLabelName: String, resultType: Type,
- body: Tree, isStat: Boolean, usePreTransform: Boolean)(
- cont: PreTransCont)(
- implicit scope: Scope, pos: Position): TailRec[Tree] = tailcall {
- val newLabel = freshLabelName(
- if (oldLabelName.isEmpty) "inlinereturn" else oldLabelName)
-
- def doMakeTree(newBody: Tree, returnedTypes: List[Type]): Tree = {
- val refinedType =
- returnedTypes.reduce(constrainedLub(_, _, resultType))
- val returnCount = returnedTypes.size - 1
-
- tryOptimizePatternMatch(oldLabelName, refinedType,
- returnCount, newBody) getOrElse {
- Labeled(Ident(newLabel, None), refinedType, newBody)
- }
- }
-
- val info = new LabelInfo(newLabel, acceptRecords = usePreTransform)
- withState(info.returnedTypes) {
- val bodyScope = scope.withEnv(scope.env.withLabelInfo(oldLabelName, info))
-
- if (usePreTransform) {
- assert(!isStat, "Cannot use pretransform in statement position")
- tryOrRollback { cancelFun =>
- pretransformExpr(body) { tbody0 =>
- val returnedTypes0 = info.returnedTypes.value
- if (returnedTypes0.isEmpty) {
- // no return to that label, we can eliminate it
- cont(tbody0)
- } else {
- val tbody = resolveLocalDef(tbody0)
- val (newBody, returnedTypes) = tbody match {
- case PreTransRecordTree(bodyTree, origType, _) =>
- (bodyTree, (bodyTree.tpe, origType) :: returnedTypes0)
- case PreTransTree(bodyTree, tpe) =>
- (bodyTree, (bodyTree.tpe, tpe) :: returnedTypes0)
- }
- val (actualTypes, origTypes) = returnedTypes.unzip
- val refinedOrigType =
- origTypes.reduce(constrainedLub(_, _, resultType))
- actualTypes.collectFirst {
- case actualType: RecordType => actualType
- }.fold[TailRec[Tree]] {
- // None of the returned types are records
- cont(PreTransTree(
- doMakeTree(newBody, actualTypes), refinedOrigType))
- } { recordType =>
- if (actualTypes.exists(t => t != recordType && t != NothingType))
- cancelFun()
-
- val resultTree = doMakeTree(newBody, actualTypes)
-
- if (origTypes.exists(t => t != refinedOrigType && !t.isNothingType))
- cancelFun()
-
- cont(PreTransRecordTree(resultTree, refinedOrigType, cancelFun))
- }
- }
- } (bodyScope)
- } { () =>
- returnable(oldLabelName, resultType, body, isStat,
- usePreTransform = false)(cont)
- }
- } else {
- val newBody = transform(body, isStat)(bodyScope)
- val returnedTypes0 = info.returnedTypes.value.map(_._1)
- if (returnedTypes0.isEmpty) {
- // no return to that label, we can eliminate it
- cont(PreTransTree(newBody, RefinedType(newBody.tpe)))
- } else {
- val returnedTypes = newBody.tpe :: returnedTypes0
- val tree = doMakeTree(newBody, returnedTypes)
- cont(PreTransTree(tree, RefinedType(tree.tpe)))
- }
- }
- }
- }
-
- def tryOptimizePatternMatch(oldLabelName: String, refinedType: Type,
- returnCount: Int, newBody: Tree): Option[Tree] = {
- if (!oldLabelName.startsWith("matchEnd")) None
- else {
- newBody match {
- case Block(stats) =>
- @tailrec
- def createRevAlts(xs: List[Tree], acc: List[(Tree, Tree)]): List[(Tree, Tree)] = xs match {
- case If(cond, body, Skip()) :: xr =>
- createRevAlts(xr, (cond, body) :: acc)
- case remaining =>
- (EmptyTree, Block(remaining)(remaining.head.pos)) :: acc
- }
- val revAlts = createRevAlts(stats, Nil)
-
- if (revAlts.size == returnCount) {
- @tailrec
- def constructOptimized(revAlts: List[(Tree, Tree)], elsep: Tree): Option[Tree] = {
- revAlts match {
- case (cond, body) :: revAltsRest =>
- body match {
- case BlockOrAlone(prep,
- Return(result, Some(Ident(newLabel, _)))) =>
- val result1 =
- if (refinedType == NoType) keepOnlySideEffects(result)
- else result
- val prepAndResult = Block(prep :+ result1)(body.pos)
- if (cond == EmptyTree) {
- assert(elsep == EmptyTree)
- constructOptimized(revAltsRest, prepAndResult)
- } else {
- assert(elsep != EmptyTree)
- constructOptimized(revAltsRest,
- foldIf(cond, prepAndResult, elsep)(refinedType)(cond.pos))
- }
- case _ =>
- None
- }
- case Nil =>
- Some(elsep)
- }
- }
- constructOptimized(revAlts, EmptyTree)
- } else None
- case _ =>
- None
- }
- }
- }
-
- private def withBindings(bindings: List[Binding])(
- buildInner: (Scope, PreTransCont) => TailRec[Tree])(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- withNewLocalDefs(bindings) { (localDefs, cont1) =>
- val newMappings = for {
- (binding, localDef) <- bindings zip localDefs
- } yield {
- binding.name -> localDef
- }
- buildInner(scope.withEnv(scope.env.withLocalDefs(newMappings)), cont1)
- } (cont)
- }
-
- private def withBinding(binding: Binding)(
- buildInner: (Scope, PreTransCont) => TailRec[Tree])(
- cont: PreTransCont)(
- implicit scope: Scope): TailRec[Tree] = {
- withNewLocalDef(binding) { (localDef, cont1) =>
- buildInner(scope.withEnv(scope.env.withLocalDef(binding.name, localDef)),
- cont1)
- } (cont)
- }
-
- private def withNewLocalDefs(bindings: List[Binding])(
- buildInner: (List[LocalDef], PreTransCont) => TailRec[Tree])(
- cont: PreTransCont): TailRec[Tree] = {
- bindings match {
- case first :: rest =>
- withNewLocalDef(first) { (firstLocalDef, cont1) =>
- withNewLocalDefs(rest) { (restLocalDefs, cont2) =>
- buildInner(firstLocalDef :: restLocalDefs, cont2)
- } (cont1)
- } (cont)
-
- case Nil =>
- buildInner(Nil, cont)
- }
- }
-
- private def isImmutableType(tpe: Type): Boolean = tpe match {
- case RecordType(fields) =>
- fields.forall(f => !f.mutable && isImmutableType(f.tpe))
- case _ =>
- true
- }
-
- private def withNewLocalDef(binding: Binding)(
- buildInner: (LocalDef, PreTransCont) => TailRec[Tree])(
- cont: PreTransCont): TailRec[Tree] = tailcall {
- val Binding(name, originalName, declaredType, mutable, value) = binding
- implicit val pos = value.pos
-
- def withDedicatedVar(tpe: RefinedType): TailRec[Tree] = {
- val newName = freshLocalName(name)
- val newOriginalName = originalName.orElse(Some(name))
-
- val used = new SimpleState(false)
- withState(used) {
- def doBuildInner(localDef: LocalDef)(varDef: => VarDef)(
- cont: PreTransCont): TailRec[Tree] = {
- buildInner(localDef, { tinner =>
- if (used.value) {
- cont(PreTransBlock(varDef :: Nil, tinner))
- } else {
- tinner match {
- case PreTransLocalDef(`localDef`) =>
- cont(value)
- case _ if tinner.contains(localDef) =>
- cont(PreTransBlock(varDef :: Nil, tinner))
- case _ =>
- val rhsSideEffects = finishTransformStat(value)
- rhsSideEffects match {
- case Skip() =>
- cont(tinner)
- case _ =>
- if (rhsSideEffects.tpe == NothingType)
- cont(PreTransTree(rhsSideEffects, RefinedType.Nothing))
- else
- cont(PreTransBlock(rhsSideEffects :: Nil, tinner))
- }
- }
- }
- })
- }
-
- resolveLocalDef(value) match {
- case PreTransRecordTree(valueTree, valueTpe, cancelFun) =>
- val recordType = valueTree.tpe.asInstanceOf[RecordType]
- if (!isImmutableType(recordType))
- cancelFun()
- val localDef = LocalDef(valueTpe, mutable,
- ReplaceWithRecordVarRef(newName, newOriginalName, recordType,
- used, cancelFun))
- doBuildInner(localDef) {
- VarDef(Ident(newName, newOriginalName), recordType, mutable,
- valueTree)
- } (cont)
-
- case PreTransTree(valueTree, valueTpe) =>
- def doDoBuildInner(optValueTree: Option[() => Tree])(
- cont1: PreTransCont) = {
- val localDef = LocalDef(tpe, mutable, ReplaceWithVarRef(
- newName, newOriginalName, used, optValueTree))
- doBuildInner(localDef) {
- VarDef(Ident(newName, newOriginalName), tpe.base, mutable,
- optValueTree.fold(valueTree)(_()))
- } (cont1)
- }
- if (mutable) {
- doDoBuildInner(None)(cont)
- } else (valueTree match {
- case LongFromInt(arg) =>
- withNewLocalDef(
- Binding("x", None, IntType, false, PreTransTree(arg))) {
- (intLocalDef, cont1) =>
- doDoBuildInner(Some(
- () => LongFromInt(intLocalDef.newReplacement)))(
- cont1)
- } (cont)
-
- case BinaryOp(op @ (BinaryOp.Long_+ | BinaryOp.Long_-),
- LongFromInt(intLhs), LongFromInt(intRhs)) =>
- withNewLocalDefs(List(
- Binding("x", None, IntType, false, PreTransTree(intLhs)),
- Binding("y", None, IntType, false, PreTransTree(intRhs)))) {
- (intLocalDefs, cont1) =>
- val List(lhsLocalDef, rhsLocalDef) = intLocalDefs
- doDoBuildInner(Some(
- () => BinaryOp(op,
- LongFromInt(lhsLocalDef.newReplacement),
- LongFromInt(rhsLocalDef.newReplacement))))(
- cont1)
- } (cont)
-
- case _ =>
- doDoBuildInner(None)(cont)
- })
- }
- }
- }
-
- if (value.tpe.isNothingType) {
- cont(value)
- } else if (mutable) {
- withDedicatedVar(RefinedType(declaredType))
- } else {
- val refinedType = value.tpe
- value match {
- case PreTransBlock(stats, result) =>
- withNewLocalDef(binding.copy(value = result))(buildInner) { tresult =>
- cont(PreTransBlock(stats, tresult))
- }
-
- case PreTransLocalDef(localDef) if !localDef.mutable =>
- buildInner(localDef, cont)
-
- case PreTransTree(literal: Literal, _) =>
- buildInner(LocalDef(refinedType, false,
- ReplaceWithConstant(literal)), cont)
-
- case PreTransTree(VarRef(Ident(refName, refOriginalName), false), _) =>
- buildInner(LocalDef(refinedType, false,
- ReplaceWithVarRef(refName, refOriginalName,
- new SimpleState(true), None)), cont)
-
- case _ =>
- withDedicatedVar(refinedType)
- }
- }
- }
-
- /** Finds a type as precise as possible which is a supertype of lhs and rhs
- * but still a subtype of upperBound.
- * Requires that lhs and rhs be subtypes of upperBound, obviously.
- */
- private def constrainedLub(lhs: RefinedType, rhs: RefinedType,
- upperBound: Type): RefinedType = {
- if (upperBound == NoType) RefinedType(upperBound)
- else if (lhs == rhs) lhs
- else if (lhs.isNothingType) rhs
- else if (rhs.isNothingType) lhs
- else {
- RefinedType(constrainedLub(lhs.base, rhs.base, upperBound),
- false, lhs.isNullable || rhs.isNullable)
- }
- }
-
- /** Finds a type as precise as possible which is a supertype of lhs and rhs
- * but still a subtype of upperBound.
- * Requires that lhs and rhs be subtypes of upperBound, obviously.
- */
- private def constrainedLub(lhs: Type, rhs: Type, upperBound: Type): Type = {
- // TODO Improve this
- if (upperBound == NoType) upperBound
- else if (lhs == rhs) lhs
- else if (lhs == NothingType) rhs
- else if (rhs == NothingType) lhs
- else upperBound
- }
-
- /** Trampolines a pretransform */
- private def trampoline(tailrec: => TailRec[Tree]): Tree = {
- curTrampolineId += 1
-
- val myTrampolineId = curTrampolineId
-
- try {
- var rec = () => tailrec
-
- while (true) {
- try {
- return rec().result
- } catch {
- case e: RollbackException if e.trampolineId == myTrampolineId =>
- rollbacksCount += 1
- if (rollbacksCount > MaxRollbacksPerMethod)
- throw new TooManyRollbacksException
-
- usedLocalNames.clear()
- usedLocalNames ++= e.savedUsedLocalNames
- usedLabelNames.clear()
- usedLabelNames ++= e.savedUsedLabelNames
- for ((state, backup) <- statesInUse zip e.savedStates)
- state.asInstanceOf[State[Any]].restore(backup)
-
- rec = e.cont
- }
- }
-
- sys.error("Reached end of infinite loop")
- } finally {
- curTrampolineId -= 1
- }
- }
-}
-
-private[optimizer] object OptimizerCore {
-
- private final val MaxRollbacksPerMethod = 256
-
- private final class TooManyRollbacksException
- extends scala.util.control.ControlThrowable
-
- private val AnonFunctionClassPrefix = "sjsr_AnonFunction"
-
- private type CancelFun = () => Nothing
- private type PreTransCont = PreTransform => TailRec[Tree]
-
- private case class RefinedType private (base: Type, isExact: Boolean,
- isNullable: Boolean)(
- val allocationSite: Option[AllocationSite], dummy: Int = 0) {
-
- def isNothingType: Boolean = base == NothingType
- }
-
- private object RefinedType {
- def apply(base: Type, isExact: Boolean, isNullable: Boolean,
- allocationSite: Option[AllocationSite]): RefinedType =
- new RefinedType(base, isExact, isNullable)(allocationSite)
-
- def apply(base: Type, isExact: Boolean, isNullable: Boolean): RefinedType =
- RefinedType(base, isExact, isNullable, None)
-
- def apply(tpe: Type): RefinedType = tpe match {
- case BooleanType | IntType | FloatType | DoubleType | StringType |
- UndefType | NothingType | _:RecordType | NoType =>
- RefinedType(tpe, isExact = true, isNullable = false)
- case NullType =>
- RefinedType(tpe, isExact = true, isNullable = true)
- case _ =>
- RefinedType(tpe, isExact = false, isNullable = true)
- }
-
- val NoRefinedType = RefinedType(NoType)
- val Nothing = RefinedType(NothingType)
- }
-
- private class AllocationSite(private val node: Tree) {
- override def equals(that: Any): Boolean = that match {
- case that: AllocationSite => this.node eq that.node
- case _ => false
- }
-
- override def hashCode(): Int =
- System.identityHashCode(node)
-
- override def toString(): String =
- s"AllocationSite($node)"
- }
-
- private case class LocalDef(
- tpe: RefinedType,
- mutable: Boolean,
- replacement: LocalDefReplacement) {
-
- def newReplacement(implicit pos: Position): Tree = replacement match {
- case ReplaceWithVarRef(name, originalName, used, _) =>
- used.value = true
- VarRef(Ident(name, originalName), mutable)(tpe.base)
-
- case ReplaceWithRecordVarRef(_, _, _, _, cancelFun) =>
- cancelFun()
-
- case ReplaceWithThis() =>
- This()(tpe.base)
-
- case ReplaceWithConstant(value) =>
- value
-
- case TentativeClosureReplacement(_, _, _, _, _, cancelFun) =>
- cancelFun()
-
- case InlineClassBeingConstructedReplacement(_, cancelFun) =>
- cancelFun()
-
- case InlineClassInstanceReplacement(_, _, cancelFun) =>
- cancelFun()
- }
-
- def contains(that: LocalDef): Boolean = {
- (this eq that) || (replacement match {
- case TentativeClosureReplacement(_, _, _, captureLocalDefs, _, _) =>
- captureLocalDefs.exists(_.contains(that))
- case InlineClassInstanceReplacement(_, fieldLocalDefs, _) =>
- fieldLocalDefs.valuesIterator.exists(_.contains(that))
- case _ =>
- false
- })
- }
- }
-
- private sealed abstract class LocalDefReplacement
-
- private final case class ReplaceWithVarRef(name: String,
- originalName: Option[String],
- used: SimpleState[Boolean],
- longOpTree: Option[() => Tree]) extends LocalDefReplacement
-
- private final case class ReplaceWithRecordVarRef(name: String,
- originalName: Option[String],
- recordType: RecordType,
- used: SimpleState[Boolean],
- cancelFun: CancelFun) extends LocalDefReplacement
-
- private final case class ReplaceWithThis() extends LocalDefReplacement
-
- private final case class ReplaceWithConstant(
- value: Tree) extends LocalDefReplacement
-
- private final case class TentativeClosureReplacement(
- captureParams: List[ParamDef], params: List[ParamDef], body: Tree,
- captureValues: List[LocalDef],
- alreadyUsed: SimpleState[Boolean],
- cancelFun: CancelFun) extends LocalDefReplacement
-
- private final case class InlineClassBeingConstructedReplacement(
- fieldLocalDefs: Map[String, LocalDef],
- cancelFun: CancelFun) extends LocalDefReplacement
-
- private final case class InlineClassInstanceReplacement(
- recordType: RecordType,
- fieldLocalDefs: Map[String, LocalDef],
- cancelFun: CancelFun) extends LocalDefReplacement
-
- private final class LabelInfo(
- val newName: String,
- val acceptRecords: Boolean,
- /** (actualType, originalType), actualType can be a RecordType. */
- val returnedTypes: SimpleState[List[(Type, RefinedType)]] = new SimpleState(Nil))
-
- private class OptEnv(
- val localDefs: Map[String, LocalDef],
- val labelInfos: Map[String, LabelInfo]) {
-
- def withLocalDef(oldName: String, rep: LocalDef): OptEnv =
- new OptEnv(localDefs + (oldName -> rep), labelInfos)
-
- def withLocalDefs(reps: List[(String, LocalDef)]): OptEnv =
- new OptEnv(localDefs ++ reps, labelInfos)
-
- def withLabelInfo(oldName: String, info: LabelInfo): OptEnv =
- new OptEnv(localDefs, labelInfos + (oldName -> info))
-
- def withinFunction(paramLocalDefs: List[(String, LocalDef)]): OptEnv =
- new OptEnv(localDefs ++ paramLocalDefs, Map.empty)
-
- override def toString(): String = {
- "localDefs:"+localDefs.mkString("\n ", "\n ", "\n") +
- "labelInfos:"+labelInfos.mkString("\n ", "\n ", "")
- }
- }
-
- private object OptEnv {
- val Empty: OptEnv = new OptEnv(Map.empty, Map.empty)
- }
-
- private class Scope(val env: OptEnv,
- val implsBeingInlined: Set[(Option[AllocationSite], AbstractMethodID)]) {
- def withEnv(env: OptEnv): Scope =
- new Scope(env, implsBeingInlined)
-
- def inlining(impl: (Option[AllocationSite], AbstractMethodID)): Scope = {
- assert(!implsBeingInlined(impl), s"Circular inlining of $impl")
- new Scope(env, implsBeingInlined + impl)
- }
- }
-
- private object Scope {
- val Empty: Scope = new Scope(OptEnv.Empty, Set.empty)
- }
-
- /** The result of pretransformExpr().
- * It has a `tpe` as precisely refined as if a full transformExpr() had
- * been performed.
- * It is also not dependent on the environment anymore. In some sense, it
- * has "captured" its environment at definition site.
- */
- private sealed abstract class PreTransform {
- def pos: Position
- val tpe: RefinedType
-
- def contains(localDef: LocalDef): Boolean = this match {
- case PreTransBlock(_, result) =>
- result.contains(localDef)
- case PreTransLocalDef(thisLocalDef) =>
- thisLocalDef.contains(localDef)
- case _ =>
- false
- }
- }
-
- private final class PreTransBlock private (val stats: List[Tree],
- val result: PreTransLocalDef) extends PreTransform {
- def pos = result.pos
- val tpe = result.tpe
-
- assert(stats.nonEmpty)
-
- override def toString(): String =
- s"PreTransBlock($stats,$result)"
- }
-
- private object PreTransBlock {
- def apply(stats: List[Tree], result: PreTransform): PreTransform = {
- if (stats.isEmpty) result
- else {
- result match {
- case PreTransBlock(innerStats, innerResult) =>
- new PreTransBlock(stats ++ innerStats, innerResult)
- case result: PreTransLocalDef =>
- new PreTransBlock(stats, result)
- case PreTransRecordTree(tree, tpe, cancelFun) =>
- PreTransRecordTree(Block(stats :+ tree)(tree.pos), tpe, cancelFun)
- case PreTransTree(tree, tpe) =>
- PreTransTree(Block(stats :+ tree)(tree.pos), tpe)
- }
- }
- }
-
- def unapply(preTrans: PreTransBlock): Some[(List[Tree], PreTransLocalDef)] =
- Some(preTrans.stats, preTrans.result)
- }
-
- private sealed abstract class PreTransNoBlock extends PreTransform
-
- private final case class PreTransLocalDef(localDef: LocalDef)(
- implicit val pos: Position) extends PreTransNoBlock {
- val tpe: RefinedType = localDef.tpe
- }
-
- private sealed abstract class PreTransGenTree extends PreTransNoBlock
-
- private final case class PreTransRecordTree(tree: Tree,
- tpe: RefinedType, cancelFun: CancelFun) extends PreTransGenTree {
- def pos = tree.pos
-
- assert(tree.tpe.isInstanceOf[RecordType],
- s"Cannot create a PreTransRecordTree with non-record type ${tree.tpe}")
- }
-
- private final case class PreTransTree(tree: Tree,
- tpe: RefinedType) extends PreTransGenTree {
- def pos: Position = tree.pos
-
- assert(!tree.tpe.isInstanceOf[RecordType],
- s"Cannot create a Tree with record type ${tree.tpe}")
- }
-
- private object PreTransTree {
- def apply(tree: Tree): PreTransTree =
- PreTransTree(tree, RefinedType(tree.tpe))
- }
-
- private final case class Binding(name: String, originalName: Option[String],
- declaredType: Type, mutable: Boolean, value: PreTransform)
-
- private object NumberLiteral {
- def unapply(tree: Literal): Option[Double] = tree match {
- case DoubleLiteral(v) => Some(v)
- case IntLiteral(v) => Some(v.toDouble)
- case FloatLiteral(v) => Some(v.toDouble)
- case _ => None
- }
- }
-
- private object LongFromInt {
- def apply(x: Tree)(implicit pos: Position): Tree = x match {
- case IntLiteral(v) => LongLiteral(v)
- case _ => UnaryOp(UnaryOp.IntToLong, x)
- }
-
- def unapply(tree: Tree): Option[Tree] = tree match {
- case LongLiteral(v) if v.toInt == v => Some(IntLiteral(v.toInt)(tree.pos))
- case UnaryOp(UnaryOp.IntToLong, x) => Some(x)
- case _ => None
- }
- }
-
- private object AndThen {
- def apply(lhs: Tree, rhs: Tree)(implicit pos: Position): Tree =
- If(lhs, rhs, BooleanLiteral(false))(BooleanType)
- }
-
- /** Tests whether `x + y` is valid without falling out of range. */
- private def canAddLongs(x: Long, y: Long): Boolean =
- if (y >= 0) x+y >= x
- else x+y < x
-
- /** Tests whether `x - y` is valid without falling out of range. */
- private def canSubtractLongs(x: Long, y: Long): Boolean =
- if (y >= 0) x-y <= x
- else x-y > x
-
- /** Tests whether `-x` is valid without falling out of range. */
- private def canNegateLong(x: Long): Boolean =
- x != Long.MinValue
-
- private object Intrinsics {
- final val ArrayCopy = 1
- final val IdentityHashCode = ArrayCopy + 1
-
- final val PropertiesOf = IdentityHashCode + 1
-
- final val LongToString = PropertiesOf + 1
- final val LongCompare = LongToString + 1
- final val LongBitCount = LongCompare + 1
- final val LongSignum = LongBitCount + 1
- final val LongLeading0s = LongSignum + 1
- final val LongTrailing0s = LongLeading0s + 1
- final val LongToBinStr = LongTrailing0s + 1
- final val LongToHexStr = LongToBinStr + 1
- final val LongToOctalStr = LongToHexStr + 1
-
- final val ByteArrayToInt8Array = LongToOctalStr + 1
- final val ShortArrayToInt16Array = ByteArrayToInt8Array + 1
- final val CharArrayToUint16Array = ShortArrayToInt16Array + 1
- final val IntArrayToInt32Array = CharArrayToUint16Array + 1
- final val FloatArrayToFloat32Array = IntArrayToInt32Array + 1
- final val DoubleArrayToFloat64Array = FloatArrayToFloat32Array + 1
-
- final val Int8ArrayToByteArray = DoubleArrayToFloat64Array + 1
- final val Int16ArrayToShortArray = Int8ArrayToByteArray + 1
- final val Uint16ArrayToCharArray = Int16ArrayToShortArray + 1
- final val Int32ArrayToIntArray = Uint16ArrayToCharArray + 1
- final val Float32ArrayToFloatArray = Int32ArrayToIntArray + 1
- final val Float64ArrayToDoubleArray = Float32ArrayToFloatArray + 1
-
- val intrinsics: Map[String, Int] = Map(
- "jl_System$.arraycopy__O__I__O__I__I__V" -> ArrayCopy,
- "jl_System$.identityHashCode__O__I" -> IdentityHashCode,
-
- "sjsr_package$.propertiesOf__sjs_js_Any__sjs_js_Array" -> PropertiesOf,
-
- "jl_Long$.toString__J__T" -> LongToString,
- "jl_Long$.compare__J__J__I" -> LongCompare,
- "jl_Long$.bitCount__J__I" -> LongBitCount,
- "jl_Long$.signum__J__J" -> LongSignum,
- "jl_Long$.numberOfLeadingZeros__J__I" -> LongLeading0s,
- "jl_Long$.numberOfTrailingZeros__J__I" -> LongTrailing0s,
- "jl_long$.toBinaryString__J__T" -> LongToBinStr,
- "jl_Long$.toHexString__J__T" -> LongToHexStr,
- "jl_Long$.toOctalString__J__T" -> LongToOctalStr,
-
- "sjs_js_typedarray_package$.byteArray2Int8Array__AB__sjs_js_typedarray_Int8Array" -> ByteArrayToInt8Array,
- "sjs_js_typedarray_package$.shortArray2Int16Array__AS__sjs_js_typedarray_Int16Array" -> ShortArrayToInt16Array,
- "sjs_js_typedarray_package$.charArray2Uint16Array__AC__sjs_js_typedarray_Uint16Array" -> CharArrayToUint16Array,
- "sjs_js_typedarray_package$.intArray2Int32Array__AI__sjs_js_typedarray_Int32Array" -> IntArrayToInt32Array,
- "sjs_js_typedarray_package$.floatArray2Float32Array__AF__sjs_js_typedarray_Float32Array" -> FloatArrayToFloat32Array,
- "sjs_js_typedarray_package$.doubleArray2Float64Array__AD__sjs_js_typedarray_Float64Array" -> DoubleArrayToFloat64Array,
-
- "sjs_js_typedarray_package$.int8Array2ByteArray__sjs_js_typedarray_Int8Array__AB" -> Int8ArrayToByteArray,
- "sjs_js_typedarray_package$.int16Array2ShortArray__sjs_js_typedarray_Int16Array__AS" -> Int16ArrayToShortArray,
- "sjs_js_typedarray_package$.uint16Array2CharArray__sjs_js_typedarray_Uint16Array__AC" -> Uint16ArrayToCharArray,
- "sjs_js_typedarray_package$.int32Array2IntArray__sjs_js_typedarray_Int32Array__AI" -> Int32ArrayToIntArray,
- "sjs_js_typedarray_package$.float32Array2FloatArray__sjs_js_typedarray_Float32Array__AF" -> Float32ArrayToFloatArray,
- "sjs_js_typedarray_package$.float64Array2DoubleArray__sjs_js_typedarray_Float64Array__AD" -> Float64ArrayToDoubleArray
- ).withDefaultValue(-1)
- }
-
- private def getIntrinsicCode(target: AbstractMethodID): Int =
- Intrinsics.intrinsics(target.toString)
-
- private trait State[A] {
- def makeBackup(): A
- def restore(backup: A): Unit
- }
-
- private class SimpleState[A](var value: A) extends State[A] {
- def makeBackup(): A = value
- def restore(backup: A): Unit = value = backup
- }
-
- trait AbstractMethodID {
- def inlineable: Boolean
- def isTraitImplForwarder: Boolean
- }
-
- /** Parts of [[GenIncOptimizer#MethodImpl]] with decisions about optimizations. */
- abstract class MethodImpl {
- def encodedName: String
- def optimizerHints: OptimizerHints
- def originalDef: MethodDef
- def thisType: Type
-
- var inlineable: Boolean = false
- var isTraitImplForwarder: Boolean = false
-
- protected def updateInlineable(): Unit = {
- val MethodDef(Ident(methodName, _), params, _, body) = originalDef
-
- isTraitImplForwarder = body match {
- // Shape of forwarders to trait impls
- case TraitImplApply(impl, method, args) =>
- ((args.size == params.size + 1) &&
- (args.head.isInstanceOf[This]) &&
- (args.tail.zip(params).forall {
- case (VarRef(Ident(aname, _), _),
- ParamDef(Ident(pname, _), _, _)) => aname == pname
- case _ => false
- }))
-
- case _ => false
- }
-
- inlineable = optimizerHints.hasInlineAnnot || isTraitImplForwarder || {
- val MethodDef(_, params, _, body) = originalDef
- body match {
- case _:Skip | _:This | _:Literal => true
-
- // Shape of accessors
- case Select(This(), _, _) if params.isEmpty => true
- case Assign(Select(This(), _, _), VarRef(_, _))
- if params.size == 1 => true
-
- // Shape of trivial call-super constructors
- case Block(stats)
- if params.isEmpty && isConstructorName(encodedName) &&
- stats.forall(isTrivialConstructorStat) => true
-
- // Simple method
- case SimpleMethodBody() => true
-
- case _ => false
- }
- }
- }
- }
-
- private def isTrivialConstructorStat(stat: Tree): Boolean = stat match {
- case This() =>
- true
- case StaticApply(This(), _, _, Nil) =>
- true
- case TraitImplApply(_, Ident(methodName, _), This() :: Nil) =>
- methodName.contains("__$init$__")
- case _ =>
- false
- }
-
- private object SimpleMethodBody {
- @tailrec
- def unapply(body: Tree): Boolean = body match {
- case New(_, _, args) => areSimpleArgs(args)
- case Apply(receiver, _, args) => areSimpleArgs(receiver :: args)
- case StaticApply(receiver, _, _, args) => areSimpleArgs(receiver :: args)
- case TraitImplApply(_, _, args) => areSimpleArgs(args)
- case Select(qual, _, _) => isSimpleArg(qual)
- case IsInstanceOf(inner, _) => isSimpleArg(inner)
-
- case Block(List(inner, Undefined())) =>
- unapply(inner)
-
- case Unbox(inner, _) => unapply(inner)
- case AsInstanceOf(inner, _) => unapply(inner)
-
- case _ => isSimpleArg(body)
- }
-
- private def areSimpleArgs(args: List[Tree]): Boolean =
- args.forall(isSimpleArg)
-
- @tailrec
- private def isSimpleArg(arg: Tree): Boolean = arg match {
- case New(_, _, Nil) => true
- case Apply(receiver, _, Nil) => isTrivialArg(receiver)
- case StaticApply(receiver, _, _, Nil) => isTrivialArg(receiver)
- case TraitImplApply(_, _, Nil) => true
-
- case ArrayLength(array) => isTrivialArg(array)
- case ArraySelect(array, index) => isTrivialArg(array) && isTrivialArg(index)
-
- case Unbox(inner, _) => isSimpleArg(inner)
- case AsInstanceOf(inner, _) => isSimpleArg(inner)
-
- case _ =>
- isTrivialArg(arg)
- }
-
- private def isTrivialArg(arg: Tree): Boolean = arg match {
- case _:VarRef | _:This | _:Literal | _:LoadModule =>
- true
- case _ =>
- false
- }
- }
-
- private object BlockOrAlone {
- def unapply(tree: Tree): Some[(List[Tree], Tree)] = Some(tree match {
- case Block(init :+ last) => (init, last)
- case _ => (Nil, tree)
- })
- }
-
- /** Recreates precise [[Infos.MethodInfo]] from the optimized [[MethodDef]]. */
- private def recreateInfo(methodDef: MethodDef): Infos.MethodInfo = {
- new RecreateInfoTraverser().recreateInfo(methodDef)
- }
-
- private final class RecreateInfoTraverser extends Traversers.Traverser {
- import RecreateInfoTraverser._
-
- private val calledMethods = mutable.Map.empty[String, mutable.Set[String]]
- private val calledMethodsStatic = mutable.Map.empty[String, mutable.Set[String]]
- private val instantiatedClasses = mutable.Set.empty[String]
- private val accessedModules = mutable.Set.empty[String]
- private val accessedClassData = mutable.Set.empty[String]
-
- def recreateInfo(methodDef: MethodDef): Infos.MethodInfo = {
- traverse(methodDef.body)
- Infos.MethodInfo(
- encodedName = methodDef.name.name,
- calledMethods = calledMethods.toMap.mapValues(_.toList),
- calledMethodsStatic = calledMethodsStatic.toMap.mapValues(_.toList),
- instantiatedClasses = instantiatedClasses.toList,
- accessedModules = accessedModules.toList,
- accessedClassData = accessedClassData.toList)
- }
-
- private def addCalledMethod(container: String, methodName: String): Unit =
- calledMethods.getOrElseUpdate(container, mutable.Set.empty) += methodName
-
- private def addCalledMethodStatic(container: String, methodName: String): Unit =
- calledMethodsStatic.getOrElseUpdate(container, mutable.Set.empty) += methodName
-
- private def refTypeToClassData(tpe: ReferenceType): String = tpe match {
- case ClassType(cls) => cls
- case ArrayType(base, _) => base
- }
-
- def addAccessedClassData(encodedName: String): Unit = {
- if (!AlwaysPresentClassData.contains(encodedName))
- accessedClassData += encodedName
- }
-
- def addAccessedClassData(tpe: ReferenceType): Unit =
- addAccessedClassData(refTypeToClassData(tpe))
-
- override def traverse(tree: Tree): Unit = {
- tree match {
- case New(ClassType(cls), ctor, _) =>
- instantiatedClasses += cls
- addCalledMethodStatic(cls, ctor.name)
-
- case Apply(receiver, method, _) =>
- receiver.tpe match {
- case ClassType(cls) if !Definitions.HijackedClasses.contains(cls) =>
- addCalledMethod(cls, method.name)
- case AnyType =>
- addCalledMethod(Definitions.ObjectClass, method.name)
- case ArrayType(_, _) if method.name != "clone__O" =>
- /* clone__O is overridden in the pseudo Array classes and is
- * always kept anyway, because it is in scalajsenv.js.
- * Other methods delegate to Object, which we can model with
- * a static call to Object.method.
- */
- addCalledMethodStatic(Definitions.ObjectClass, method.name)
- case _ =>
- // Nothing to do
- }
-
- case StaticApply(_, ClassType(cls), method, _) =>
- addCalledMethodStatic(cls, method.name)
- case TraitImplApply(ClassType(impl), method, _) =>
- addCalledMethodStatic(impl, method.name)
-
- case LoadModule(ClassType(cls)) =>
- accessedModules += cls.stripSuffix("$")
-
- case NewArray(tpe, _) =>
- addAccessedClassData(tpe)
- case ArrayValue(tpe, _) =>
- addAccessedClassData(tpe)
- case IsInstanceOf(_, cls) =>
- addAccessedClassData(cls)
- case AsInstanceOf(_, cls) =>
- addAccessedClassData(cls)
- case ClassOf(cls) =>
- addAccessedClassData(cls)
-
- case _ =>
- }
- super.traverse(tree)
- }
- }
-
- private object RecreateInfoTraverser {
- /** Class data that are never eliminated by dce, so we don't need to
- * record them.
- */
- private val AlwaysPresentClassData = {
- import Definitions._
- Set("V", "Z", "C", "B", "S", "I", "J", "F", "D",
- ObjectClass, StringClass)
- }
- }
-
- private def exceptionMsg(myself: AbstractMethodID,
- attemptedInlining: List[AbstractMethodID]) = {
- val buf = new StringBuilder()
-
- buf.append("The Scala.js optimizer crashed while optimizing " + myself)
-
- buf.append("\nMethods attempted to inline:\n")
-
- for (m <- attemptedInlining) {
- buf.append("* ")
- buf.append(m)
- buf.append('\n')
- }
-
- buf.toString
- }
-
- private class RollbackException(val trampolineId: Int,
- val savedUsedLocalNames: Set[String],
- val savedUsedLabelNames: Set[String],
- val savedStates: List[Any],
- val cont: () => TailRec[Tree]) extends ControlThrowable
-
- class OptimizeException(val myself: AbstractMethodID,
- val attemptedInlining: List[AbstractMethodID], cause: Throwable
- ) extends Exception(exceptionMsg(myself, attemptedInlining), cause)
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSOptimizer.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSOptimizer.scala
deleted file mode 100644
index 646484b..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/optimizer/ScalaJSOptimizer.scala
+++ /dev/null
@@ -1,552 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.optimizer
-
-import scala.annotation.{switch, tailrec}
-
-import scala.collection.mutable
-import scala.collection.immutable.{Seq, Traversable}
-
-import java.net.URI
-
-import scala.scalajs.ir
-import ir.Infos
-import ir.ClassKind
-
-import scala.scalajs.tools.logging._
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.classpath._
-import scala.scalajs.tools.sourcemap._
-import scala.scalajs.tools.corelib._
-
-import scala.scalajs.tools.sem.Semantics
-
-import scala.scalajs.tools.javascript
-import javascript.{Trees => js}
-
-/** Scala.js optimizer: does type-aware global dce. */
-class ScalaJSOptimizer(
- semantics: Semantics,
- optimizerFactory: (Semantics) => GenIncOptimizer) {
- import ScalaJSOptimizer._
-
- private val classEmitter = new javascript.ScalaJSClassEmitter(semantics)
-
- private[this] var persistentState: PersistentState = new PersistentState
- private[this] var optimizer: GenIncOptimizer = optimizerFactory(semantics)
-
- def this(semantics: Semantics) = this(semantics, new IncOptimizer(_))
-
- /** Applies Scala.js-specific optimizations to a CompleteIRClasspath.
- * See [[ScalaJSOptimizer.Inputs]] for details about the required and
- * optional inputs.
- * See [[ScalaJSOptimizer.OutputConfig]] for details about the configuration
- * for the output of this method.
- * Returns a [[CompleteCIClasspath]] containing the result of the
- * optimizations.
- *
- * analyzes, dead code eliminates and concatenates IR content
- * - Maintains/establishes order
- * - No IR in result
- * - CoreJSLibs in result (since they are implicitly in the CompleteIRCP)
- */
- def optimizeCP(inputs: Inputs[IRClasspath], outCfg: OutputConfig,
- logger: Logger): LinkedClasspath = {
-
- val cp = inputs.input
-
- CacheUtils.cached(cp.version, outCfg.output, outCfg.cache) {
- logger.info(s"Fast optimizing ${outCfg.output.path}")
- optimizeIR(inputs.copy(input = inputs.input.scalaJSIR), outCfg, logger)
- }
-
- new LinkedClasspath(cp.jsLibs, outCfg.output, cp.requiresDOM, cp.version)
- }
-
- def optimizeIR(inputs: Inputs[Traversable[VirtualScalaJSIRFile]],
- outCfg: OutputConfig, logger: Logger): Unit = {
-
- val builder = {
- import outCfg._
- if (wantSourceMap)
- new JSFileBuilderWithSourceMap(output.name,
- output.contentWriter,
- output.sourceMapWriter,
- relativizeSourceMapBase)
- else
- new JSFileBuilder(output.name, output.contentWriter)
- }
-
- builder.addLine("'use strict';")
- CoreJSLibs.libs(semantics).foreach(builder.addFile _)
-
- optimizeIR(inputs, outCfg, builder, logger)
-
- builder.complete()
- builder.closeWriters()
- }
-
- def optimizeIR(inputs: Inputs[Traversable[VirtualScalaJSIRFile]],
- outCfg: OptimizerConfig, builder: JSTreeBuilder, logger: Logger): Unit = {
-
- /* Handle tree equivalence: If we handled source maps so far, positions are
- still up-to-date. Otherwise we need to flush the state if proper
- positions are requested now.
- */
- if (outCfg.wantSourceMap && !persistentState.wasWithSourceMap)
- clean()
-
- persistentState.wasWithSourceMap = outCfg.wantSourceMap
-
- persistentState.startRun()
- try {
- import inputs._
- val allData =
- GenIncOptimizer.logTime(logger, "Read info") {
- readAllData(inputs.input, logger)
- }
- val (useOptimizer, refinedAnalyzer) = GenIncOptimizer.logTime(
- logger, "Optimizations part") {
- val analyzer =
- GenIncOptimizer.logTime(logger, "Compute reachability") {
- val analyzer = new Analyzer(logger, semantics, allData,
- globalWarnEnabled = true,
- isBeforeOptimizer = !outCfg.disableOptimizer)
- analyzer.computeReachability(manuallyReachable, noWarnMissing)
- analyzer
- }
- if (outCfg.checkIR) {
- GenIncOptimizer.logTime(logger, "Check IR") {
- if (analyzer.allAvailable)
- checkIR(analyzer, logger)
- else if (inputs.noWarnMissing.isEmpty)
- sys.error("Could not check IR because there where linking errors.")
- }
- }
- def getClassTreeIfChanged(encodedName: String,
- lastVersion: Option[String]): Option[(ir.Trees.ClassDef, Option[String])] = {
- val persistentFile = persistentState.encodedNameToPersistentFile(encodedName)
- persistentFile.treeIfChanged(lastVersion)
- }
-
- val useOptimizer = analyzer.allAvailable && !outCfg.disableOptimizer
-
- if (outCfg.batchMode)
- optimizer = optimizerFactory(semantics)
-
- val refinedAnalyzer = if (useOptimizer) {
- GenIncOptimizer.logTime(logger, "Inliner") {
- optimizer.update(analyzer, getClassTreeIfChanged,
- outCfg.wantSourceMap, logger)
- }
- GenIncOptimizer.logTime(logger, "Refined reachability analysis") {
- val refinedData = computeRefinedData(allData, optimizer)
- val refinedAnalyzer = new Analyzer(logger, semantics, refinedData,
- globalWarnEnabled = false,
- isBeforeOptimizer = false)
- refinedAnalyzer.computeReachability(manuallyReachable, noWarnMissing)
- refinedAnalyzer
- }
- } else {
- if (inputs.noWarnMissing.isEmpty && !outCfg.disableOptimizer)
- logger.warn("Not running the inliner because there where linking errors.")
- analyzer
- }
- (useOptimizer, refinedAnalyzer)
- }
- GenIncOptimizer.logTime(logger, "Write DCE'ed output") {
- buildDCEedOutput(builder, refinedAnalyzer, useOptimizer)
- }
- } finally {
- persistentState.endRun(outCfg.unCache)
- logger.debug(
- s"Inc. opt stats: reused: ${persistentState.statsReused} -- "+
- s"invalidated: ${persistentState.statsInvalidated} -- "+
- s"trees read: ${persistentState.statsTreesRead}")
- }
- }
-
- /** Resets all persistent state of this optimizer */
- def clean(): Unit = {
- persistentState = new PersistentState
- optimizer = optimizerFactory(semantics)
- }
-
- private def readAllData(ir: Traversable[VirtualScalaJSIRFile],
- logger: Logger): scala.collection.Seq[Infos.ClassInfo] = {
- ir.map(persistentState.getPersistentIRFile(_).info).toSeq
- }
-
- private def checkIR(analyzer: Analyzer, logger: Logger): Unit = {
- val allClassDefs = for {
- classInfo <- analyzer.classInfos.values
- persistentIRFile <- persistentState.encodedNameToPersistentFile.get(
- classInfo.encodedName)
- } yield persistentIRFile.tree
- val checker = new IRChecker(analyzer, allClassDefs.toSeq, logger)
- if (!checker.check())
- sys.error(s"There were ${checker.errorCount} IR checking errors.")
- }
-
- private def computeRefinedData(
- allData: scala.collection.Seq[Infos.ClassInfo],
- optimizer: GenIncOptimizer): scala.collection.Seq[Infos.ClassInfo] = {
-
- def refineMethodInfo(container: optimizer.MethodContainer,
- methodInfo: Infos.MethodInfo): Infos.MethodInfo = {
- container.methods.get(methodInfo.encodedName).fold(methodInfo) {
- methodImpl => methodImpl.preciseInfo
- }
- }
-
- def refineMethodInfos(container: optimizer.MethodContainer,
- methodInfos: List[Infos.MethodInfo]): List[Infos.MethodInfo] = {
- methodInfos.map(m => refineMethodInfo(container, m))
- }
-
- def refineClassInfo(container: optimizer.MethodContainer,
- info: Infos.ClassInfo): Infos.ClassInfo = {
- val refinedMethods = refineMethodInfos(container, info.methods)
- Infos.ClassInfo(info.name, info.encodedName, info.isExported,
- info.ancestorCount, info.kind, info.superClass, info.ancestors,
- Infos.OptimizerHints.empty, refinedMethods)
- }
-
- for {
- info <- allData
- } yield {
- info.kind match {
- case ClassKind.Class | ClassKind.ModuleClass =>
- optimizer.getClass(info.encodedName).fold(info) {
- cls => refineClassInfo(cls, info)
- }
-
- case ClassKind.TraitImpl =>
- optimizer.getTraitImpl(info.encodedName).fold(info) {
- impl => refineClassInfo(impl, info)
- }
-
- case _ =>
- info
- }
- }
- }
-
- private def buildDCEedOutput(builder: JSTreeBuilder,
- analyzer: Analyzer, useInliner: Boolean): Unit = {
-
- def compareClassInfo(lhs: analyzer.ClassInfo, rhs: analyzer.ClassInfo) = {
- if (lhs.ancestorCount != rhs.ancestorCount) lhs.ancestorCount < rhs.ancestorCount
- else lhs.encodedName.compareTo(rhs.encodedName) < 0
- }
-
- def addPersistentFile(classInfo: analyzer.ClassInfo,
- persistentFile: PersistentIRFile) = {
- import ir.Trees._
- import javascript.JSDesugaring.{desugarJavaScript => desugar}
-
- val d = persistentFile.desugared
- lazy val classDef = {
- persistentState.statsTreesRead += 1
- persistentFile.tree
- }
-
- def addTree(tree: js.Tree): Unit =
- builder.addJSTree(tree)
-
- def addReachableMethods(emitFun: (String, MethodDef) => js.Tree): Unit = {
- /* This is a bit convoluted because we have to:
- * * avoid to use classDef at all if we already know all the needed methods
- * * if any new method is needed, better to go through the defs once
- */
- val methodNames = d.methodNames.getOrElseUpdate(
- classDef.defs collect {
- case MethodDef(Ident(encodedName, _), _, _, _) => encodedName
- })
- val reachableMethods = methodNames.filter(
- name => classInfo.methodInfos(name).isReachable)
- if (reachableMethods.forall(d.methods.contains(_))) {
- for (encodedName <- reachableMethods) {
- addTree(d.methods(encodedName))
- }
- } else {
- classDef.defs.foreach {
- case m: MethodDef if m.name.isInstanceOf[Ident] =>
- if (classInfo.methodInfos(m.name.name).isReachable) {
- addTree(d.methods.getOrElseUpdate(m.name.name,
- emitFun(classInfo.encodedName, m)))
- }
- case _ =>
- }
- }
- }
-
- if (classInfo.isImplClass) {
- if (useInliner) {
- for {
- method <- optimizer.findTraitImpl(classInfo.encodedName).methods.values
- if (classInfo.methodInfos(method.encodedName).isReachable)
- } {
- addTree(method.desugaredDef)
- }
- } else {
- addReachableMethods(classEmitter.genTraitImplMethod)
- }
- } else if (!classInfo.hasMoreThanData) {
- // there is only the data anyway
- addTree(d.wholeClass.getOrElseUpdate(
- classEmitter.genClassDef(classDef)))
- } else {
- if (classInfo.isAnySubclassInstantiated) {
- addTree(d.constructor.getOrElseUpdate(
- classEmitter.genConstructor(classDef)))
- if (useInliner) {
- for {
- method <- optimizer.findClass(classInfo.encodedName).methods.values
- if (classInfo.methodInfos(method.encodedName).isReachable)
- } {
- addTree(method.desugaredDef)
- }
- } else {
- addReachableMethods(classEmitter.genMethod)
- }
- addTree(d.exportedMembers.getOrElseUpdate(js.Block {
- classDef.defs collect {
- case m: MethodDef if m.name.isInstanceOf[StringLiteral] =>
- classEmitter.genMethod(classInfo.encodedName, m)
- case p: PropertyDef =>
- classEmitter.genProperty(classInfo.encodedName, p)
- }
- }(classDef.pos)))
- }
- if (classInfo.isDataAccessed) {
- addTree(d.typeData.getOrElseUpdate(js.Block(
- classEmitter.genInstanceTests(classDef),
- classEmitter.genArrayInstanceTests(classDef),
- classEmitter.genTypeData(classDef)
- )(classDef.pos)))
- }
- if (classInfo.isAnySubclassInstantiated)
- addTree(d.setTypeData.getOrElseUpdate(
- classEmitter.genSetTypeData(classDef)))
- if (classInfo.isModuleAccessed)
- addTree(d.moduleAccessor.getOrElseUpdate(
- classEmitter.genModuleAccessor(classDef)))
- addTree(d.classExports.getOrElseUpdate(
- classEmitter.genClassExports(classDef)))
- }
- }
-
-
- for {
- classInfo <- analyzer.classInfos.values.toSeq.sortWith(compareClassInfo)
- if classInfo.isNeededAtAll
- } {
- val optPersistentFile =
- persistentState.encodedNameToPersistentFile.get(classInfo.encodedName)
-
- // if we have a persistent file, this is not a dummy class
- optPersistentFile.fold {
- if (classInfo.isAnySubclassInstantiated) {
- // Subclass will emit constructor that references this dummy class.
- // Therefore, we need to emit a dummy parent.
- builder.addJSTree(
- classEmitter.genDummyParent(classInfo.encodedName))
- }
- } { pf => addPersistentFile(classInfo, pf) }
- }
- }
-}
-
-object ScalaJSOptimizer {
- /** Inputs of the Scala.js optimizer. */
- final case class Inputs[T](
- /** The CompleteNCClasspath or the IR files to be packaged. */
- input: T,
- /** Manual additions to reachability */
- manuallyReachable: Seq[ManualReachability] = Nil,
- /** Elements we won't warn even if they don't exist */
- noWarnMissing: Seq[NoWarnMissing] = Nil
- )
-
- sealed abstract class ManualReachability
- final case class ReachObject(name: String) extends ManualReachability
- final case class Instantiate(name: String) extends ManualReachability
- final case class ReachMethod(className: String, methodName: String,
- static: Boolean) extends ManualReachability
-
- sealed abstract class NoWarnMissing
- final case class NoWarnClass(className: String) extends NoWarnMissing
- final case class NoWarnMethod(className: String, methodName: String)
- extends NoWarnMissing
-
- /** Configurations relevant to the optimizer */
- trait OptimizerConfig {
- /** Ask to produce source map for the output. Is used in the incremental
- * optimizer to decide whether a position change should trigger re-inlining
- */
- val wantSourceMap: Boolean
- /** If true, performs expensive checks of the IR for the used parts. */
- val checkIR: Boolean
- /** If true, the optimizer removes trees that have not been used in the
- * last run from the cache. Otherwise, all trees that has been used once,
- * are kept in memory. */
- val unCache: Boolean
- /** If true, no optimizations are performed */
- val disableOptimizer: Boolean
- /** If true, nothing is performed incrementally */
- val batchMode: Boolean
- }
-
- /** Configuration for the output of the Scala.js optimizer. */
- final case class OutputConfig(
- /** Writer for the output. */
- output: WritableVirtualJSFile,
- /** Cache file */
- cache: Option[WritableVirtualTextFile] = None,
- /** Ask to produce source map for the output */
- wantSourceMap: Boolean = false,
- /** Base path to relativize paths in the source map. */
- relativizeSourceMapBase: Option[URI] = None,
- /** If true, performs expensive checks of the IR for the used parts. */
- checkIR: Boolean = false,
- /** If true, the optimizer removes trees that have not been used in the
- * last run from the cache. Otherwise, all trees that has been used once,
- * are kept in memory. */
- unCache: Boolean = true,
- /** If true, no optimizations are performed */
- disableOptimizer: Boolean = false,
- /** If true, nothing is performed incrementally */
- batchMode: Boolean = false
- ) extends OptimizerConfig
-
- // Private helpers -----------------------------------------------------------
-
- private final class PersistentState {
- val files = mutable.Map.empty[String, PersistentIRFile]
- val encodedNameToPersistentFile =
- mutable.Map.empty[String, PersistentIRFile]
-
- var statsReused: Int = 0
- var statsInvalidated: Int = 0
- var statsTreesRead: Int = 0
-
- var wasWithSourceMap: Boolean = true
-
- def startRun(): Unit = {
- statsReused = 0
- statsInvalidated = 0
- statsTreesRead = 0
- for (file <- files.values)
- file.startRun()
- }
-
- def getPersistentIRFile(irFile: VirtualScalaJSIRFile): PersistentIRFile = {
- val file = files.getOrElseUpdate(irFile.path,
- new PersistentIRFile(irFile.path))
- if (file.updateFile(irFile))
- statsReused += 1
- else
- statsInvalidated += 1
- encodedNameToPersistentFile += ((file.info.encodedName, file))
- file
- }
-
- def endRun(unCache: Boolean): Unit = {
- // "Garbage-collect" persisted versions of files that have disappeared
- files.retain((_, f) => f.cleanAfterRun(unCache))
- encodedNameToPersistentFile.clear()
- }
- }
-
- private final class PersistentIRFile(val path: String) {
- import ir.Trees._
-
- private[this] var existedInThisRun: Boolean = false
- private[this] var desugaredUsedInThisRun: Boolean = false
-
- private[this] var irFile: VirtualScalaJSIRFile = null
- private[this] var version: Option[String] = None
- private[this] var _info: Infos.ClassInfo = null
- private[this] var _tree: ClassDef = null
- private[this] var _desugared: Desugared = null
-
- def startRun(): Unit = {
- existedInThisRun = false
- desugaredUsedInThisRun = false
- }
-
- def updateFile(irFile: VirtualScalaJSIRFile): Boolean = {
- existedInThisRun = true
- this.irFile = irFile
- if (version.isDefined && version == irFile.version) {
- // yeepeeh, nothing to do
- true
- } else {
- version = irFile.version
- _info = irFile.info
- _tree = null
- _desugared = null
- false
- }
- }
-
- def info: Infos.ClassInfo = _info
-
- def desugared: Desugared = {
- desugaredUsedInThisRun = true
- if (_desugared == null)
- _desugared = new Desugared
- _desugared
- }
-
- def tree: ClassDef = {
- if (_tree == null)
- _tree = irFile.tree
- _tree
- }
-
- def treeIfChanged(lastVersion: Option[String]): Option[(ClassDef, Option[String])] = {
- if (lastVersion.isDefined && lastVersion == version) None
- else Some((tree, version))
- }
-
- /** Returns true if this file should be kept for the next run at all. */
- def cleanAfterRun(unCache: Boolean): Boolean = {
- irFile = null
- if (unCache && !desugaredUsedInThisRun)
- _desugared = null // free desugared if unused in this run
- existedInThisRun
- }
- }
-
- private final class Desugared {
- // for class kinds that are not decomposed
- val wholeClass = new OneTimeCache[js.Tree]
-
- val constructor = new OneTimeCache[js.Tree]
- val methodNames = new OneTimeCache[List[String]]
- val methods = mutable.Map.empty[String, js.Tree]
- val exportedMembers = new OneTimeCache[js.Tree]
- val typeData = new OneTimeCache[js.Tree]
- val setTypeData = new OneTimeCache[js.Tree]
- val moduleAccessor = new OneTimeCache[js.Tree]
- val classExports = new OneTimeCache[js.Tree]
- }
-
- private final class OneTimeCache[A >: Null] {
- private[this] var value: A = null
- def getOrElseUpdate(v: => A): A = {
- if (value == null)
- value = v
- value
- }
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/CheckedBehavior.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/CheckedBehavior.scala
deleted file mode 100644
index 4668b3c..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/CheckedBehavior.scala
+++ /dev/null
@@ -1,24 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.sem
-
-sealed abstract class CheckedBehavior {
- import CheckedBehavior._
- def optimized: CheckedBehavior = this match {
- case Fatal => Unchecked
- case _ => this
- }
-}
-
-object CheckedBehavior {
- case object Compliant extends CheckedBehavior
- case object Fatal extends CheckedBehavior
- case object Unchecked extends CheckedBehavior
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/Semantics.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/Semantics.scala
deleted file mode 100644
index 9d17b06..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sem/Semantics.scala
+++ /dev/null
@@ -1,97 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.sem
-
-import scala.collection.immutable.Traversable
-
-final class Semantics private (
- val asInstanceOfs: CheckedBehavior,
- val strictFloats: Boolean) {
-
- import Semantics._
-
- def withAsInstanceOfs(behavior: CheckedBehavior): Semantics =
- copy(asInstanceOfs = behavior)
-
- def withStrictFloats(strictFloats: Boolean): Semantics =
- copy(strictFloats = strictFloats)
-
- def optimized: Semantics =
- copy(asInstanceOfs = this.asInstanceOfs.optimized)
-
- override def equals(that: Any): Boolean = that match {
- case that: Semantics =>
- this.asInstanceOfs == that.asInstanceOfs &&
- this.strictFloats == that.strictFloats
- case _ =>
- false
- }
-
- override def hashCode(): Int = {
- import scala.util.hashing.MurmurHash3._
- var acc = HashSeed
- acc = mix(acc, asInstanceOfs.hashCode)
- acc = mixLast(acc, strictFloats.##)
- finalizeHash(acc, 1)
- }
-
- override def toString(): String = {
- s"""Semantics(
- | asInstanceOfs = $asInstanceOfs,
- | strictFloats = $strictFloats
- |)""".stripMargin
- }
-
- /** Checks whether the given semantics setting is Java compliant */
- def isCompliant(name: String): Boolean = name match {
- case "asInstanceOfs" => asInstanceOfs == CheckedBehavior.Compliant
- case "strictFloats" => strictFloats
- case _ => false
- }
-
- /** Retrieve a list of semantics which are set to compliant */
- def compliants: List[String] = {
- def cl(name: String, cond: Boolean) = if (cond) List(name) else Nil
-
- cl("asInstanceOfs", asInstanceOfs == CheckedBehavior.Compliant) ++
- cl("strictFloats", strictFloats)
- }
-
- private def copy(
- asInstanceOfs: CheckedBehavior = this.asInstanceOfs,
- strictFloats: Boolean = this.strictFloats): Semantics = {
- new Semantics(
- asInstanceOfs = asInstanceOfs,
- strictFloats = strictFloats)
- }
-}
-
-object Semantics {
- private val HashSeed =
- scala.util.hashing.MurmurHash3.stringHash(classOf[Semantics].getName)
-
- val Defaults: Semantics = new Semantics(
- asInstanceOfs = CheckedBehavior.Fatal,
- strictFloats = false)
-
- def compliantTo(semantics: Traversable[String]): Semantics = {
- import Defaults._
- import CheckedBehavior._
-
- val semsSet = semantics.toSet
-
- def sw[T](name: String, compliant: T, default: T): T =
- if (semsSet.contains(name)) compliant else default
-
- new Semantics(
- asInstanceOfs = sw("asInstanceOfs", Compliant, asInstanceOfs),
- strictFloats = sw("strictFloats", true, strictFloats))
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/JSFileBuilder.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/JSFileBuilder.scala
deleted file mode 100644
index 1bf2254..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/JSFileBuilder.scala
+++ /dev/null
@@ -1,144 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.sourcemap
-
-import scala.annotation.tailrec
-
-import scala.collection.mutable
-
-import java.io._
-import java.util.regex.Pattern
-import java.net.{ URI, URISyntaxException }
-
-import scala.scalajs.ir.Position
-import scala.scalajs.tools.{javascript => js}
-import scala.scalajs.tools.io._
-import scala.scalajs.tools.optimizer.JSTreeBuilder
-
-class JSFileBuilder(val name: String,
- protected val outputWriter: Writer) extends JSTreeBuilder {
- def addLine(line: String): Unit = {
- outputWriter.write(line)
- outputWriter.write('\n')
- }
-
- def addLines(lines: Seq[String]): Unit =
- lines.foreach(addLine)
-
- def addFile(file: VirtualJSFile): Unit =
- addPartsOfFile(file)(!_.startsWith("//# sourceMappingURL="))
-
- def addPartsOfFile(file: VirtualJSFile)(selector: String => Boolean): Unit = {
- for (line <- file.readLines() if selector(line))
- addLine(line)
- }
-
- /** Add a JavaScript tree representing a statement.
- * The tree must be a valid JavaScript tree (typically obtained by
- * desugaring a full-fledged IR tree).
- */
- def addJSTree(tree: js.Trees.Tree): Unit = {
- val printer = new js.Printers.JSTreePrinter(outputWriter)
- printer.printTopLevelTree(tree)
- // Do not close the printer: we do not have ownership of the writers
- }
-
- /** Closes the underlying writer(s).
- */
- def closeWriters(): Unit = {
- outputWriter.close()
- }
-}
-
-class JSFileBuilderWithSourceMapWriter(n: String, ow: Writer,
- protected val sourceMapWriter: SourceMapWriter)
- extends JSFileBuilder(n, ow) {
-
- override def addLine(line: String): Unit = {
- super.addLine(line)
- sourceMapWriter.nextLine()
- }
-
- private final val NotSelected = -1
-
- override def addPartsOfFile(file: VirtualJSFile)(
- selector: String => Boolean): Unit = {
- val br = new BufferedReader(file.reader)
- try {
- // Select lines, and remember offsets
- val offsets = new mutable.ArrayBuffer[Int] // (maybe NotSelected)
- val selectedLineLengths = new mutable.ArrayBuffer[Int]
- var line: String = br.readLine()
- var selectedCount = 0
- while (line != null) {
- if (selector(line)) {
- super.addLine(line) // super call not to advance line in source map
- offsets += selectedCount
- selectedLineLengths += line.length
- selectedCount += 1
- } else {
- offsets += NotSelected
- }
- line = br.readLine()
- }
-
- /* We ignore a potential source map.
- * This happens typically for corejslib.js and other helper files
- * written directly in JS.
- * We generate a fake line-by-line source map for these on the fly
- */
- val sourceFile = file.toURI
-
- for (lineNumber <- 0 until offsets.size) {
- val offset = offsets(lineNumber)
- if (offset != NotSelected) {
- val originalPos = Position(sourceFile, lineNumber, 0)
- sourceMapWriter.startNode(0, originalPos, None)
- sourceMapWriter.endNode(selectedLineLengths(offset))
- sourceMapWriter.nextLine()
- }
- }
- } finally {
- br.close()
- }
- }
-
- override def addJSTree(tree: js.Trees.Tree): Unit = {
- val printer = new js.Printers.JSTreePrinterWithSourceMap(
- outputWriter, sourceMapWriter)
- printer.printTopLevelTree(tree)
- // Do not close the printer: we do not have ownership of the writers
- }
-
- override def complete(): Unit = {
- super.complete()
- sourceMapWriter.complete()
- }
-
-}
-
-class JSFileBuilderWithSourceMap(n: String, ow: Writer,
- sourceMapOutputWriter: Writer,
- relativizeSourceMapBasePath: Option[URI] = None)
- extends JSFileBuilderWithSourceMapWriter(
- n, ow,
- new SourceMapWriter(sourceMapOutputWriter, n,
- relativizeSourceMapBasePath)) {
-
- override def complete(): Unit = {
- addLine("//# sourceMappingURL=" + name + ".map")
- super.complete()
- }
-
- override def closeWriters(): Unit = {
- super.closeWriters()
- sourceMapOutputWriter.close()
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapWriter.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapWriter.scala
deleted file mode 100644
index 5d8bdb1..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/sourcemap/SourceMapWriter.scala
+++ /dev/null
@@ -1,213 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.sourcemap
-
-import java.io.Writer
-import java.net.URI
-
-import scala.collection.mutable.{ ListBuffer, HashMap, Stack, StringBuilder }
-
-import scala.scalajs.ir
-import ir.Position
-import ir.Position._
-import ir.Utils
-
-object SourceMapWriter {
- private val Base64Map =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
- "abcdefghijklmnopqrstuvwxyz" +
- "0123456789+/"
-
- // Some constants for writeBase64VLQ
- // Each base-64 digit covers 6 bits, but 1 is used for the continuation
- private final val VLQBaseShift = 5
- private final val VLQBase = 1 << VLQBaseShift
- private final val VLQBaseMask = VLQBase - 1
- private final val VLQContinuationBit = VLQBase
-
- private def jsonString(s: String) =
- "\"" + Utils.escapeJS(s) + "\""
-}
-
-class SourceMapWriter(
- val out: Writer,
- val generatedFile: String,
- val relativizeBaseURI: Option[URI] = None) {
-
- import SourceMapWriter._
-
- private val sources = new ListBuffer[String]
- private val _srcToIndex = new HashMap[SourceFile, Int]
-
- private val names = new ListBuffer[String]
- private val _nameToIndex = new HashMap[String, Int]
-
- private val nodePosStack = new Stack[(Position, Option[String])]
- nodePosStack.push((NoPosition, None))
-
- private var lineCountInGenerated = 0
- private var lastColumnInGenerated = 0
- private var firstSegmentOfLine = true
- private var lastSource: SourceFile = null
- private var lastSourceIndex = 0
- private var lastLine: Int = 0
- private var lastColumn: Int = 0
- private var lastNameIndex: Int = 0
-
- private var pendingColumnInGenerated: Int = -1
- private var pendingPos: Position = NoPosition
- private var pendingName: Option[String] = None
-
- writeHeader()
-
- private def sourceToIndex(source: SourceFile) =
- _srcToIndex.getOrElseUpdate(source,
- (sources += sourceToURI(source)).size-1)
-
- /** Relatively hacky way to get a Web-friendly URI to the source file */
- private def sourceToURI(source: SourceFile): String = {
- val uri = source
- val relURI = relativizeBaseURI.fold(uri)(Utils.relativize(_, uri))
-
- Utils.fixFileURI(relURI).toASCIIString
- }
-
- private def nameToIndex(name: String) =
- _nameToIndex.getOrElseUpdate(name, (names += name).size-1)
-
- private def writeHeader(): Unit = {
- out.write("{\n")
- out.write("\"version\": 3,\n")
- out.write("\"file\": " + jsonString(generatedFile) + ",\n")
- out.write("\"mappings\": \"")
- }
-
- def nextLine(): Unit = {
- writePendingSegment()
- out.write(';')
- lineCountInGenerated += 1
- lastColumnInGenerated = 0
- firstSegmentOfLine = true
- pendingColumnInGenerated = -1
- pendingPos = nodePosStack.top._1
- pendingName = nodePosStack.top._2
- }
-
- def startNode(column: Int, originalPos: Position,
- originalName: Option[String] = None): Unit = {
- nodePosStack.push((originalPos, originalName))
- startSegment(column, originalPos, originalName)
- }
-
- def endNode(column: Int): Unit = {
- nodePosStack.pop()
- startSegment(column, nodePosStack.top._1, nodePosStack.top._2)
- }
-
- private def startSegment(startColumn: Int, originalPos: Position,
- originalName: Option[String]): Unit = {
- // There is no point in outputting a segment with the same information
- if ((originalPos == pendingPos) && (originalName == pendingName))
- return
-
- // Write pending segment if it covers a non-empty range
- if (startColumn != pendingColumnInGenerated)
- writePendingSegment()
-
- // New pending
- pendingColumnInGenerated = startColumn
- pendingPos = originalPos
- pendingName =
- if (startColumn != pendingColumnInGenerated) originalName
- else pendingName orElse originalName
- }
-
- private def writePendingSegment() {
- if (pendingColumnInGenerated < 0)
- return
-
- // Segments of a line are separated by ','
- if (firstSegmentOfLine) firstSegmentOfLine = false
- else out.write(',')
-
- // Generated column field
- writeBase64VLQ(pendingColumnInGenerated-lastColumnInGenerated)
- lastColumnInGenerated = pendingColumnInGenerated
-
- // If the position is NoPosition, stop here
- if (!pendingPos.isDefined)
- return
-
- // Extract relevant properties of pendingPos
- val source = pendingPos.source
- val line = pendingPos.line
- val column = pendingPos.column
-
- // Source index field
- if (source == lastSource) { // highly likely
- writeBase64VLQ0()
- } else {
- val sourceIndex = sourceToIndex(source)
- writeBase64VLQ(sourceIndex-lastSourceIndex)
- lastSource = source
- lastSourceIndex = sourceIndex
- }
-
- // Line field
- writeBase64VLQ(line - lastLine)
- lastLine = line
-
- // Column field
- writeBase64VLQ(column - lastColumn)
- lastColumn = column
-
- // Name field
- if (pendingName.isDefined) {
- val nameIndex = nameToIndex(pendingName.get)
- writeBase64VLQ(nameIndex-lastNameIndex)
- lastNameIndex = nameIndex
- }
- }
-
- def complete(): Unit = {
- writePendingSegment()
-
- out.write("\",\n")
- out.write(
- sources.map(jsonString(_)).mkString("\"sources\": [", ", ", "],\n"))
- out.write(
- names.map(jsonString(_)).mkString("\"names\": [", ", ", "],\n"))
- out.write("\"lineCount\": "+lineCountInGenerated+"\n")
- out.write("}\n")
- }
-
- /** Write the Base 64 VLQ of an integer to the mappings
- * Inspired by the implementation in Closure Compiler:
- * http://code.google.com/p/closure-compiler/source/browse/src/com/google/debugging/sourcemap/Base64VLQ.java
- */
- private def writeBase64VLQ(value0: Int): Unit = {
- // Sign is encoded in the least significant bit
- var value =
- if (value0 < 0) ((-value0) << 1) + 1
- else value0 << 1
-
- // Write as many base-64 digits as necessary to encode value
- do {
- var digit = value & VLQBaseMask
- value = value >>> VLQBaseShift
- if (value != 0)
- digit |= VLQContinuationBit
- out.write(Base64Map.charAt(digit))
- } while (value != 0)
- }
-
- private def writeBase64VLQ0(): Unit =
- out.write('A')
-}