diff options
Diffstat (limited to 'examples/scala-js/tools/shared/src/main')
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') -} |