summaryrefslogtreecommitdiff
path: root/examples/scala-js/compiler/src/main/scala
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/compiler/src/main/scala')
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala143
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/Compat210Component.scala108
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala3911
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSExports.scala751
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSFiles.scala51
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSDefinitions.scala128
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala261
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSGlobalAddons.scala244
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSPrimitives.scala119
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSTreeExtractors.scala66
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSExports.scala251
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSInterop.scala621
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSOptions.scala30
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSPlugin.scala143
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala252
-rw-r--r--examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/util/ScopedVar.scala38
16 files changed, 0 insertions, 7117 deletions
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala
deleted file mode 100644
index 026d664..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ClassInfos.scala
+++ /dev/null
@@ -1,143 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Sébastien Doeraene
- */
-
-package scala.scalajs.compiler
-
-import scala.language.implicitConversions
-
-import scala.collection.mutable
-import scala.tools.nsc._
-
-import java.io.{ File, PrintWriter, BufferedOutputStream, FileOutputStream }
-
-import scala.scalajs.ir
-import ir.{Trees => js, Types => jstpe, ClassKind}
-import ir.Infos._
-
-trait ClassInfos extends SubComponent { self: GenJSCode =>
- import global._
- import jsAddons._
-
- /** Class data that are never eliminated by dce, so we don't need to
- * record them.
- */
- private val AlwaysPresentClassData = {
- import ir.Definitions._
- Set("V", "Z", "C", "B", "S", "I", "J", "F", "D",
- ObjectClass, StringClass)
- }
-
- class ClassInfoBuilder(val symbol: ClassSymbol) {
- val name = classNameOf(symbol)
- val encodedName = encodeClassFullName(symbol)
- var isExported: Boolean = false
- val ancestorCount = symbol.ancestors.count(!_.isInterface)
- val kind = {
- if (isStaticModule(symbol)) ClassKind.ModuleClass
- else if (symbol.isInterface) ClassKind.Interface
- else if (isRawJSType(symbol.tpe)) ClassKind.RawJSType
- else if (isHijackedBoxedClass(symbol)) ClassKind.HijackedClass
- else if (symbol.isImplClass) ClassKind.TraitImpl
- else ClassKind.Class
- }
- val superClass =
- if (kind.isClass || kind == ClassKind.HijackedClass)
- encodeClassFullName(symbol.superClass)
- else
- ""
- val ancestors = (symbol :: symbol.ancestors) map encodeClassFullName
-
- var optimizerHints: OptimizerHints = OptimizerHints.empty
-
- val methodInfos = mutable.ListBuffer.empty[MethodInfoBuilder]
-
- def addMethod(encodedName: String, isAbstract: Boolean = false,
- isExported: Boolean = false): MethodInfoBuilder = {
- val b = new MethodInfoBuilder(encodedName, isAbstract, isExported)
- methodInfos += b
- b
- }
-
- def result(): ClassInfo = {
- ClassInfo(name, encodedName, isExported, ancestorCount, kind,
- superClass, ancestors, optimizerHints,
- methodInfos.map(_.result()).result())
- }
- }
-
- class MethodInfoBuilder(val encodedName: String,
- val isAbstract: Boolean = false,
- val isExported: Boolean = false) {
-
- val calledMethods = mutable.Set.empty[(String, String)] // (tpe, method)
- val calledMethodsStatic = mutable.Set.empty[(String, String)] // (class, method)
- val instantiatedClasses = mutable.Set.empty[String]
- val accessedModules = mutable.Set.empty[String]
- val accessedClassData = mutable.Set.empty[String]
- var optimizerHints: OptimizerHints = OptimizerHints.empty
-
- def callsMethod(ownerIdent: js.Ident, method: js.Ident): Unit =
- calledMethods += ((patchClassName(ownerIdent.name), method.name))
-
- def callsMethod(owner: Symbol, method: js.Ident): Unit =
- calledMethods += ((patchClassName(encodeClassFullName(owner)), method.name))
-
- def callsMethodStatic(ownerIdent: js.Ident, method: js.Ident): Unit =
- calledMethodsStatic += ((patchClassName(ownerIdent.name), method.name))
-
- def instantiatesClass(classSym: Symbol): Unit =
- instantiatedClasses += patchClassName(encodeClassFullName(classSym))
-
- def accessesModule(moduleClassSym: Symbol): Unit =
- accessedModules += patchModuleName(encodeModuleFullName(moduleClassSym))
-
- def accessesClassData(refType: jstpe.ReferenceType): Unit = {
- val className = refType match {
- case jstpe.ClassType(name) => name
- case jstpe.ArrayType(base, _) => base
- }
- if (!AlwaysPresentClassData.contains(className))
- accessedClassData += className
- }
-
- def createsAnonFunction(funInfo: ClassInfoBuilder): Unit = {
- for (methodInfo <- funInfo.methodInfos) {
- calledMethods ++= methodInfo.calledMethods
- calledMethodsStatic ++= methodInfo.calledMethodsStatic
- instantiatedClasses ++= methodInfo.instantiatedClasses
- accessedModules ++= methodInfo.accessedModules
- accessedClassData ++= methodInfo.accessedClassData
- }
- }
-
- private def patchClassName(name: String): String = name match {
- case "jl_String$" => "sjsr_RuntimeString$"
- case _ => name
- }
-
- private def patchModuleName(name: String): String = name match {
- case "jl_String" => "sjsr_RuntimeString"
- case _ => name
- }
-
- def result(): MethodInfo = {
- MethodInfo(
- encodedName,
- isAbstract,
- isExported,
- calledMethods.toList.groupBy(_._1).mapValues(_.map(_._2)),
- calledMethodsStatic.toList.groupBy(_._1).mapValues(_.map(_._2)),
- instantiatedClasses.toList,
- accessedModules.result.toList,
- accessedClassData.result.toList,
- optimizerHints
- )
- }
- }
-
- private def classNameOf(sym: Symbol): String =
- if (needsModuleClassSuffix(sym)) sym.fullName + nme.MODULE_SUFFIX_STRING
- else sym.fullName
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/Compat210Component.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/Compat210Component.scala
deleted file mode 100644
index f357337..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/Compat210Component.scala
+++ /dev/null
@@ -1,108 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Sébastien Doeraene
- */
-
-package scala.scalajs.compiler
-
-import scala.tools.nsc._
-
-/** Hacks to have our source code compatible with 2.10 and 2.11.
- * It exposes 2.11 API in a 2.10 compiler.
- *
- * @author Sébastien Doeraene
- */
-trait Compat210Component {
-
- val global: Global
-
- import global._
-
- // unexpandedName replaces originalName
-
- implicit final class SymbolCompat(self: Symbol) {
- def unexpandedName: Name = self.originalName
- def originalName: Name = sys.error("infinite loop in Compat")
-
- def isLocalToBlock: Boolean = self.isLocal
- }
-
- // enteringPhase/exitingPhase replace beforePhase/afterPhase
-
- @inline final def enteringPhase[T](ph: Phase)(op: => T): T = {
- global.enteringPhase(ph)(op)
- }
-
- @inline final def exitingPhase[T](ph: Phase)(op: => T): T = {
- global.exitingPhase(ph)(op)
- }
-
- private implicit final class GlobalCompat(
- self: Compat210Component.this.global.type) {
-
- def enteringPhase[T](ph: Phase)(op: => T): T = self.beforePhase(ph)(op)
- def beforePhase[T](ph: Phase)(op: => T): T = sys.error("infinite loop in Compat")
-
- def exitingPhase[T](ph: Phase)(op: => T): T = self.afterPhase(ph)(op)
- def afterPhase[T](ph: Phase)(op: => T): T = sys.error("infinite loop in Compat")
- }
-
- // ErasedValueType has a different encoding
-
- implicit final class ErasedValueTypeCompat(self: global.ErasedValueType) {
- def valueClazz: Symbol = self.original.typeSymbol
- def erasedUnderlying: Type =
- enteringPhase(currentRun.erasurePhase)(
- erasure.erasedValueClassArg(self.original))
- def original: TypeRef = sys.error("infinite loop in Compat")
- }
-
- // repeatedToSingle
-
- @inline final def repeatedToSingle(t: Type) =
- global.definitions.repeatedToSingle(t)
-
- private implicit final class DefinitionsCompat(
- self: Compat210Component.this.global.definitions.type) {
-
- def repeatedToSingle(t: Type) = t match {
- case TypeRef(_, self.RepeatedParamClass, arg :: Nil) => arg
- case _ => t
- }
-
- }
-
- // run.runDefinitions bundles methods and state related to the run
- // that were previously in definitions itself
-
- implicit final class RunCompat(self: Run) {
- val runDefinitions: Compat210Component.this.global.definitions.type =
- global.definitions
- }
-
- // Mode.FUNmode replaces analyzer.FUNmode
-
- object Mode {
- import Compat210Component.AnalyzerCompat
- // No type ascription! Type is different in 2.10 / 2.11
- val FUNmode = analyzer.FUNmode
- }
-}
-
-object Compat210Component {
- private object LowPriorityMode {
- object Mode {
- def FUNmode = sys.error("infinite loop in Compat")
- }
- }
-
- private implicit final class AnalyzerCompat(self: scala.tools.nsc.typechecker.Analyzer) {
- def FUNmode = {
- import Compat210Component.LowPriorityMode._
- {
- import scala.reflect.internal._
- Mode.FUNmode
- }
- }
- }
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala
deleted file mode 100644
index f9885a0..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSCode.scala
+++ /dev/null
@@ -1,3911 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Sébastien Doeraene
- */
-
-package scala.scalajs.compiler
-
-import scala.language.implicitConversions
-
-import scala.annotation.switch
-
-import scala.collection.mutable
-import scala.collection.mutable.ListBuffer
-
-import scala.tools.nsc._
-
-import scala.annotation.tailrec
-
-import scala.scalajs.ir
-import ir.{Trees => js, Types => jstpe, ClassKind, Hashers}
-
-import util.ScopedVar
-import ScopedVar.withScopedVars
-
-/** Generate JavaScript code and output it to disk
- *
- * @author Sébastien Doeraene
- */
-abstract class GenJSCode extends plugins.PluginComponent
- with TypeKinds
- with JSEncoding
- with GenJSExports
- with ClassInfos
- with GenJSFiles
- with Compat210Component {
-
- val jsAddons: JSGlobalAddons {
- val global: GenJSCode.this.global.type
- }
-
- val scalaJSOpts: ScalaJSOptions
-
- import global._
- import jsAddons._
- import rootMirror._
- import definitions._
- import jsDefinitions._
- import JSTreeExtractors._
-
- import treeInfo.hasSynthCaseSymbol
-
- import platform.isMaybeBoxed
-
- val phaseName = "jscode"
-
- /** testing: this will be called when ASTs are generated */
- def generatedJSAST(clDefs: List[js.Tree]): Unit
-
- /** Implicit conversion from nsc Position to ir.Position. */
- implicit def pos2irPos(pos: Position): ir.Position = {
- if (pos == NoPosition) ir.Position.NoPosition
- else {
- val source = pos2irPosCache.toIRSource(pos.source)
- // nsc positions are 1-based but IR positions are 0-based
- ir.Position(source, pos.line-1, pos.column-1)
- }
- }
-
- private[this] object pos2irPosCache {
- import scala.reflect.internal.util._
-
- private[this] var lastNscSource: SourceFile = null
- private[this] var lastIRSource: ir.Position.SourceFile = null
-
- def toIRSource(nscSource: SourceFile): ir.Position.SourceFile = {
- if (nscSource != lastNscSource) {
- lastIRSource = convert(nscSource)
- lastNscSource = nscSource
- }
- lastIRSource
- }
-
- private[this] def convert(nscSource: SourceFile): ir.Position.SourceFile = {
- nscSource.file.file match {
- case null =>
- new java.net.URI(
- "virtualfile", // Pseudo-Scheme
- nscSource.file.path, // Scheme specific part
- null // Fragment
- )
- case file =>
- val srcURI = file.toURI
- def matches(pat: java.net.URI) = pat.relativize(srcURI) != srcURI
-
- scalaJSOpts.sourceURIMaps.collectFirst {
- case ScalaJSOptions.URIMap(from, to) if matches(from) =>
- val relURI = from.relativize(srcURI)
- to.fold(relURI)(_.resolve(relURI))
- } getOrElse srcURI
- }
- }
-
- def clear(): Unit = {
- lastNscSource = null
- lastIRSource = null
- }
- }
-
- /** Materialize implicitly an ir.Position from an implicit nsc Position. */
- implicit def implicitPos2irPos(implicit pos: Position): ir.Position = pos
-
- override def newPhase(p: Phase) = new JSCodePhase(p)
-
- private object jsnme {
- val arg_outer = newTermName("arg$outer")
- val newString = newTermName("newString")
- }
-
- class JSCodePhase(prev: Phase) extends StdPhase(prev) with JSExportsPhase {
-
- override def name = phaseName
- override def description = "Generate JavaScript code from ASTs"
- override def erasedTypes = true
-
- // Some state --------------------------------------------------------------
-
- val currentClassSym = new ScopedVar[Symbol]
- val currentClassInfoBuilder = new ScopedVar[ClassInfoBuilder]
- val currentMethodSym = new ScopedVar[Symbol]
- val currentMethodInfoBuilder = new ScopedVar[MethodInfoBuilder]
- val methodTailJumpThisSym = new ScopedVar[Symbol](NoSymbol)
- val fakeTailJumpParamRepl = new ScopedVar[(Symbol, Symbol)]((NoSymbol, NoSymbol))
- val enclosingLabelDefParams = new ScopedVar(Map.empty[Symbol, List[Symbol]])
- val mutableLocalVars = new ScopedVar[mutable.Set[Symbol]]
- val mutatedLocalVars = new ScopedVar[mutable.Set[Symbol]]
- val paramAccessorLocals = new ScopedVar(Map.empty[Symbol, js.ParamDef])
-
- var isModuleInitialized: Boolean = false // see genApply for super calls
-
- def currentClassType = encodeClassType(currentClassSym)
-
- val tryingToGenMethodAsJSFunction = new ScopedVar[Boolean](false)
- class CancelGenMethodAsJSFunction(message: String)
- extends Throwable(message) with scala.util.control.ControlThrowable
-
- // Rewriting of anonymous function classes ---------------------------------
-
- private val translatedAnonFunctions =
- mutable.Map.empty[Symbol,
- (/*ctor args:*/ List[js.Tree] => /*instance:*/ js.Tree, ClassInfoBuilder)]
- private val instantiatedAnonFunctions =
- mutable.Set.empty[Symbol]
- private val undefinedDefaultParams =
- mutable.Set.empty[Symbol]
-
- // Top-level apply ---------------------------------------------------------
-
- override def run() {
- scalaPrimitives.init()
- jsPrimitives.init()
- super.run()
- }
-
- /** Generates the Scala.js IR for a compilation unit
- * This method iterates over all the class and interface definitions
- * found in the compilation unit and emits their IR (.sjsir).
- *
- * Some classes are never actually emitted:
- * - Classes representing primitive types
- * - The scala.Array class
- * - Implementation classes for raw JS traits
- *
- * Some classes representing anonymous functions are not actually emitted.
- * Instead, a temporary representation of their `apply` method is built
- * and recorded, so that it can be inlined as a JavaScript anonymous
- * function in the method that instantiates it.
- *
- * Other ClassDefs are emitted according to their nature:
- * * Raw JS type (<: js.Any) -> `genRawJSClassData()`
- * * Interface -> `genInterface()`
- * * Implementation class -> `genImplClass()`
- * * Normal class -> `genClass()`
- */
- override def apply(cunit: CompilationUnit) {
- try {
- val generatedClasses = ListBuffer.empty[(Symbol, js.ClassDef, ClassInfoBuilder)]
-
- def collectClassDefs(tree: Tree): List[ClassDef] = {
- tree match {
- case EmptyTree => Nil
- case PackageDef(_, stats) => stats flatMap collectClassDefs
- case cd: ClassDef => cd :: Nil
- }
- }
- val allClassDefs = collectClassDefs(cunit.body)
-
- /* First gen and record lambdas for js.FunctionN and js.ThisFunctionN.
- * Since they are SAMs, there cannot be dependencies within this set,
- * and hence we are sure we can record them before they are used,
- * which is critical for these.
- */
- val nonRawJSFunctionDefs = allClassDefs filterNot { cd =>
- if (isRawJSFunctionDef(cd.symbol)) {
- genAndRecordRawJSFunctionClass(cd)
- true
- } else {
- false
- }
- }
-
- /* Then try to gen and record lambdas for scala.FunctionN.
- * These may fail, and sometimes because of dependencies. Since there
- * appears to be more forward dependencies than backward dependencies
- * (at least for non-nested lambdas, which we cannot translate anyway),
- * we process class defs in reverse order here.
- */
- val fullClassDefs = (nonRawJSFunctionDefs.reverse filterNot { cd =>
- cd.symbol.isAnonymousFunction && tryGenAndRecordAnonFunctionClass(cd)
- }).reverse
-
- /* Finally, we emit true code for the remaining class defs. */
- for (cd <- fullClassDefs) {
- val sym = cd.symbol
- implicit val pos = sym.pos
-
- /* Do not actually emit code for primitive types nor scala.Array. */
- val isPrimitive =
- isPrimitiveValueClass(sym) || (sym == ArrayClass)
-
- /* Similarly, do not emit code for impl classes of raw JS traits. */
- val isRawJSImplClass =
- sym.isImplClass && isRawJSType(
- sym.owner.info.decl(sym.name.dropRight(nme.IMPL_CLASS_SUFFIX.length)).tpe)
-
- if (!isPrimitive && !isRawJSImplClass) {
- withScopedVars(
- currentClassInfoBuilder := new ClassInfoBuilder(sym.asClass),
- currentClassSym := sym
- ) {
- val tree = if (isRawJSType(sym.tpe)) {
- assert(!isRawJSFunctionDef(sym),
- s"Raw JS function def should have been recorded: $cd")
- genRawJSClassData(cd)
- } else if (sym.isInterface) {
- genInterface(cd)
- } else if (sym.isImplClass) {
- genImplClass(cd)
- } else {
- genClass(cd)
- }
- generatedClasses += ((sym, tree, currentClassInfoBuilder.get))
- }
- }
- }
-
- val clDefs = generatedClasses.map(_._2).toList
- generatedJSAST(clDefs)
-
- for ((sym, tree, infoBuilder) <- generatedClasses) {
- genIRFile(cunit, sym, tree, infoBuilder.result())
- }
- } finally {
- translatedAnonFunctions.clear()
- instantiatedAnonFunctions.clear()
- undefinedDefaultParams.clear()
- pos2irPosCache.clear()
- }
- }
-
- // Generate a class --------------------------------------------------------
-
- /** Gen the IR ClassDef for a class definition (maybe a module class).
- */
- def genClass(cd: ClassDef): js.ClassDef = {
- val ClassDef(mods, name, _, impl) = cd
- val sym = cd.symbol
- implicit val pos = sym.pos
-
- assert(!sym.isInterface && !sym.isImplClass,
- "genClass() must be called only for normal classes: "+sym)
- assert(sym.superClass != NoSymbol, sym)
-
- val classIdent = encodeClassFullNameIdent(sym)
- val isHijacked = isHijackedBoxedClass(sym)
-
- // Optimizer hints
-
- def isStdLibClassWithAdHocInlineAnnot(sym: Symbol): Boolean = {
- val fullName = sym.fullName
- (fullName.startsWith("scala.Tuple") && !fullName.endsWith("$")) ||
- (fullName.startsWith("scala.collection.mutable.ArrayOps$of"))
- }
-
- if (sym.hasAnnotation(InlineAnnotationClass) ||
- (sym.isAnonymousFunction && !sym.isSubClass(PartialFunctionClass)) ||
- isStdLibClassWithAdHocInlineAnnot(sym))
- currentClassInfoBuilder.optimizerHints =
- currentClassInfoBuilder.optimizerHints.copy(hasInlineAnnot = true)
-
- // Generate members (constructor + methods)
-
- val generatedMembers = new ListBuffer[js.Tree]
- val exportedSymbols = new ListBuffer[Symbol]
-
- if (!isHijacked)
- generatedMembers ++= genClassFields(cd)
-
- def gen(tree: Tree): Unit = {
- tree match {
- case EmptyTree => ()
- case Template(_, _, body) => body foreach gen
-
- case ValDef(mods, name, tpt, rhs) =>
- () // fields are added via genClassFields()
-
- case dd: DefDef =>
- val sym = dd.symbol
-
- val isExport = jsInterop.isExport(sym)
- val isNamedExport = isExport && sym.annotations.exists(
- _.symbol == JSExportNamedAnnotation)
-
- if (isNamedExport)
- generatedMembers += genNamedExporterDef(dd)
- else
- generatedMembers ++= genMethod(dd)
-
- if (isExport) {
- // We add symbols that we have to export here. This way we also
- // get inherited stuff that is implemented in this class.
- exportedSymbols += sym
- }
-
- case _ => abort("Illegal tree in gen of genClass(): " + tree)
- }
- }
-
- gen(impl)
-
- // Create method info builder for exported stuff
- val exports = withScopedVars(
- currentMethodInfoBuilder := currentClassInfoBuilder.addMethod(
- dceExportName + classIdent.name, isExported = true)
- ) {
- // Generate the exported members
- val memberExports = genMemberExports(sym, exportedSymbols.toList)
-
- // Generate exported constructors or accessors
- val exportedConstructorsOrAccessors =
- if (isStaticModule(sym)) genModuleAccessorExports(sym)
- else genConstructorExports(sym)
- if (exportedConstructorsOrAccessors.nonEmpty)
- currentClassInfoBuilder.isExported = true
-
- memberExports ++ exportedConstructorsOrAccessors
- }
-
- // Generate the reflective call proxies (where required)
- val reflProxies =
- if (isHijacked) Nil
- else genReflCallProxies(sym)
-
- // Hashed definitions of the class
- val hashedDefs =
- Hashers.hashDefs(generatedMembers.toList ++ exports ++ reflProxies)
-
- // The complete class definition
- val kind =
- if (sym.isModuleClass) ClassKind.ModuleClass
- else if (isHijacked) ClassKind.HijackedClass
- else ClassKind.Class
-
- val classDefinition = js.ClassDef(
- classIdent,
- kind,
- Some(encodeClassFullNameIdent(sym.superClass)),
- sym.ancestors.map(encodeClassFullNameIdent),
- hashedDefs)
-
- classDefinition
- }
-
- // Generate the class data of a raw JS class -------------------------------
-
- /** Gen the IR ClassDef for a raw JS class or trait.
- */
- def genRawJSClassData(cd: ClassDef): js.ClassDef = {
- val sym = cd.symbol
- implicit val pos = sym.pos
-
- // Check that RawJS type is not exported
- for (exp <- jsInterop.exportsOf(sym))
- reporter.error(exp.pos, "You may not export a class extending js.Any")
-
- val classIdent = encodeClassFullNameIdent(sym)
- js.ClassDef(classIdent, ClassKind.RawJSType, None, Nil, Nil)
- }
-
- // Generate an interface ---------------------------------------------------
-
- /** Gen the IR ClassDef for an interface definition.
- */
- def genInterface(cd: ClassDef): js.ClassDef = {
- val sym = cd.symbol
- implicit val pos = sym.pos
-
- val classIdent = encodeClassFullNameIdent(sym)
-
- // fill in class info builder
- def gen(tree: Tree) {
- tree match {
- case EmptyTree => ()
- case Template(_, _, body) => body foreach gen
- case dd: DefDef =>
- currentClassInfoBuilder.addMethod(
- encodeMethodName(dd.symbol), isAbstract = true)
- case _ => abort("Illegal tree in gen of genInterface(): " + tree)
- }
- }
- gen(cd.impl)
-
- // Check that interface/trait is not exported
- for (exp <- jsInterop.exportsOf(sym))
- reporter.error(exp.pos, "You may not export a trait")
-
- js.ClassDef(classIdent, ClassKind.Interface, None,
- sym.ancestors.map(encodeClassFullNameIdent), Nil)
- }
-
- // Generate an implementation class of a trait -----------------------------
-
- /** Gen the IR ClassDef for an implementation class (of a trait).
- */
- def genImplClass(cd: ClassDef): js.ClassDef = {
- val ClassDef(mods, name, _, impl) = cd
- val sym = cd.symbol
- implicit val pos = sym.pos
-
- def gen(tree: Tree): List[js.MethodDef] = {
- tree match {
- case EmptyTree => Nil
- case Template(_, _, body) => body.flatMap(gen)
-
- case dd: DefDef =>
- val m = genMethod(dd)
- m.toList
-
- case _ => abort("Illegal tree in gen of genImplClass(): " + tree)
- }
- }
- val generatedMethods = gen(impl)
-
- js.ClassDef(encodeClassFullNameIdent(sym), ClassKind.TraitImpl,
- None, Nil, generatedMethods)
- }
-
- // Generate the fields of a class ------------------------------------------
-
- /** Gen definitions for the fields of a class.
- * The fields are initialized with the zero of their types.
- */
- def genClassFields(cd: ClassDef): List[js.VarDef] = withScopedVars(
- currentMethodInfoBuilder :=
- currentClassInfoBuilder.addMethod("__init__")
- ) {
- // Non-method term members are fields, except for module members.
- (for {
- f <- currentClassSym.info.decls
- if !f.isMethod && f.isTerm && !f.isModule
- } yield {
- implicit val pos = f.pos
- js.VarDef(encodeFieldSym(f), toIRType(f.tpe),
- mutable = f.isMutable, genZeroOf(f.tpe))
- }).toList
- }
-
- // Generate a method -------------------------------------------------------
-
- def genMethod(dd: DefDef): Option[js.MethodDef] = withNewLocalNameScope {
- genMethodWithInfoBuilder(dd).map(_._1)
- }
-
- /** Gen JS code for a method definition in a class or in an impl class.
- * On the JS side, method names are mangled to encode the full signature
- * of the Scala method, as described in `JSEncoding`, to support
- * overloading.
- *
- * Some methods are not emitted at all:
- * * Primitives, since they are never actually called
- * * Abstract methods
- * * Constructors of hijacked classes
- * * Trivial constructors, which only call their super constructor, with
- * the same signature, and the same arguments. The JVM needs these
- * constructors, but not JavaScript. Since there are lots of them, we
- * take the trouble of recognizing and removing them.
- *
- * Constructors are emitted by generating their body as a statement, then
- * return `this`.
- *
- * Other (normal) methods are emitted with `genMethodBody()`.
- */
- def genMethodWithInfoBuilder(
- dd: DefDef): Option[(js.MethodDef, MethodInfoBuilder)] = {
-
- implicit val pos = dd.pos
- val DefDef(mods, name, _, vparamss, _, rhs) = dd
- val sym = dd.symbol
-
- isModuleInitialized = false
-
- val result = withScopedVars(
- currentMethodSym := sym,
- methodTailJumpThisSym := NoSymbol,
- fakeTailJumpParamRepl := (NoSymbol, NoSymbol),
- enclosingLabelDefParams := Map.empty
- ) {
- assert(vparamss.isEmpty || vparamss.tail.isEmpty,
- "Malformed parameter list: " + vparamss)
- val params = if (vparamss.isEmpty) Nil else vparamss.head map (_.symbol)
-
- assert(!sym.owner.isInterface,
- "genMethod() must not be called for methods in interfaces: "+sym)
-
- val methodIdent = encodeMethodSym(sym)
-
- def createInfoBuilder(isAbstract: Boolean = false) = {
- currentClassInfoBuilder.addMethod(methodIdent.name,
- isAbstract = isAbstract,
- isExported = sym.isClassConstructor &&
- jsInterop.exportsOf(sym).nonEmpty)
- }
-
- if (scalaPrimitives.isPrimitive(sym)) {
- None
- } else if (sym.isDeferred) {
- createInfoBuilder(isAbstract = true)
- None
- } else if (isRawJSCtorDefaultParam(sym)) {
- None
- } else if (isTrivialConstructor(sym, params, rhs)) {
- createInfoBuilder().callsMethod(sym.owner.superClass, methodIdent)
- None
- } else if (sym.isClassConstructor && isHijackedBoxedClass(sym.owner)) {
- None
- } else {
- withScopedVars(
- currentMethodInfoBuilder := createInfoBuilder(),
- mutableLocalVars := mutable.Set.empty,
- mutatedLocalVars := mutable.Set.empty
- ) {
- def shouldMarkInline = {
- sym.hasAnnotation(InlineAnnotationClass) ||
- sym.name.startsWith(nme.ANON_FUN_NAME)
- }
- currentMethodInfoBuilder.optimizerHints =
- currentMethodInfoBuilder.optimizerHints.copy(
- isAccessor = sym.isAccessor,
- hasInlineAnnot = shouldMarkInline)
-
- val methodDef = {
- if (sym.isClassConstructor) {
- val jsParams = for (param <- params) yield {
- implicit val pos = param.pos
- js.ParamDef(encodeLocalSym(param), toIRType(param.tpe),
- mutable = false)
- }
- js.MethodDef(methodIdent, jsParams, currentClassType,
- js.Block(genStat(rhs), genThis()))(None)
- } else {
- val resultIRType = toIRType(sym.tpe.resultType)
- genMethodDef(methodIdent, params, resultIRType, rhs)
- }
- }
-
- val methodDefWithoutUselessVars = {
- val unmutatedMutableLocalVars =
- (mutableLocalVars -- mutatedLocalVars).toList
- val mutatedImmutableLocalVals =
- (mutatedLocalVars -- mutableLocalVars).toList
- if (unmutatedMutableLocalVars.isEmpty &&
- mutatedImmutableLocalVals.isEmpty) {
- // OK, we're good (common case)
- methodDef
- } else {
- val patches = (
- unmutatedMutableLocalVars.map(encodeLocalSym(_).name -> false) :::
- mutatedImmutableLocalVals.map(encodeLocalSym(_).name -> true)
- ).toMap
- patchMutableFlagOfLocals(methodDef, patches)
- }
- }
-
- Some((methodDefWithoutUselessVars, currentMethodInfoBuilder.get))
- }
- }
- }
-
- result
- }
-
- private def isTrivialConstructor(sym: Symbol, params: List[Symbol],
- rhs: Tree): Boolean = {
- if (!sym.isClassConstructor) {
- false
- } else {
- rhs match {
- // Shape of a constructor that only calls super
- case Block(List(Apply(fun @ Select(_:Super, _), args)), Literal(_)) =>
- val callee = fun.symbol
- implicit val dummyPos = NoPosition
-
- // Does the callee have the same signature as sym
- if (encodeMethodSym(sym) == encodeMethodSym(callee)) {
- // Test whether args are trivial forwarders
- assert(args.size == params.size, "Argument count mismatch")
- params.zip(args) forall { case (param, arg) =>
- arg.symbol == param
- }
- } else {
- false
- }
-
- case _ => false
- }
- }
- }
-
- /** Patches the mutable flags of selected locals in a [[js.MethodDef]].
- *
- * @param patches Map from local name to new value of the mutable flags.
- * For locals not in the map, the flag is untouched.
- */
- private def patchMutableFlagOfLocals(methodDef: js.MethodDef,
- patches: Map[String, Boolean]): js.MethodDef = {
-
- def newMutable(name: String, oldMutable: Boolean): Boolean =
- patches.getOrElse(name, oldMutable)
-
- val js.MethodDef(methodName, params, resultType, body) = methodDef
- val newParams = for {
- p @ js.ParamDef(name, ptpe, mutable) <- params
- } yield {
- js.ParamDef(name, ptpe, newMutable(name.name, mutable))(p.pos)
- }
- val transformer = new ir.Transformers.Transformer {
- override def transform(tree: js.Tree, isStat: Boolean): js.Tree = tree match {
- case js.VarDef(name, vtpe, mutable, rhs) =>
- assert(isStat)
- super.transform(js.VarDef(
- name, vtpe, newMutable(name.name, mutable), rhs)(tree.pos), isStat)
- case js.VarRef(name, mutable) =>
- js.VarRef(name, newMutable(name.name, mutable))(tree.tpe)(tree.pos)
- case js.Closure(captureParams, params, body, captureValues) =>
- js.Closure(captureParams, params, body,
- captureValues.map(transformExpr))(tree.pos)
- case _ =>
- super.transform(tree, isStat)
- }
- }
- val newBody =
- transformer.transform(body, isStat = resultType == jstpe.NoType)
- js.MethodDef(methodName, newParams, resultType, newBody)(None)(methodDef.pos)
- }
-
- /**
- * Generates reflective proxy methods for methods in sym
- *
- * Reflective calls don't depend on the return type, so it's hard to
- * generate calls without using runtime reflection to list the methods. We
- * generate a method to be used for reflective calls (without return
- * type in the name).
- *
- * There are cases where non-trivial overloads cause ambiguous situations:
- *
- * {{{
- * object A {
- * def foo(x: Option[Int]): String
- * def foo(x: Option[String]): Int
- * }
- * }}}
- *
- * This is completely legal code, but due to the same erased parameter
- * type of the {{{foo}}} overloads, they cannot be disambiguated in a
- * reflective call, as the exact return type is unknown at the call site.
- *
- * Cases like the upper currently fail on the JVM backend at runtime. The
- * Scala.js backend uses the following rules for selection (which will
- * also cause runtime failures):
- *
- * - If a proxy with the same signature (method name and parameters)
- * exists in the superclass, no proxy is generated (proxy is inherited)
- * - If no proxy exists in the superclass, a proxy is generated for the
- * first method with matching signatures.
- */
- def genReflCallProxies(sym: Symbol): List[js.MethodDef] = {
- import scala.reflect.internal.Flags
-
- // Flags of members we do not want to consider for reflective call proxys
- val excludedFlags = (
- Flags.BRIDGE |
- Flags.PRIVATE |
- Flags.MACRO
- )
-
- /** Check if two method symbols conform in name and parameter types */
- def weakMatch(s1: Symbol)(s2: Symbol) = {
- val p1 = s1.tpe.params
- val p2 = s2.tpe.params
- s1 == s2 || // Shortcut
- s1.name == s2.name &&
- p1.size == p2.size &&
- (p1 zip p2).forall { case (s1,s2) =>
- s1.tpe =:= s2.tpe
- }
- }
-
- /** Check if the symbol's owner's superclass has a matching member (and
- * therefore an existing proxy).
- */
- def superHasProxy(s: Symbol) = {
- val alts = sym.superClass.tpe.findMember(
- name = s.name,
- excludedFlags = excludedFlags,
- requiredFlags = Flags.METHOD,
- stableOnly = false).alternatives
- alts.exists(weakMatch(s) _)
- }
-
- // Query candidate methods
- val methods = sym.tpe.findMembers(
- excludedFlags = excludedFlags,
- requiredFlags = Flags.METHOD)
-
- val candidates = methods filterNot { s =>
- s.isConstructor ||
- superHasProxy(s) ||
- jsInterop.isExport(s)
- }
-
- val proxies = candidates filter {
- c => candidates.find(weakMatch(c) _).get == c
- }
-
- proxies.map(genReflCallProxy _).toList
- }
-
- /** actually generates reflective call proxy for the given method symbol */
- private def genReflCallProxy(sym: Symbol): js.MethodDef = {
- implicit val pos = sym.pos
-
- val proxyIdent = encodeMethodSym(sym, reflProxy = true)
-
- withNewLocalNameScope {
- withScopedVars(
- currentMethodInfoBuilder :=
- currentClassInfoBuilder.addMethod(proxyIdent.name)
- ) {
- val jsParams = for (param <- sym.tpe.params) yield {
- implicit val pos = param.pos
- js.ParamDef(encodeLocalSym(param), toIRType(param.tpe),
- mutable = false)
- }
-
- val call = genApplyMethod(genThis(), sym.owner, sym,
- jsParams.map(_.ref))
- val body = ensureBoxed(call,
- enteringPhase(currentRun.posterasurePhase)(sym.tpe.resultType))
-
- js.MethodDef(proxyIdent, jsParams, jstpe.AnyType, body)(None)
- }
- }
- }
-
- /** Generates the MethodDef of a (non-constructor) method
- *
- * Most normal methods are emitted straightforwardly. If the result
- * type is Unit, then the body is emitted as a statement. Otherwise, it is
- * emitted as an expression.
- *
- * The additional complexity of this method handles the transformation of
- * a peculiarity of recursive tail calls: the local ValDef that replaces
- * `this`.
- */
- def genMethodDef(methodIdent: js.Ident, paramsSyms: List[Symbol],
- resultIRType: jstpe.Type, tree: Tree): js.MethodDef = {
- implicit val pos = tree.pos
-
- val jsParams = for (param <- paramsSyms) yield {
- implicit val pos = param.pos
- js.ParamDef(encodeLocalSym(param), toIRType(param.tpe), mutable = false)
- }
-
- val bodyIsStat = resultIRType == jstpe.NoType
-
- val body = tree match {
- case Block(
- (thisDef @ ValDef(_, nme.THIS, _, initialThis)) :: otherStats,
- rhs) =>
- // This method has tail jumps
- withScopedVars(
- (initialThis match {
- case This(_) =>
- Seq(methodTailJumpThisSym := thisDef.symbol,
- fakeTailJumpParamRepl := (NoSymbol, NoSymbol))
- case Ident(_) =>
- Seq(methodTailJumpThisSym := NoSymbol,
- fakeTailJumpParamRepl := (thisDef.symbol, initialThis.symbol))
- }): _*
- ) {
- val innerBody = js.Block(otherStats.map(genStat) :+ (
- if (bodyIsStat) genStat(rhs)
- else genExpr(rhs)))
-
- if (methodTailJumpThisSym.get == NoSymbol) {
- innerBody
- } else {
- if (methodTailJumpThisSym.isMutable)
- mutableLocalVars += methodTailJumpThisSym
- js.Block(
- js.VarDef(encodeLocalSym(methodTailJumpThisSym),
- currentClassType, methodTailJumpThisSym.isMutable,
- js.This()(currentClassType)),
- innerBody)
- }
- }
-
- case _ =>
- if (bodyIsStat) genStat(tree)
- else genExpr(tree)
- }
-
- js.MethodDef(methodIdent, jsParams, resultIRType, body)(None)
- }
-
- /** Gen JS code for a tree in statement position (in the IR).
- */
- def genStat(tree: Tree): js.Tree = {
- exprToStat(genStatOrExpr(tree, isStat = true))
- }
-
- /** Turn a JavaScript expression of type Unit into a statement */
- def exprToStat(tree: js.Tree): js.Tree = {
- /* Any JavaScript expression is also a statement, but at least we get rid
- * of some pure expressions that come from our own codegen.
- */
- implicit val pos = tree.pos
- tree match {
- case js.Block(stats :+ expr) => js.Block(stats :+ exprToStat(expr))
- case _:js.Literal | js.This() => js.Skip()
- case _ => tree
- }
- }
-
- /** Gen JS code for a tree in expression position (in the IR).
- */
- def genExpr(tree: Tree): js.Tree = {
- val result = genStatOrExpr(tree, isStat = false)
- assert(result.tpe != jstpe.NoType,
- s"genExpr($tree) returned a tree with type NoType at pos ${tree.pos}")
- result
- }
-
- /** Gen JS code for a tree in statement or expression position (in the IR).
- *
- * This is the main transformation method. Each node of the Scala AST
- * is transformed into an equivalent portion of the JS AST.
- */
- def genStatOrExpr(tree: Tree, isStat: Boolean): js.Tree = {
- implicit val pos = tree.pos
-
- tree match {
- /** LabelDefs (for while and do..while loops) */
- case lblDf: LabelDef =>
- genLabelDef(lblDf)
-
- /** Local val or var declaration */
- case ValDef(_, name, _, rhs) =>
- /* Must have been eliminated by the tail call transform performed
- * by genMethodBody(). */
- assert(name != nme.THIS,
- s"ValDef(_, nme.THIS, _, _) found at ${tree.pos}")
-
- val sym = tree.symbol
- val rhsTree =
- if (rhs == EmptyTree) genZeroOf(sym.tpe)
- else genExpr(rhs)
-
- rhsTree match {
- case js.UndefinedParam() =>
- // This is an intermediate assignment for default params on a
- // js.Any. Add the symbol to the corresponding set to inform
- // the Ident resolver how to replace it and don't emit the symbol
- undefinedDefaultParams += sym
- js.Skip()
- case _ =>
- if (sym.isMutable)
- mutableLocalVars += sym
- js.VarDef(encodeLocalSym(sym),
- toIRType(sym.tpe), sym.isMutable, rhsTree)
- }
-
- case If(cond, thenp, elsep) =>
- js.If(genExpr(cond), genStatOrExpr(thenp, isStat),
- genStatOrExpr(elsep, isStat))(toIRType(tree.tpe))
-
- case Return(expr) =>
- js.Return(toIRType(expr.tpe) match {
- case jstpe.NoType => js.Block(genStat(expr), js.Undefined())
- case _ => genExpr(expr)
- })
-
- case t: Try =>
- genTry(t, isStat)
-
- case Throw(expr) =>
- val ex = genExpr(expr)
- js.Throw {
- if (isMaybeJavaScriptException(expr.tpe)) {
- genApplyMethod(
- genLoadModule(RuntimePackageModule),
- RuntimePackageModule.moduleClass,
- Runtime_unwrapJavaScriptException,
- List(ex))
- } else {
- ex
- }
- }
-
- case app: Apply =>
- genApply(app, isStat)
-
- case app: ApplyDynamic =>
- genApplyDynamic(app)
-
- case This(qual) =>
- if (tree.symbol == currentClassSym.get) {
- genThis()
- } else {
- assert(tree.symbol.isModuleClass,
- "Trying to access the this of another class: " +
- "tree.symbol = " + tree.symbol +
- ", class symbol = " + currentClassSym.get +
- " compilation unit:" + currentUnit)
- genLoadModule(tree.symbol)
- }
-
- case Select(qualifier, selector) =>
- val sym = tree.symbol
- if (sym.isModule) {
- assert(!sym.isPackageClass, "Cannot use package as value: " + tree)
- genLoadModule(sym)
- } else if (sym.isStaticMember) {
- genStaticMember(sym)
- } else if (paramAccessorLocals contains sym) {
- paramAccessorLocals(sym).ref
- } else {
- js.Select(genExpr(qualifier), encodeFieldSym(sym),
- mutable = sym.isMutable)(toIRType(sym.tpe))
- }
-
- case Ident(name) =>
- val sym = tree.symbol
- if (!sym.hasPackageFlag) {
- if (sym.isModule) {
- assert(!sym.isPackageClass, "Cannot use package as value: " + tree)
- genLoadModule(sym)
- } else if (undefinedDefaultParams contains sym) {
- // This is a default parameter whose assignment was moved to
- // a local variable. Put a literal undefined param again
- js.UndefinedParam()(toIRType(sym.tpe))
- } else {
- js.VarRef(encodeLocalSym(sym), sym.isMutable)(toIRType(sym.tpe))
- }
- } else {
- sys.error("Cannot use package as value: " + tree)
- }
-
- case Literal(value) =>
- value.tag match {
- case UnitTag =>
- js.Skip()
- case BooleanTag =>
- js.BooleanLiteral(value.booleanValue)
- case ByteTag | ShortTag | CharTag | IntTag =>
- js.IntLiteral(value.intValue)
- case LongTag =>
- js.LongLiteral(value.longValue)
- case FloatTag =>
- js.FloatLiteral(value.floatValue)
- case DoubleTag =>
- js.DoubleLiteral(value.doubleValue)
- case StringTag =>
- js.StringLiteral(value.stringValue)
- case NullTag =>
- js.Null()
- case ClazzTag =>
- genClassConstant(value.typeValue)
- case EnumTag =>
- genStaticMember(value.symbolValue)
- }
-
- case tree: Block =>
- genBlock(tree, isStat)
-
- case Typed(Super(_, _), _) =>
- genThis()
-
- case Typed(expr, _) =>
- genExpr(expr)
-
- case Assign(lhs, rhs) =>
- val sym = lhs.symbol
- if (sym.isStaticMember)
- abort(s"Assignment to static member ${sym.fullName} not supported")
- val genLhs = lhs match {
- case Select(qualifier, _) =>
- js.Select(genExpr(qualifier), encodeFieldSym(sym),
- mutable = sym.isMutable)(toIRType(sym.tpe))
- case _ =>
- mutatedLocalVars += sym
- js.VarRef(encodeLocalSym(sym), sym.isMutable)(toIRType(sym.tpe))
- }
- js.Assign(genLhs, genExpr(rhs))
-
- /** Array constructor */
- case av: ArrayValue =>
- genArrayValue(av)
-
- /** A Match reaching the backend is supposed to be optimized as a switch */
- case mtch: Match =>
- genMatch(mtch, isStat)
-
- /** Anonymous function (only with -Ydelambdafy:method) */
- case fun: Function =>
- genAnonFunction(fun)
-
- case EmptyTree =>
- js.Skip()
-
- case _ =>
- abort("Unexpected tree in genExpr: " +
- tree + "/" + tree.getClass + " at: " + tree.pos)
- }
- } // end of GenJSCode.genExpr()
-
- /** Gen JS this of the current class.
- * Normally encoded straightforwardly as a JS this.
- * But must be replaced by the tail-jump-this local variable if there
- * is one.
- */
- private def genThis()(implicit pos: Position): js.Tree = {
- if (methodTailJumpThisSym.get != NoSymbol) {
- js.VarRef(
- encodeLocalSym(methodTailJumpThisSym),
- methodTailJumpThisSym.isMutable)(currentClassType)
- } else {
- if (tryingToGenMethodAsJSFunction)
- throw new CancelGenMethodAsJSFunction(
- "Trying to generate `this` inside the body")
- js.This()(currentClassType)
- }
- }
-
- /** Gen JS code for LabelDef
- * The only LabelDefs that can reach here are the desugaring of
- * while and do..while loops. All other LabelDefs (for tail calls or
- * matches) are caught upstream and transformed in ad hoc ways.
- *
- * So here we recognize all the possible forms of trees that can result
- * of while or do..while loops, and we reconstruct the loop for emission
- * to JS.
- */
- def genLabelDef(tree: LabelDef): js.Tree = {
- implicit val pos = tree.pos
- val sym = tree.symbol
-
- tree match {
- // while (cond) { body }
- case LabelDef(lname, Nil,
- If(cond,
- Block(bodyStats, Apply(target @ Ident(lname2), Nil)),
- Literal(_))) if (target.symbol == sym) =>
- js.While(genExpr(cond), js.Block(bodyStats map genStat))
-
- // while (cond) { body }; result
- case LabelDef(lname, Nil,
- Block(List(
- If(cond,
- Block(bodyStats, Apply(target @ Ident(lname2), Nil)),
- Literal(_))),
- result)) if (target.symbol == sym) =>
- js.Block(
- js.While(genExpr(cond), js.Block(bodyStats map genStat)),
- genExpr(result))
-
- // while (true) { body }
- case LabelDef(lname, Nil,
- Block(bodyStats,
- Apply(target @ Ident(lname2), Nil))) if (target.symbol == sym) =>
- js.While(js.BooleanLiteral(true), js.Block(bodyStats map genStat))
-
- // while (false) { body }
- case LabelDef(lname, Nil, Literal(Constant(()))) =>
- js.Skip()
-
- // do { body } while (cond)
- case LabelDef(lname, Nil,
- Block(bodyStats,
- If(cond,
- Apply(target @ Ident(lname2), Nil),
- Literal(_)))) if (target.symbol == sym) =>
- js.DoWhile(js.Block(bodyStats map genStat), genExpr(cond))
-
- // do { body } while (cond); result
- case LabelDef(lname, Nil,
- Block(
- bodyStats :+
- If(cond,
- Apply(target @ Ident(lname2), Nil),
- Literal(_)),
- result)) if (target.symbol == sym) =>
- js.Block(
- js.DoWhile(js.Block(bodyStats map genStat), genExpr(cond)),
- genExpr(result))
-
- /* Arbitrary other label - we can jump to it from inside it.
- * This is typically for the label-defs implementing tail-calls.
- * It can also handle other weird LabelDefs generated by some compiler
- * plugins (see for example #1148).
- */
- case LabelDef(labelName, labelParams, rhs) =>
- val labelParamSyms = labelParams.map(_.symbol) map {
- s => if (s == fakeTailJumpParamRepl._1) fakeTailJumpParamRepl._2 else s
- }
-
- withScopedVars(
- enclosingLabelDefParams :=
- enclosingLabelDefParams.get + (tree.symbol -> labelParamSyms)
- ) {
- val bodyType = toIRType(tree.tpe)
- val labelIdent = encodeLabelSym(tree.symbol)
- val blockLabelIdent = freshLocalIdent()
-
- js.Labeled(blockLabelIdent, bodyType, {
- js.While(js.BooleanLiteral(true), {
- if (bodyType == jstpe.NoType)
- js.Block(genStat(rhs), js.Return(js.Undefined(), Some(blockLabelIdent)))
- else
- js.Return(genExpr(rhs), Some(blockLabelIdent))
- }, Some(labelIdent))
- })
- }
- }
- }
-
- /** Gen JS code for a try..catch or try..finally block
- *
- * try..finally blocks are compiled straightforwardly to try..finally
- * blocks of JS.
- *
- * try..catch blocks are a bit more subtle, as JS does not have
- * type-based selection of exceptions to catch. We thus encode explicitly
- * the type tests, like in:
- *
- * try { ... }
- * catch (e) {
- * if (e.isInstanceOf[IOException]) { ... }
- * else if (e.isInstanceOf[Exception]) { ... }
- * else {
- * throw e; // default, re-throw
- * }
- * }
- */
- def genTry(tree: Try, isStat: Boolean): js.Tree = {
- implicit val pos = tree.pos
- val Try(block, catches, finalizer) = tree
-
- val blockAST = genStatOrExpr(block, isStat)
-
- val exceptIdent = freshLocalIdent("e")
- val origExceptVar = js.VarRef(exceptIdent, mutable = false)(jstpe.AnyType)
-
- val resultType = toIRType(tree.tpe)
-
- val handlerAST = {
- if (catches.isEmpty) {
- js.EmptyTree
- } else {
- val mightCatchJavaScriptException = catches.exists { caseDef =>
- caseDef.pat match {
- case Typed(Ident(nme.WILDCARD), tpt) =>
- isMaybeJavaScriptException(tpt.tpe)
- case Ident(nme.WILDCARD) =>
- true
- case pat @ Bind(_, _) =>
- isMaybeJavaScriptException(pat.symbol.tpe)
- }
- }
-
- val (exceptValDef, exceptVar) = if (mightCatchJavaScriptException) {
- val valDef = js.VarDef(freshLocalIdent("e"),
- encodeClassType(ThrowableClass), mutable = false, {
- genApplyMethod(
- genLoadModule(RuntimePackageModule),
- RuntimePackageModule.moduleClass,
- Runtime_wrapJavaScriptException,
- List(origExceptVar))
- })
- (valDef, valDef.ref)
- } else {
- (js.Skip(), origExceptVar)
- }
-
- val elseHandler: js.Tree = js.Throw(origExceptVar)
-
- val handler0 = catches.foldRight(elseHandler) { (caseDef, elsep) =>
- implicit val pos = caseDef.pos
- val CaseDef(pat, _, body) = caseDef
-
- // Extract exception type and variable
- val (tpe, boundVar) = (pat match {
- case Typed(Ident(nme.WILDCARD), tpt) =>
- (tpt.tpe, None)
- case Ident(nme.WILDCARD) =>
- (ThrowableClass.tpe, None)
- case Bind(_, _) =>
- (pat.symbol.tpe, Some(encodeLocalSym(pat.symbol)))
- })
-
- // Generate the body that must be executed if the exception matches
- val bodyWithBoundVar = (boundVar match {
- case None =>
- genStatOrExpr(body, isStat)
- case Some(bv) =>
- val castException = genAsInstanceOf(exceptVar, tpe)
- js.Block(
- js.VarDef(bv, toIRType(tpe), mutable = false, castException),
- genStatOrExpr(body, isStat))
- })
-
- // Generate the test
- if (tpe == ThrowableClass.tpe) {
- bodyWithBoundVar
- } else {
- val cond = genIsInstanceOf(exceptVar, tpe)
- js.If(cond, bodyWithBoundVar, elsep)(resultType)
- }
- }
-
- js.Block(
- exceptValDef,
- handler0)
- }
- }
-
- val finalizerAST = genStat(finalizer) match {
- case js.Skip() => js.EmptyTree
- case ast => ast
- }
-
- if (handlerAST == js.EmptyTree && finalizerAST == js.EmptyTree) blockAST
- else js.Try(blockAST, exceptIdent, handlerAST, finalizerAST)(resultType)
- }
-
- /** Gen JS code for an Apply node (method call)
- *
- * There's a whole bunch of varieties of Apply nodes: regular method
- * calls, super calls, constructor calls, isInstanceOf/asInstanceOf,
- * primitives, JS calls, etc. They are further dispatched in here.
- */
- def genApply(tree: Apply, isStat: Boolean): js.Tree = {
- implicit val pos = tree.pos
- val Apply(fun, args) = tree
-
- fun match {
- case TypeApply(_, _) =>
- genApplyTypeApply(tree)
-
- case Select(Super(_, _), _) =>
- genSuperCall(tree)
-
- case Select(New(_), nme.CONSTRUCTOR) =>
- genApplyNew(tree)
-
- case _ =>
- val sym = fun.symbol
-
- if (sym.isLabel) {
- genLabelApply(tree)
- } else if (scalaPrimitives.isPrimitive(sym)) {
- genPrimitiveOp(tree, isStat)
- } else if (currentRun.runDefinitions.isBox(sym)) {
- // Box a primitive value (cannot be Unit)
- val arg = args.head
- makePrimitiveBox(genExpr(arg), arg.tpe)
- } else if (currentRun.runDefinitions.isUnbox(sym)) {
- // Unbox a primitive value (cannot be Unit)
- val arg = args.head
- makePrimitiveUnbox(genExpr(arg), tree.tpe)
- } else {
- genNormalApply(tree, isStat)
- }
- }
- }
-
- /** Gen an Apply with a TypeApply method.
- * Only isInstanceOf and asInstanceOf keep their type argument until the
- * backend.
- */
- private def genApplyTypeApply(tree: Apply): js.Tree = {
- implicit val pos = tree.pos
- val Apply(TypeApply(fun @ Select(obj, _), targs), _) = tree
- val sym = fun.symbol
-
- val cast = sym match {
- case Object_isInstanceOf => false
- case Object_asInstanceOf => true
- case _ =>
- abort("Unexpected type application " + fun +
- "[sym: " + sym.fullName + "]" + " in: " + tree)
- }
-
- val to = targs.head.tpe
- val l = toTypeKind(obj.tpe)
- val r = toTypeKind(to)
- val source = genExpr(obj)
-
- if (l.isValueType && r.isValueType) {
- if (cast)
- genConversion(l, r, source)
- else
- js.BooleanLiteral(l == r)
- } else if (l.isValueType) {
- val result = if (cast) {
- val ctor = ClassCastExceptionClass.info.member(
- nme.CONSTRUCTOR).suchThat(_.tpe.params.isEmpty)
- js.Throw(genNew(ClassCastExceptionClass, ctor, Nil))
- } else {
- js.BooleanLiteral(false)
- }
- js.Block(source, result) // eval and discard source
- } else if (r.isValueType) {
- assert(!cast, s"Unexpected asInstanceOf from ref type to value type")
- genIsInstanceOf(source, boxedClass(to.typeSymbol).tpe)
- } else {
- if (cast)
- genAsInstanceOf(source, to)
- else
- genIsInstanceOf(source, to)
- }
- }
-
- /** Gen JS code for a super call, of the form Class.super[mix].fun(args).
- *
- * This does not include calls defined in mixin traits, as these are
- * already desugared by the 'mixin' phase. Only calls to super classes
- * remain.
- * Since a class has exactly one direct superclass, and calling a method
- * two classes above the current one is invalid, the `mix` item is
- * irrelevant.
- */
- private def genSuperCall(tree: Apply): js.Tree = {
- implicit val pos = tree.pos
- val Apply(fun @ Select(sup @ Super(_, mix), _), args) = tree
- val sym = fun.symbol
-
- if (sym == Object_getClass) {
- // The only primitive that is also callable as super call
- js.GetClass(genThis())
- } else {
- val superCall = genStaticApplyMethod(
- genThis()(sup.pos), sym, genActualArgs(sym, args))
-
- // Initialize the module instance just after the super constructor call.
- if (isStaticModule(currentClassSym) && !isModuleInitialized &&
- currentMethodSym.isClassConstructor) {
- isModuleInitialized = true
- val thisType = jstpe.ClassType(encodeClassFullName(currentClassSym))
- val initModule = js.StoreModule(thisType, js.This()(thisType))
- js.Block(superCall, initModule, js.This()(thisType))
- } else {
- superCall
- }
- }
- }
-
- /** Gen JS code for a constructor call (new).
- * Further refined into:
- * * new String(...)
- * * new of a hijacked boxed class
- * * new of an anonymous function class that was recorded as JS function
- * * new of a raw JS class
- * * new Array
- * * regular new
- */
- private def genApplyNew(tree: Apply): js.Tree = {
- implicit val pos = tree.pos
- val Apply(fun @ Select(New(tpt), nme.CONSTRUCTOR), args) = tree
- val ctor = fun.symbol
- val tpe = tpt.tpe
-
- assert(ctor.isClassConstructor,
- "'new' call to non-constructor: " + ctor.name)
-
- if (isStringType(tpe)) {
- genNewString(tree)
- } else if (isHijackedBoxedClass(tpe.typeSymbol)) {
- genNewHijackedBoxedClass(tpe.typeSymbol, ctor, args map genExpr)
- } else if (translatedAnonFunctions contains tpe.typeSymbol) {
- val (functionMaker, funInfo) = translatedAnonFunctions(tpe.typeSymbol)
- currentMethodInfoBuilder.createsAnonFunction(funInfo)
- functionMaker(args map genExpr)
- } else if (isRawJSType(tpe)) {
- genPrimitiveJSNew(tree)
- } else {
- toTypeKind(tpe) match {
- case arr @ ARRAY(elem) =>
- genNewArray(arr.toIRType, args map genExpr)
- case rt @ REFERENCE(cls) =>
- genNew(cls, ctor, genActualArgs(ctor, args))
- case generatedType =>
- abort(s"Non reference type cannot be instantiated: $generatedType")
- }
- }
- }
-
- /** Gen jump to a label.
- * Most label-applys are caught upstream (while and do..while loops,
- * jumps to next case of a pattern match), but some are still handled here:
- * * Jumps to enclosing label-defs, including tail-recursive calls
- * * Jump to the end of a pattern match
- */
- private def genLabelApply(tree: Apply): js.Tree = {
- implicit val pos = tree.pos
- val Apply(fun, args) = tree
- val sym = fun.symbol
-
- if (enclosingLabelDefParams.contains(sym)) {
- genEnclosingLabelApply(tree)
- } else if (sym.name.toString() startsWith "matchEnd") {
- /* Jump the to the end-label of a pattern match
- * Such labels have exactly one argument, which is the result of
- * the pattern match (of type BoxedUnit if the match is in statement
- * position). We simply `return` the argument as the result of the
- * labeled block surrounding the match.
- */
- js.Return(genExpr(args.head), Some(encodeLabelSym(sym)))
- } else {
- /* No other label apply should ever happen. If it does, then we
- * have missed a pattern of LabelDef/LabelApply and some new
- * translation must be found for it.
- */
- abort("Found unknown label apply at "+tree.pos+": "+tree)
- }
- }
-
- /** Gen a label-apply to an enclosing label def.
- *
- * This is typically used for tail-recursive calls.
- *
- * Basically this is compiled into
- * continue labelDefIdent;
- * but arguments need to be updated beforehand.
- *
- * Since the rhs for the new value of an argument can depend on the value
- * of another argument (and since deciding if it is indeed the case is
- * impossible in general), new values are computed in temporary variables
- * first, then copied to the actual variables representing the argument.
- *
- * Trivial assignments (arg1 = arg1) are eliminated.
- *
- * If, after elimination of trivial assignments, only one assignment
- * remains, then we do not use a temporary variable for this one.
- */
- private def genEnclosingLabelApply(tree: Apply): js.Tree = {
- implicit val pos = tree.pos
- val Apply(fun, args) = tree
- val sym = fun.symbol
-
- // Prepare quadruplets of (formalArg, irType, tempVar, actualArg)
- // Do not include trivial assignments (when actualArg == formalArg)
- val formalArgs = enclosingLabelDefParams(sym)
- val actualArgs = args map genExpr
- val quadruplets = {
- for {
- (formalArgSym, actualArg) <- formalArgs zip actualArgs
- formalArg = encodeLocalSym(formalArgSym)
- if (actualArg match {
- case js.VarRef(`formalArg`, _) => false
- case _ => true
- })
- } yield {
- mutatedLocalVars += formalArgSym
- val tpe = toIRType(formalArgSym.tpe)
- (js.VarRef(formalArg, formalArgSym.isMutable)(tpe), tpe,
- freshLocalIdent("temp$" + formalArg.name),
- actualArg)
- }
- }
-
- // The actual jump (continue labelDefIdent;)
- val jump = js.Continue(Some(encodeLabelSym(sym)))
-
- quadruplets match {
- case Nil => jump
-
- case (formalArg, argType, _, actualArg) :: Nil =>
- js.Block(
- js.Assign(formalArg, actualArg),
- jump)
-
- case _ =>
- val tempAssignments =
- for ((_, argType, tempArg, actualArg) <- quadruplets)
- yield js.VarDef(tempArg, argType, mutable = false, actualArg)
- val trueAssignments =
- for ((formalArg, argType, tempArg, _) <- quadruplets)
- yield js.Assign(
- formalArg,
- js.VarRef(tempArg, mutable = false)(argType))
- js.Block(tempAssignments ++ trueAssignments :+ jump)
- }
- }
-
- /** Gen a "normal" apply (to a true method).
- *
- * But even these are further refined into:
- * * Methods of java.lang.String, which are redirected to the
- * RuntimeString trait implementation.
- * * Calls to methods of raw JS types (Scala.js -> JS bridge)
- * * Calls to methods in impl classes of traits.
- * * Regular method call
- */
- private def genNormalApply(tree: Apply, isStat: Boolean): js.Tree = {
- implicit val pos = tree.pos
- val Apply(fun @ Select(receiver, _), args) = tree
- val sym = fun.symbol
-
- def isStringMethodFromObject: Boolean = sym.name match {
- case nme.toString_ | nme.equals_ | nme.hashCode_ => true
- case _ => false
- }
-
- if (sym.owner == StringClass && !isStringMethodFromObject) {
- genStringCall(tree)
- } else if (isRawJSType(receiver.tpe) && sym.owner != ObjectClass) {
- genPrimitiveJSCall(tree, isStat)
- } else if (foreignIsImplClass(sym.owner)) {
- genTraitImplApply(sym, args map genExpr)
- } else if (isRawJSCtorDefaultParam(sym)) {
- js.UndefinedParam()(toIRType(sym.tpe.resultType))
- } else if (sym.isClassConstructor) {
- /* See #66: we have to emit a static call to avoid calling a
- * constructor with the same signature in a subclass */
- genStaticApplyMethod(genExpr(receiver), sym, genActualArgs(sym, args))
- } else {
- genApplyMethod(genExpr(receiver), receiver.tpe, sym, genActualArgs(sym, args))
- }
- }
-
- def genStaticApplyMethod(receiver: js.Tree, method: Symbol,
- arguments: List[js.Tree])(implicit pos: Position): js.Tree = {
- val classIdent = encodeClassFullNameIdent(method.owner)
- val methodIdent = encodeMethodSym(method)
- currentMethodInfoBuilder.callsMethodStatic(classIdent, methodIdent)
- js.StaticApply(receiver, jstpe.ClassType(classIdent.name), methodIdent,
- arguments)(toIRType(method.tpe.resultType))
- }
-
- def genTraitImplApply(method: Symbol, arguments: List[js.Tree])(
- implicit pos: Position): js.Tree = {
- val implIdent = encodeClassFullNameIdent(method.owner)
- val methodIdent = encodeMethodSym(method)
- genTraitImplApply(implIdent, methodIdent, arguments,
- toIRType(method.tpe.resultType))
- }
-
- def genTraitImplApply(implIdent: js.Ident, methodIdent: js.Ident,
- arguments: List[js.Tree], resultType: jstpe.Type)(
- implicit pos: Position): js.Tree = {
- currentMethodInfoBuilder.callsMethod(implIdent, methodIdent)
- js.TraitImplApply(jstpe.ClassType(implIdent.name), methodIdent,
- arguments)(resultType)
- }
-
- /** Gen JS code for a conversion between primitive value types */
- def genConversion(from: TypeKind, to: TypeKind, value: js.Tree)(
- implicit pos: Position): js.Tree = {
- def int0 = js.IntLiteral(0)
- def int1 = js.IntLiteral(1)
- def long0 = js.LongLiteral(0L)
- def long1 = js.LongLiteral(1L)
- def float0 = js.FloatLiteral(0.0f)
- def float1 = js.FloatLiteral(1.0f)
-
- (from, to) match {
- case (INT(_), BOOL) => js.BinaryOp(js.BinaryOp.Num_!=, value, int0)
- case (LONG, BOOL) => js.BinaryOp(js.BinaryOp.Long_!=, value, long0)
- case (FLOAT(_), BOOL) => js.BinaryOp(js.BinaryOp.Num_!=, value, float0)
-
- case (BOOL, INT(_)) => js.If(value, int1, int0 )(jstpe.IntType)
- case (BOOL, LONG) => js.If(value, long1, long0 )(jstpe.LongType)
- case (BOOL, FLOAT(_)) => js.If(value, float1, float0)(jstpe.FloatType)
-
- case _ => value
- }
- }
-
- /** Gen JS code for an isInstanceOf test (for reference types only) */
- def genIsInstanceOf(value: js.Tree, to: Type)(
- implicit pos: Position): js.Tree = {
-
- def genTypeOfTest(typeString: String) = {
- js.BinaryOp(js.BinaryOp.===,
- js.UnaryOp(js.UnaryOp.typeof, value),
- js.StringLiteral(typeString))
- }
-
- if (isRawJSType(to)) {
- to.typeSymbol match {
- case JSNumberClass => genTypeOfTest("number")
- case JSStringClass => genTypeOfTest("string")
- case JSBooleanClass => genTypeOfTest("boolean")
- case JSUndefinedClass => genTypeOfTest("undefined")
- case sym if sym.isTrait =>
- reporter.error(pos,
- s"isInstanceOf[${sym.fullName}] not supported because it is a raw JS trait")
- js.BooleanLiteral(true)
- case sym =>
- js.BinaryOp(js.BinaryOp.instanceof, value, genGlobalJSObject(sym))
- }
- } else {
- val refType = toReferenceType(to)
- currentMethodInfoBuilder.accessesClassData(refType)
- js.IsInstanceOf(value, refType)
- }
- }
-
- /** Gen JS code for an asInstanceOf cast (for reference types only) */
- def genAsInstanceOf(value: js.Tree, to: Type)(
- implicit pos: Position): js.Tree = {
-
- def default: js.Tree = {
- val refType = toReferenceType(to)
- currentMethodInfoBuilder.accessesClassData(refType)
- js.AsInstanceOf(value, refType)
- }
-
- if (isRawJSType(to)) {
- // asInstanceOf on JavaScript is completely erased
- value
- } else if (FunctionClass.seq contains to.typeSymbol) {
- /* Don't hide a JSFunctionToScala inside a useless cast, otherwise
- * the optimization avoiding double-wrapping in genApply() will not
- * be able to kick in.
- */
- value match {
- case JSFunctionToScala(fun, _) => value
- case _ => default
- }
- } else {
- default
- }
- }
-
- /** Gen JS code for a call to a Scala method.
- * This also registers that the given method is called by the current
- * method in the method info builder.
- */
- def genApplyMethod(receiver: js.Tree, receiverType: Type,
- methodSym: Symbol, arguments: List[js.Tree])(
- implicit pos: Position): js.Tree = {
- genApplyMethod(receiver, receiverType.typeSymbol, methodSym, arguments)
- }
-
- /** Gen JS code for a call to a Scala method.
- * This also registers that the given method is called by the current
- * method in the method info builder.
- */
- def genApplyMethod(receiver: js.Tree, receiverTypeSym: Symbol,
- methodSym: Symbol, arguments: List[js.Tree])(
- implicit pos: Position): js.Tree = {
- genApplyMethod(receiver, receiverTypeSym,
- encodeMethodSym(methodSym), arguments,
- toIRType(methodSym.tpe.resultType))
- }
-
- /** Gen JS code for a call to a Scala method.
- * This also registers that the given method is called by the current
- * method in the method info builder.
- */
- def genApplyMethod(receiver: js.Tree, receiverType: Type,
- methodIdent: js.Ident, arguments: List[js.Tree], resultType: jstpe.Type)(
- implicit pos: Position): js.Tree = {
- genApplyMethod(receiver, receiverType.typeSymbol, methodIdent,
- arguments, resultType)
- }
-
- /** Gen JS code for a call to a Scala method.
- * This also registers that the given method is called by the current
- * method in the method info builder.
- */
- def genApplyMethod(receiver: js.Tree, receiverTypeSym: Symbol,
- methodIdent: js.Ident, arguments: List[js.Tree], resultType: jstpe.Type)(
- implicit pos: Position): js.Tree = {
- currentMethodInfoBuilder.callsMethod(receiverTypeSym, methodIdent)
- js.Apply(receiver, methodIdent, arguments)(resultType)
- }
-
- /** Gen JS code for a call to a Scala class constructor.
- *
- * This also registers that the given class is instantiated by the current
- * method, and that the given constructor is called, in the method info
- * builder.
- */
- def genNew(clazz: Symbol, ctor: Symbol, arguments: List[js.Tree])(
- implicit pos: Position): js.Tree = {
- if (clazz.isAnonymousFunction)
- instantiatedAnonFunctions += clazz
- assert(!isRawJSFunctionDef(clazz),
- s"Trying to instantiate a raw JS function def $clazz")
- val ctorIdent = encodeMethodSym(ctor)
- currentMethodInfoBuilder.instantiatesClass(clazz)
- currentMethodInfoBuilder.callsMethod(clazz, ctorIdent)
- js.New(jstpe.ClassType(encodeClassFullName(clazz)),
- ctorIdent, arguments)
- }
-
- /** Gen JS code for a call to a constructor of a hijacked boxed class.
- * All of these have 2 constructors: one with the primitive
- * value, which is erased, and one with a String, which is
- * equivalent to BoxedClass.valueOf(arg).
- */
- private def genNewHijackedBoxedClass(clazz: Symbol, ctor: Symbol,
- arguments: List[js.Tree])(implicit pos: Position): js.Tree = {
- assert(arguments.size == 1)
- if (isStringType(ctor.tpe.params.head.tpe)) {
- // BoxedClass.valueOf(arg)
- val companion = clazz.companionModule.moduleClass
- val valueOf = getMemberMethod(companion, nme.valueOf) suchThat { s =>
- s.tpe.params.size == 1 && isStringType(s.tpe.params.head.tpe)
- }
- genApplyMethod(genLoadModule(companion), companion, valueOf, arguments)
- } else {
- // erased
- arguments.head
- }
- }
-
- /** Gen JS code for creating a new Array: new Array[T](length)
- * For multidimensional arrays (dimensions > 1), the arguments can
- * specify up to `dimensions` lengths for the first dimensions of the
- * array.
- */
- def genNewArray(arrayType: jstpe.ArrayType, arguments: List[js.Tree])(
- implicit pos: Position): js.Tree = {
- assert(arguments.length <= arrayType.dimensions,
- "too many arguments for array constructor: found " + arguments.length +
- " but array has only " + arrayType.dimensions + " dimension(s)")
-
- currentMethodInfoBuilder.accessesClassData(arrayType)
- js.NewArray(arrayType, arguments)
- }
-
- /** Gen JS code for an array literal.
- */
- def genArrayValue(tree: Tree): js.Tree = {
- implicit val pos = tree.pos
- val ArrayValue(tpt @ TypeTree(), elems) = tree
-
- val arrType = toReferenceType(tree.tpe).asInstanceOf[jstpe.ArrayType]
- currentMethodInfoBuilder.accessesClassData(arrType)
- js.ArrayValue(arrType, elems map genExpr)
- }
-
- /** Gen JS code for a Match, i.e., a switch-able pattern match
- * Eventually, this is compiled into a JS switch construct. But because
- * we can be in expression position, and a JS switch cannot be given a
- * meaning in expression position, we emit a JS "match" construct (which
- * does not need the `break`s in each case. `JSDesugaring` will transform
- * that in a switch.
- *
- * Some caveat here. It may happen that there is a guard in here, despite
- * the fact that switches cannot have guards (in the JVM nor in JS).
- * The JVM backend emits a jump to the default clause when a guard is not
- * fulfilled. We cannot do that. Instead, currently we duplicate the body
- * of the default case in the else branch of the guard test.
- */
- def genMatch(tree: Tree, isStat: Boolean): js.Tree = {
- implicit val pos = tree.pos
- val Match(selector, cases) = tree
-
- val expr = genExpr(selector)
- val resultType = toIRType(tree.tpe)
-
- val List(defaultBody0) = for {
- CaseDef(Ident(nme.WILDCARD), EmptyTree, body) <- cases
- } yield body
-
- val (defaultBody, defaultLabelSym) = defaultBody0 match {
- case LabelDef(_, Nil, rhs) if hasSynthCaseSymbol(defaultBody0) =>
- (rhs, defaultBody0.symbol)
- case _ =>
- (defaultBody0, NoSymbol)
- }
-
- val genDefaultBody = genStatOrExpr(defaultBody, isStat)
-
- var clauses: List[(List[js.Literal], js.Tree)] = Nil
- var elseClause: js.Tree = js.EmptyTree
-
- for (caze @ CaseDef(pat, guard, body) <- cases) {
- assert(guard == EmptyTree)
-
- def genBody() = body match {
- // Yes, this will duplicate the default body in the output
- case If(cond, thenp, app @ Apply(_, Nil))
- if app.symbol == defaultLabelSym =>
- js.If(genExpr(cond), genStatOrExpr(thenp, isStat), genDefaultBody)(
- resultType)(body.pos)
- case If(cond, thenp, Block(List(app @ Apply(_, Nil)), _))
- if app.symbol == defaultLabelSym =>
- js.If(genExpr(cond), genStatOrExpr(thenp, isStat), genDefaultBody)(
- resultType)(body.pos)
-
- case _ =>
- genStatOrExpr(body, isStat)
- }
-
- def genLiteral(lit: Literal): js.Literal =
- genExpr(lit).asInstanceOf[js.Literal]
-
- pat match {
- case lit: Literal =>
- clauses = (List(genLiteral(lit)), genBody()) :: clauses
- case Ident(nme.WILDCARD) =>
- elseClause = genDefaultBody
- case Alternative(alts) =>
- val genAlts = {
- alts map {
- case lit: Literal => genLiteral(lit)
- case _ =>
- abort("Invalid case in alternative in switch-like pattern match: " +
- tree + " at: " + tree.pos)
- }
- }
- clauses = (genAlts, genBody()) :: clauses
- case _ =>
- abort("Invalid case statement in switch-like pattern match: " +
- tree + " at: " + (tree.pos))
- }
- }
-
- js.Match(expr, clauses.reverse, elseClause)(resultType)
- }
-
- private def genBlock(tree: Block, isStat: Boolean): js.Tree = {
- implicit val pos = tree.pos
- val Block(stats, expr) = tree
-
- /** Predicate satisfied by LabelDefs produced by the pattern matcher */
- def isCaseLabelDef(tree: Tree) =
- tree.isInstanceOf[LabelDef] && hasSynthCaseSymbol(tree)
-
- def translateMatch(expr: LabelDef) = {
- /* Block that appeared as the result of a translated match
- * Such blocks are recognized by having at least one element that is
- * a so-called case-label-def.
- * The method `genTranslatedMatch()` takes care of compiling the
- * actual match.
- *
- * The assumption is once we encounter a case, the remainder of the
- * block will consist of cases.
- * The prologue may be empty, usually it is the valdef that stores
- * the scrut.
- */
- val (prologue, cases) = stats.span(s => !isCaseLabelDef(s))
- assert(cases.forall(isCaseLabelDef),
- "Assumption on the form of translated matches broken: " + tree)
-
- val genPrologue = prologue map genStat
- val translatedMatch =
- genTranslatedMatch(cases.map(_.asInstanceOf[LabelDef]), expr)
-
- js.Block(genPrologue :+ translatedMatch)
- }
-
- expr match {
- case expr: LabelDef if isCaseLabelDef(expr) =>
- translateMatch(expr)
-
- // Sometimes the pattern matcher casts its final result
- case Apply(TypeApply(Select(expr: LabelDef, nme.asInstanceOf_Ob), _), _)
- if isCaseLabelDef(expr) =>
- translateMatch(expr)
-
- case _ =>
- assert(!stats.exists(isCaseLabelDef), "Found stats with case label " +
- s"def in non-match block at ${tree.pos}: $tree")
-
- /* Normal block */
- val statements = stats map genStat
- val expression = genStatOrExpr(expr, isStat)
- js.Block(statements :+ expression)
- }
- }
-
- /** Gen JS code for a translated match
- *
- * This implementation relies heavily on the patterns of trees emitted
- * by the current pattern match phase (as of Scala 2.10).
- *
- * The trees output by the pattern matcher are assumed to follow these
- * rules:
- * * Each case LabelDef (in `cases`) must not take any argument.
- * * The last one must be a catch-all (case _ =>) that never falls through.
- * * Jumps to the `matchEnd` are allowed anywhere in the body of the
- * corresponding case label-defs, but not outside.
- * * Jumps to case label-defs are restricted to jumping to the very next
- * case, and only in positions denoted by <jump> in:
- * <case-body> ::=
- * If(_, <case-body>, <case-body>)
- * | Block(_, <case-body>)
- * | <jump>
- * | _
- * These restrictions, together with the fact that we are in statement
- * position (thanks to the above transformation), mean that they can be
- * simply replaced by `skip`.
- *
- * To implement jumps to `matchEnd`, which have one argument which is the
- * result of the match, we enclose all the cases in one big labeled block.
- * Jumps are then compiled as `return`s out of the block.
- */
- def genTranslatedMatch(cases: List[LabelDef],
- matchEnd: LabelDef)(implicit pos: Position): js.Tree = {
-
- val nextCaseSyms = (cases.tail map (_.symbol)) :+ NoSymbol
-
- val translatedCases = for {
- (LabelDef(_, Nil, rhs), nextCaseSym) <- cases zip nextCaseSyms
- } yield {
- def genCaseBody(tree: Tree): js.Tree = {
- implicit val pos = tree.pos
- tree match {
- case If(cond, thenp, elsep) =>
- js.If(genExpr(cond), genCaseBody(thenp), genCaseBody(elsep))(
- jstpe.NoType)
-
- case Block(stats, expr) =>
- js.Block((stats map genStat) :+ genCaseBody(expr))
-
- case Apply(_, Nil) if tree.symbol == nextCaseSym =>
- js.Skip()
-
- case _ =>
- genStat(tree)
- }
- }
-
- genCaseBody(rhs)
- }
-
- js.Labeled(encodeLabelSym(matchEnd.symbol), toIRType(matchEnd.tpe),
- js.Block(translatedCases))
- }
-
- /** Gen JS code for a primitive method call */
- private def genPrimitiveOp(tree: Apply, isStat: Boolean): js.Tree = {
- import scalaPrimitives._
-
- implicit val pos = tree.pos
-
- val sym = tree.symbol
- val Apply(fun @ Select(receiver, _), args) = tree
-
- val code = scalaPrimitives.getPrimitive(sym, receiver.tpe)
-
- if (isArithmeticOp(code) || isLogicalOp(code) || isComparisonOp(code))
- genSimpleOp(tree, receiver :: args, code)
- else if (code == scalaPrimitives.CONCAT)
- genStringConcat(tree, receiver, args)
- else if (code == HASH)
- genScalaHash(tree, receiver)
- else if (isArrayOp(code))
- genArrayOp(tree, code)
- else if (code == SYNCHRONIZED)
- genSynchronized(tree, isStat)
- else if (isCoercion(code))
- genCoercion(tree, receiver, code)
- else if (jsPrimitives.isJavaScriptPrimitive(code))
- genJSPrimitive(tree, receiver, args, code)
- else
- abort("Unknown primitive operation: " + sym.fullName + "(" +
- fun.symbol.simpleName + ") " + " at: " + (tree.pos))
- }
-
- /** Gen JS code for a simple operation (arithmetic, logical, or comparison) */
- private def genSimpleOp(tree: Apply, args: List[Tree], code: Int): js.Tree = {
- import scalaPrimitives._
-
- implicit val pos = tree.pos
-
- def isLongOp(ltpe: Type, rtpe: Type) =
- (isLongType(ltpe) || isLongType(rtpe)) &&
- !(toTypeKind(ltpe).isInstanceOf[FLOAT] ||
- toTypeKind(rtpe).isInstanceOf[FLOAT] ||
- isStringType(ltpe) || isStringType(rtpe))
-
- val sources = args map genExpr
-
- val resultType = toIRType(tree.tpe)
-
- sources match {
- // Unary operation
- case List(source) =>
- (code match {
- case POS =>
- source
- case NEG =>
- (resultType: @unchecked) match {
- case jstpe.IntType =>
- js.BinaryOp(js.BinaryOp.Int_-, js.IntLiteral(0), source)
- case jstpe.LongType =>
- js.BinaryOp(js.BinaryOp.Long_-, js.LongLiteral(0), source)
- case jstpe.FloatType =>
- js.BinaryOp(js.BinaryOp.Float_-, js.FloatLiteral(0.0f), source)
- case jstpe.DoubleType =>
- js.BinaryOp(js.BinaryOp.Double_-, js.DoubleLiteral(0), source)
- }
- case NOT =>
- (resultType: @unchecked) match {
- case jstpe.IntType =>
- js.BinaryOp(js.BinaryOp.Int_^, js.IntLiteral(-1), source)
- case jstpe.LongType =>
- js.BinaryOp(js.BinaryOp.Long_^, js.LongLiteral(-1), source)
- }
- case ZNOT =>
- js.UnaryOp(js.UnaryOp.Boolean_!, source)
- case _ =>
- abort("Unknown unary operation code: " + code)
- })
-
- // Binary operation on Longs
- case List(lsrc, rsrc) if isLongOp(args(0).tpe, args(1).tpe) =>
- def toLong(tree: js.Tree, tpe: Type) =
- if (isLongType(tpe)) tree
- else js.UnaryOp(js.UnaryOp.IntToLong, tree)
-
- def toInt(tree: js.Tree, tpe: Type) =
- if (isLongType(tpe)) js.UnaryOp(js.UnaryOp.LongToInt, rsrc)
- else tree
-
- val ltree = toLong(lsrc, args(0).tpe)
- def rtree = toLong(rsrc, args(1).tpe)
- def rtreeInt = toInt(rsrc, args(1).tpe)
-
- import js.BinaryOp._
- (code: @switch) match {
- case ADD => js.BinaryOp(Long_+, ltree, rtree)
- case SUB => js.BinaryOp(Long_-, ltree, rtree)
- case MUL => js.BinaryOp(Long_*, ltree, rtree)
- case DIV => js.BinaryOp(Long_/, ltree, rtree)
- case MOD => js.BinaryOp(Long_%, ltree, rtree)
- case OR => js.BinaryOp(Long_|, ltree, rtree)
- case XOR => js.BinaryOp(Long_^, ltree, rtree)
- case AND => js.BinaryOp(Long_&, ltree, rtree)
- case LSL => js.BinaryOp(Long_<<, ltree, rtreeInt)
- case LSR => js.BinaryOp(Long_>>>, ltree, rtreeInt)
- case ASR => js.BinaryOp(Long_>>, ltree, rtreeInt)
- case EQ => js.BinaryOp(Long_==, ltree, rtree)
- case NE => js.BinaryOp(Long_!=, ltree, rtree)
- case LT => js.BinaryOp(Long_<, ltree, rtree)
- case LE => js.BinaryOp(Long_<=, ltree, rtree)
- case GT => js.BinaryOp(Long_>, ltree, rtree)
- case GE => js.BinaryOp(Long_>=, ltree, rtree)
- case _ =>
- abort("Unknown binary operation code: " + code)
- }
-
- // Binary operation
- case List(lsrc_in, rsrc_in) =>
- def convertArg(tree: js.Tree, tpe: Type) = {
- val kind = toTypeKind(tpe)
-
- // If we end up with a long, target must be float or double
- val fromLong =
- if (kind == LongKind) js.UnaryOp(js.UnaryOp.LongToDouble, tree)
- else tree
-
- if (resultType != jstpe.FloatType) fromLong
- else if (kind == FloatKind) fromLong
- else js.UnaryOp(js.UnaryOp.DoubleToFloat, fromLong)
- }
-
- val lsrc = convertArg(lsrc_in, args(0).tpe)
- val rsrc = convertArg(rsrc_in, args(1).tpe)
-
- def genEquality(eqeq: Boolean, not: Boolean) = {
- val typeKind = toTypeKind(args(0).tpe)
- typeKind match {
- case INT(_) | LONG | FLOAT(_) =>
- /* Note that LONG happens when a fromLong() had to do something,
- * which means we're effectively in the FLOAT case. */
- js.BinaryOp(if (not) js.BinaryOp.Num_!= else js.BinaryOp.Num_==, lsrc, rsrc)
- case BOOL =>
- js.BinaryOp(if (not) js.BinaryOp.Boolean_!= else js.BinaryOp.Boolean_==, lsrc, rsrc)
- case REFERENCE(_) =>
- if (eqeq &&
- // don't call equals if we have a literal null at either side
- !lsrc.isInstanceOf[js.Null] &&
- !rsrc.isInstanceOf[js.Null]) {
- val body = genEqEqPrimitive(args(0).tpe, args(1).tpe, lsrc, rsrc)
- if (not) js.UnaryOp(js.UnaryOp.Boolean_!, body) else body
- } else {
- js.BinaryOp(if (not) js.BinaryOp.!== else js.BinaryOp.===, lsrc, rsrc)
- }
- case _ =>
- // Arrays, Null, Nothing do not have an equals() method.
- js.BinaryOp(if (not) js.BinaryOp.!== else js.BinaryOp.===, lsrc, rsrc)
- }
- }
-
- (code: @switch) match {
- case EQ => genEquality(eqeq = true, not = false)
- case NE => genEquality(eqeq = true, not = true)
- case ID => genEquality(eqeq = false, not = false)
- case NI => genEquality(eqeq = false, not = true)
-
- case ZOR => js.If(lsrc, js.BooleanLiteral(true), rsrc)(jstpe.BooleanType)
- case ZAND => js.If(lsrc, rsrc, js.BooleanLiteral(false))(jstpe.BooleanType)
-
- case _ =>
- import js.BinaryOp._
- val op = (resultType: @unchecked) match {
- case jstpe.IntType =>
- (code: @switch) match {
- case ADD => Int_+
- case SUB => Int_-
- case MUL => Int_*
- case DIV => Int_/
- case MOD => Int_%
- case OR => Int_|
- case AND => Int_&
- case XOR => Int_^
- case LSL => Int_<<
- case LSR => Int_>>>
- case ASR => Int_>>
- }
- case jstpe.FloatType =>
- (code: @switch) match {
- case ADD => Float_+
- case SUB => Float_-
- case MUL => Float_*
- case DIV => Float_/
- case MOD => Float_%
- }
- case jstpe.DoubleType =>
- (code: @switch) match {
- case ADD => Double_+
- case SUB => Double_-
- case MUL => Double_*
- case DIV => Double_/
- case MOD => Double_%
- }
- case jstpe.BooleanType =>
- (code: @switch) match {
- case LT => Num_<
- case LE => Num_<=
- case GT => Num_>
- case GE => Num_>=
- case OR => Boolean_|
- case AND => Boolean_&
- case XOR => Boolean_!=
- }
- }
- js.BinaryOp(op, lsrc, rsrc)
- }
-
- case _ =>
- abort("Too many arguments for primitive function: " + tree)
- }
- }
-
- /** Gen JS code for a call to Any.== */
- def genEqEqPrimitive(ltpe: Type, rtpe: Type, lsrc: js.Tree, rsrc: js.Tree)(
- implicit pos: Position): js.Tree = {
- /* True if the equality comparison is between values that require the
- * use of the rich equality comparator
- * (scala.runtime.BoxesRunTime.equals).
- * This is the case when either side of the comparison might have a
- * run-time type subtype of java.lang.Number or java.lang.Character,
- * **which includes when either is a raw JS type**.
- * When it is statically known that both sides are equal and subtypes of
- * Number or Character, not using the rich equality is possible (their
- * own equals method will do ok.)
- */
- val mustUseAnyComparator: Boolean = isRawJSType(ltpe) || isRawJSType(rtpe) || {
- val areSameFinals = ltpe.isFinalType && rtpe.isFinalType && (ltpe =:= rtpe)
- !areSameFinals && isMaybeBoxed(ltpe.typeSymbol) && isMaybeBoxed(rtpe.typeSymbol)
- }
-
- if (mustUseAnyComparator) {
- val equalsMethod: Symbol = {
- val ptfm = platform.asInstanceOf[backend.JavaPlatform with ThisPlatform] // 2.10 compat
- if (ltpe <:< BoxedNumberClass.tpe) {
- if (rtpe <:< BoxedNumberClass.tpe) ptfm.externalEqualsNumNum
- else if (rtpe <:< BoxedCharacterClass.tpe) ptfm.externalEqualsNumChar
- else ptfm.externalEqualsNumObject
- } else ptfm.externalEquals
- }
- val moduleClass = equalsMethod.owner
- val instance = genLoadModule(moduleClass)
- genApplyMethod(instance, moduleClass, equalsMethod, List(lsrc, rsrc))
- } else {
- // if (lsrc eq null) rsrc eq null else lsrc.equals(rsrc)
- if (isStringType(ltpe)) {
- // String.equals(that) === (this eq that)
- js.BinaryOp(js.BinaryOp.===, lsrc, rsrc)
- } else {
- /* This requires to evaluate both operands in local values first.
- * The optimizer will eliminate them if possible.
- */
- val ltemp = js.VarDef(freshLocalIdent(), lsrc.tpe, mutable = false, lsrc)
- val rtemp = js.VarDef(freshLocalIdent(), rsrc.tpe, mutable = false, rsrc)
- js.Block(
- ltemp,
- rtemp,
- js.If(js.BinaryOp(js.BinaryOp.===, ltemp.ref, js.Null()),
- js.BinaryOp(js.BinaryOp.===, rtemp.ref, js.Null()),
- genApplyMethod(ltemp.ref, ltpe, Object_equals, List(rtemp.ref)))(
- jstpe.BooleanType))
- }
- }
- }
-
- /** Gen JS code for string concatenation.
- */
- private def genStringConcat(tree: Apply, receiver: Tree,
- args: List[Tree]): js.Tree = {
- implicit val pos = tree.pos
-
- /* Primitive number types such as scala.Int have a
- * def +(s: String): String
- * method, which is why we have to box the lhs sometimes.
- * Otherwise, both lhs and rhs are already reference types (Any of String)
- * so boxing is not necessary (in particular, rhs is never a primitive).
- */
- assert(!isPrimitiveValueType(receiver.tpe) || isStringType(args.head.tpe))
- assert(!isPrimitiveValueType(args.head.tpe))
-
- val rhs = genExpr(args.head)
-
- val lhs = {
- val lhs0 = genExpr(receiver)
- // Box the receiver if it is a primitive value
- if (!isPrimitiveValueType(receiver.tpe)) lhs0
- else makePrimitiveBox(lhs0, receiver.tpe)
- }
-
- js.BinaryOp(js.BinaryOp.String_+, lhs, rhs)
- }
-
- /** Gen JS code for a call to Any.## */
- private def genScalaHash(tree: Apply, receiver: Tree): js.Tree = {
- implicit val pos = tree.pos
-
- val instance = genLoadModule(ScalaRunTimeModule)
- val arguments = List(genExpr(receiver))
- val sym = getMember(ScalaRunTimeModule, nme.hash_)
-
- genApplyMethod(instance, ScalaRunTimeModule.moduleClass, sym, arguments)
- }
-
- /** Gen JS code for an array operation (get, set or length) */
- private def genArrayOp(tree: Tree, code: Int): js.Tree = {
- import scalaPrimitives._
-
- implicit val pos = tree.pos
-
- val Apply(Select(arrayObj, _), args) = tree
- val arrayValue = genExpr(arrayObj)
- val arguments = args map genExpr
-
- def genSelect() = {
- val elemIRType =
- toTypeKind(arrayObj.tpe).asInstanceOf[ARRAY].elem.toIRType
- js.ArraySelect(arrayValue, arguments(0))(elemIRType)
- }
-
- if (scalaPrimitives.isArrayGet(code)) {
- // get an item of the array
- assert(args.length == 1,
- s"Array get requires 1 argument, found ${args.length} in $tree")
- genSelect()
- } else if (scalaPrimitives.isArraySet(code)) {
- // set an item of the array
- assert(args.length == 2,
- s"Array set requires 2 arguments, found ${args.length} in $tree")
- js.Assign(genSelect(), arguments(1))
- } else {
- // length of the array
- js.ArrayLength(arrayValue)
- }
- }
-
- /** Gen JS code for a call to AnyRef.synchronized */
- private def genSynchronized(tree: Apply, isStat: Boolean): js.Tree = {
- /* JavaScript is single-threaded, so we can drop the
- * synchronization altogether.
- */
- val Apply(Select(receiver, _), List(arg)) = tree
- val newReceiver = genExpr(receiver)
- val newArg = genStatOrExpr(arg, isStat)
- newReceiver match {
- case js.This() =>
- // common case for which there is no side-effect nor NPE
- newArg
- case _ =>
- implicit val pos = tree.pos
- val NPECtor = getMemberMethod(NullPointerExceptionClass,
- nme.CONSTRUCTOR).suchThat(_.tpe.params.isEmpty)
- js.Block(
- js.If(js.BinaryOp(js.BinaryOp.===, newReceiver, js.Null()),
- js.Throw(genNew(NullPointerExceptionClass, NPECtor, Nil)),
- js.Skip())(jstpe.NoType),
- newArg)
- }
- }
-
- /** Gen JS code for a coercion */
- private def genCoercion(tree: Apply, receiver: Tree, code: Int): js.Tree = {
- import scalaPrimitives._
-
- implicit val pos = tree.pos
-
- val source = genExpr(receiver)
-
- def source2int = (code: @switch) match {
- case F2C | D2C | F2B | D2B | F2S | D2S | F2I | D2I =>
- js.UnaryOp(js.UnaryOp.DoubleToInt, source)
- case L2C | L2B | L2S | L2I =>
- js.UnaryOp(js.UnaryOp.LongToInt, source)
- case _ =>
- source
- }
-
- (code: @switch) match {
- // To Char, need to crop at unsigned 16-bit
- case B2C | S2C | I2C | L2C | F2C | D2C =>
- js.BinaryOp(js.BinaryOp.Int_&, source2int, js.IntLiteral(0xffff))
-
- // To Byte, need to crop at signed 8-bit
- case C2B | S2B | I2B | L2B | F2B | D2B =>
- // note: & 0xff would not work because of negative values
- js.BinaryOp(js.BinaryOp.Int_>>,
- js.BinaryOp(js.BinaryOp.Int_<<, source2int, js.IntLiteral(24)),
- js.IntLiteral(24))
-
- // To Short, need to crop at signed 16-bit
- case C2S | I2S | L2S | F2S | D2S =>
- // note: & 0xffff would not work because of negative values
- js.BinaryOp(js.BinaryOp.Int_>>,
- js.BinaryOp(js.BinaryOp.Int_<<, source2int, js.IntLiteral(16)),
- js.IntLiteral(16))
-
- // To Int, need to crop at signed 32-bit
- case L2I | F2I | D2I =>
- source2int
-
- // Any int to Long
- case C2L | B2L | S2L | I2L =>
- js.UnaryOp(js.UnaryOp.IntToLong, source)
-
- // Any double to Long
- case F2L | D2L =>
- js.UnaryOp(js.UnaryOp.DoubleToLong, source)
-
- // Long to Double
- case L2D =>
- js.UnaryOp(js.UnaryOp.LongToDouble, source)
-
- // Any int, or Double, to Float
- case C2F | B2F | S2F | I2F | D2F =>
- js.UnaryOp(js.UnaryOp.DoubleToFloat, source)
-
- // Long to Float === Long to Double to Float
- case L2F =>
- js.UnaryOp(js.UnaryOp.DoubleToFloat,
- js.UnaryOp(js.UnaryOp.LongToDouble, source))
-
- // Identities and IR upcasts
- case C2C | B2B | S2S | I2I | L2L | F2F | D2D |
- C2I | C2D |
- B2S | B2I | B2D |
- S2I | S2D |
- I2D |
- F2D =>
- source
- }
- }
-
- /** Gen JS code for an ApplyDynamic
- * ApplyDynamic nodes appear as the result of calls to methods of a
- * structural type.
- *
- * Most unfortunately, earlier phases of the compiler assume too much
- * about the backend, namely, they believe arguments and the result must
- * be boxed, and do the boxing themselves. This decision should be left
- * to the backend, but it's not, so we have to undo these boxes.
- * Note that this applies to parameter types only. The return type is boxed
- * anyway since we do not know it's exact type.
- *
- * This then generates a call to the reflective call proxy for the given
- * arguments.
- */
- private def genApplyDynamic(tree: ApplyDynamic): js.Tree = {
- implicit val pos = tree.pos
-
- val sym = tree.symbol
- val params = sym.tpe.params
-
- /** check if the method we are invoking is eq or ne. they cannot be
- * overridden since they are final. If this is true, we only emit a
- * `===` or `!==`.
- */
- val isEqOrNeq = (sym.name == nme.eq || sym.name == nme.ne) &&
- params.size == 1 && params.head.tpe.typeSymbol == ObjectClass
-
- /** check if the method we are invoking conforms to a method on
- * scala.Array. If this is the case, we check that case specially at
- * runtime to avoid having reflective call proxies on scala.Array.
- * (Also, note that the element type of Array#update is not erased and
- * therefore the method name mangling would turn out wrong)
- *
- * Note that we cannot check if the expected return type is correct,
- * since this type information is already erased.
- */
- def isArrayLikeOp = {
- sym.name == nme.update &&
- params.size == 2 && params.head.tpe.typeSymbol == IntClass ||
- sym.name == nme.apply &&
- params.size == 1 && params.head.tpe.typeSymbol == IntClass ||
- sym.name == nme.length &&
- params.size == 0 ||
- sym.name == nme.clone_ &&
- params.size == 0
- }
-
- /**
- * Tests whether one of our reflective "boxes" for primitive types
- * implements the particular method. If this is the case
- * (result != NoSymbol), we generate a runtime instance check if we are
- * dealing with the appropriate primitive type.
- */
- def matchingSymIn(clazz: Symbol) = clazz.tpe.member(sym.name).suchThat { s =>
- val sParams = s.tpe.params
- !s.isBridge &&
- params.size == sParams.size &&
- (params zip sParams).forall { case (s1,s2) =>
- s1.tpe =:= s2.tpe
- }
- }
-
- val ApplyDynamic(receiver, args) = tree
-
- if (isEqOrNeq) {
- // Just emit a boxed equality check
- val jsThis = genExpr(receiver)
- val jsThat = genExpr(args.head)
- val op = if (sym.name == nme.eq) js.BinaryOp.=== else js.BinaryOp.!==
- ensureBoxed(js.BinaryOp(op, jsThis, jsThat), BooleanClass.tpe)
- } else {
- // Create a fully-fledged reflective call
- val receiverType = toIRType(receiver.tpe)
- val callTrgIdent = freshLocalIdent()
- val callTrgVarDef =
- js.VarDef(callTrgIdent, receiverType, mutable = false, genExpr(receiver))
- val callTrg = js.VarRef(callTrgIdent, mutable = false)(receiverType)
-
- val arguments = args zip sym.tpe.params map { case (arg, param) =>
- /* No need for enteringPosterasure, because value classes are not
- * supported as parameters of methods in structural types.
- * We could do it for safety and future-proofing anyway, except that
- * I am weary of calling enteringPosterasure for a reflective method
- * symbol.
- *
- * Note also that this will typically unbox a primitive value that
- * has just been boxed, or will .asInstanceOf[T] an expression which
- * is already of type T. But the optimizer will get rid of that, and
- * reflective calls are not numerous, so we don't complicate the
- * compiler to eliminate them early.
- */
- fromAny(genExpr(arg), param.tpe)
- }
-
- val proxyIdent = encodeMethodSym(sym, reflProxy = true)
- var callStatement: js.Tree =
- genApplyMethod(callTrg, receiver.tpe, proxyIdent, arguments,
- jstpe.AnyType)
-
- if (isArrayLikeOp) {
- def genRTCall(method: Symbol, args: js.Tree*) =
- genApplyMethod(genLoadModule(ScalaRunTimeModule),
- ScalaRunTimeModule.moduleClass, method, args.toList)
- val isArrayTree =
- genRTCall(ScalaRunTime_isArray, callTrg, js.IntLiteral(1))
- callStatement = js.If(isArrayTree, {
- sym.name match {
- case nme.update =>
- js.Block(
- genRTCall(currentRun.runDefinitions.arrayUpdateMethod,
- callTrg, arguments(0), arguments(1)),
- js.Undefined()) // Boxed Unit
- case nme.apply =>
- genRTCall(currentRun.runDefinitions.arrayApplyMethod, callTrg,
- arguments(0))
- case nme.length =>
- genRTCall(currentRun.runDefinitions.arrayLengthMethod, callTrg)
- case nme.clone_ =>
- genApplyMethod(callTrg, receiver.tpe, Object_clone, arguments)
- }
- }, {
- callStatement
- })(jstpe.AnyType)
- }
-
- for {
- (primTypeOf, reflBoxClass) <- Seq(
- ("string", StringClass),
- ("number", NumberReflectiveCallClass),
- ("boolean", BooleanReflectiveCallClass)
- )
- implMethodSym = matchingSymIn(reflBoxClass)
- if implMethodSym != NoSymbol && implMethodSym.isPublic
- } {
- callStatement = js.If(
- js.BinaryOp(js.BinaryOp.===,
- js.UnaryOp(js.UnaryOp.typeof, callTrg),
- js.StringLiteral(primTypeOf)), {
- if (implMethodSym.owner == ObjectClass) {
- // If the method is defined on Object, we can call it normally.
- genApplyMethod(callTrg, receiver.tpe, implMethodSym, arguments)
- } else {
- if (primTypeOf == "string") {
- val (rtModuleClass, methodIdent) =
- encodeRTStringMethodSym(implMethodSym)
- val retTpe = implMethodSym.tpe.resultType
- val castCallTrg = fromAny(callTrg, StringClass.toTypeConstructor)
- val rawApply = genApplyMethod(
- genLoadModule(rtModuleClass),
- rtModuleClass,
- methodIdent,
- castCallTrg :: arguments,
- toIRType(retTpe))
- // Box the result of the implementing method if required
- if (isPrimitiveValueType(retTpe))
- makePrimitiveBox(rawApply, retTpe)
- else
- rawApply
- } else {
- val (reflBoxClassPatched, callTrg1) = {
- def isIntOrLongKind(kind: TypeKind) = kind match {
- case _:INT | LONG => true
- case _ => false
- }
- if (primTypeOf == "number" &&
- toTypeKind(implMethodSym.tpe.resultType) == DoubleKind &&
- isIntOrLongKind(toTypeKind(sym.tpe.resultType))) {
- // This must be an Int, and not a Double
- (IntegerReflectiveCallClass,
- js.AsInstanceOf(callTrg,
- toReferenceType(BoxedIntClass.toTypeConstructor)))
- } else {
- (reflBoxClass, callTrg)
- }
- }
- val castCallTrg =
- fromAny(callTrg1,
- reflBoxClassPatched.primaryConstructor.tpe.params.head.tpe)
- val reflBox = genNew(reflBoxClassPatched,
- reflBoxClassPatched.primaryConstructor, List(castCallTrg))
- genApplyMethod(
- reflBox,
- reflBoxClassPatched,
- proxyIdent,
- arguments,
- jstpe.AnyType)
- }
- }
- }, { // else
- callStatement
- })(jstpe.AnyType)
- }
-
- js.Block(callTrgVarDef, callStatement)
- }
- }
-
- /** Ensures that the value of the given tree is boxed.
- * @param expr Tree to be boxed if needed.
- * @param tpeEnteringPosterasure The type of `expr` as it was entering
- * the posterasure phase.
- */
- def ensureBoxed(expr: js.Tree, tpeEnteringPosterasure: Type)(
- implicit pos: Position): js.Tree = {
-
- tpeEnteringPosterasure match {
- case tpe if isPrimitiveValueType(tpe) =>
- makePrimitiveBox(expr, tpe)
-
- case tpe: ErasedValueType =>
- val boxedClass = tpe.valueClazz
- val ctor = boxedClass.primaryConstructor
- genNew(boxedClass, ctor, List(expr))
-
- case _ =>
- expr
- }
- }
-
- /** Extracts a value typed as Any to the given type after posterasure.
- * @param expr Tree to be extracted.
- * @param tpeEnteringPosterasure The type of `expr` as it was entering
- * the posterasure phase.
- */
- def fromAny(expr: js.Tree, tpeEnteringPosterasure: Type)(
- implicit pos: Position): js.Tree = {
-
- tpeEnteringPosterasure match {
- case tpe if isPrimitiveValueType(tpe) =>
- makePrimitiveUnbox(expr, tpe)
-
- case tpe: ErasedValueType =>
- val boxedClass = tpe.valueClazz
- val unboxMethod = boxedClass.derivedValueClassUnbox
- val content = genApplyMethod(
- genAsInstanceOf(expr, tpe),
- boxedClass, unboxMethod, Nil)
- if (unboxMethod.tpe.resultType <:< tpe.erasedUnderlying)
- content
- else
- fromAny(content, tpe.erasedUnderlying)
-
- case tpe =>
- genAsInstanceOf(expr, tpe)
- }
- }
-
- /** Gen a boxing operation (tpe is the primitive type) */
- def makePrimitiveBox(expr: js.Tree, tpe: Type)(
- implicit pos: Position): js.Tree = {
- toTypeKind(tpe) match {
- case VOID => // must be handled at least for JS interop
- js.Block(expr, js.Undefined())
- case kind: ValueTypeKind =>
- if (kind == CharKind) {
- genApplyMethod(
- genLoadModule(BoxesRunTimeClass),
- BoxesRunTimeClass,
- BoxesRunTime_boxToCharacter,
- List(expr))
- } else {
- expr // box is identity for all non-Char types
- }
- case _ =>
- abort(s"makePrimitiveBox requires a primitive type, found $tpe at $pos")
- }
- }
-
- /** Gen an unboxing operation (tpe is the primitive type) */
- def makePrimitiveUnbox(expr: js.Tree, tpe: Type)(
- implicit pos: Position): js.Tree = {
- toTypeKind(tpe) match {
- case VOID => // must be handled at least for JS interop
- expr
- case kind: ValueTypeKind =>
- if (kind == CharKind) {
- genApplyMethod(
- genLoadModule(BoxesRunTimeClass),
- BoxesRunTimeClass,
- BoxesRunTime_unboxToChar,
- List(expr))
- } else {
- js.Unbox(expr, kind.primitiveCharCode)
- }
- case _ =>
- abort(s"makePrimitiveUnbox requires a primitive type, found $tpe at $pos")
- }
- }
-
- private def lookupModuleClass(name: String) = {
- val module = getModuleIfDefined(name)
- if (module == NoSymbol) NoSymbol
- else module.moduleClass
- }
-
- lazy val ReflectArrayModuleClass = lookupModuleClass("java.lang.reflect.Array")
- lazy val UtilArraysModuleClass = lookupModuleClass("java.util.Arrays")
-
- /** Gen JS code for a Scala.js-specific primitive method */
- private def genJSPrimitive(tree: Apply, receiver0: Tree,
- args: List[Tree], code: Int): js.Tree = {
- import jsPrimitives._
-
- implicit val pos = tree.pos
-
- def receiver = genExpr(receiver0)
- val genArgArray = genPrimitiveJSArgs(tree.symbol, args)
-
- lazy val js.JSArrayConstr(genArgs) = genArgArray
-
- def extractFirstArg() = {
- (genArgArray: @unchecked) match {
- case js.JSArrayConstr(firstArg :: otherArgs) =>
- (firstArg, js.JSArrayConstr(otherArgs))
- case js.JSBracketMethodApply(
- js.JSArrayConstr(firstArg :: firstPart), concat, otherParts) =>
- (firstArg, js.JSBracketMethodApply(
- js.JSArrayConstr(firstPart), concat, otherParts))
- }
- }
-
- if (code == DYNNEW) {
- // js.Dynamic.newInstance(clazz)(actualArgs:_*)
- val (jsClass, actualArgArray) = extractFirstArg()
- actualArgArray match {
- case js.JSArrayConstr(actualArgs) =>
- js.JSNew(jsClass, actualArgs)
- case _ =>
- genNewJSWithVarargs(jsClass, actualArgArray)
- }
- } else if (code == DYNAPPLY) {
- // js.Dynamic.applyDynamic(methodName)(actualArgs:_*)
- val (methodName, actualArgArray) = extractFirstArg()
- actualArgArray match {
- case js.JSArrayConstr(actualArgs) =>
- js.JSBracketMethodApply(receiver, methodName, actualArgs)
- case _ =>
- genApplyJSMethodWithVarargs(receiver, methodName, actualArgArray)
- }
- } else if (code == DYNLITN) {
- // We have a call of the form:
- // js.Dynamic.literal(name1 = ..., name2 = ...)
- // Translate to:
- // {"name1": ..., "name2": ... }
- extractFirstArg() match {
- case (js.StringLiteral("apply"),
- js.JSArrayConstr(jse.LitNamed(pairs))) =>
- js.JSObjectConstr(pairs)
- case (js.StringLiteral(name), _) if name != "apply" =>
- reporter.error(pos,
- s"js.Dynamic.literal does not have a method named $name")
- js.Undefined()
- case _ =>
- reporter.error(pos,
- "js.Dynamic.literal.applyDynamicNamed may not be called directly")
- js.Undefined()
- }
- } else if (code == DYNLIT) {
- // We have a call of some other form
- // js.Dynamic.literal(...)
- // Translate to:
- // var obj = {};
- // obj[...] = ...;
- // obj
-
- // Extract first arg to future proof against varargs
- extractFirstArg() match {
- // case js.Dynamic.literal("name1" -> ..., "name2" -> ...)
- case (js.StringLiteral("apply"),
- js.JSArrayConstr(jse.LitNamed(pairs))) =>
- js.JSObjectConstr(pairs)
-
- // case js.Dynamic.literal(x, y)
- case (js.StringLiteral("apply"), js.JSArrayConstr(tups)) =>
- // Create tmp variable
- val resIdent = freshLocalIdent("obj")
- val resVarDef = js.VarDef(resIdent, jstpe.AnyType, mutable = false,
- js.JSObjectConstr(Nil))
- val res = resVarDef.ref
-
- // Assign fields
- val tuple2Type = encodeClassType(TupleClass(2))
- val assigns = tups flatMap {
- // special case for literals
- case jse.Tuple2(name, value) =>
- js.Assign(js.JSBracketSelect(res, name), value) :: Nil
- case tupExpr =>
- val tupIdent = freshLocalIdent("tup")
- val tup = js.VarRef(tupIdent, mutable = false)(tuple2Type)
- js.VarDef(tupIdent, tuple2Type, mutable = false, tupExpr) ::
- js.Assign(js.JSBracketSelect(res,
- genApplyMethod(tup, TupleClass(2), js.Ident("$$und1__O"), Nil, jstpe.AnyType)),
- genApplyMethod(tup, TupleClass(2), js.Ident("$$und2__O"), Nil, jstpe.AnyType)) :: Nil
- }
-
- js.Block(resVarDef +: assigns :+ res: _*)
-
- /* Here we would need the case where the varargs are passed in
- * as non-literal list:
- * js.Dynamic.literal(x: _*)
- * However, Scala does not currently support this
- */
-
- // case where another method is called
- case (js.StringLiteral(name), _) if name != "apply" =>
- reporter.error(pos,
- s"js.Dynamic.literal does not have a method named $name")
- js.Undefined()
- case _ =>
- reporter.error(pos,
- "js.Dynamic.literal.applyDynamic may not be called directly")
- js.Undefined()
- }
- } else if (code == ARR_CREATE) {
- // js.Array.create(elements: _*)
- genArgArray
- } else (genArgs match {
- case Nil =>
- code match {
- case GETCLASS => js.GetClass(receiver)
- case ENV_INFO => js.JSEnvInfo()
- case DEBUGGER => js.Debugger()
- case UNDEFVAL => js.Undefined()
- case UNITVAL => js.Undefined()
- case UNITTYPE => genClassConstant(UnitTpe)
- case JS_NATIVE =>
- reporter.error(pos, "js.native may only be used as stub implementation in facade types")
- js.Undefined()
- }
-
- case List(arg) =>
-
- /** Factorization of F2JS and F2JSTHIS. */
- def genFunctionToJSFunction(isThisFunction: Boolean): js.Tree = {
- val arity = {
- val funName = tree.fun.symbol.name.encoded
- assert(funName.startsWith("fromFunction"))
- funName.stripPrefix("fromFunction").toInt
- }
- val inputClass = FunctionClass(arity)
- val inputIRType = encodeClassType(inputClass)
- val applyMeth = getMemberMethod(inputClass, nme.apply) suchThat { s =>
- val ps = s.paramss
- ps.size == 1 &&
- ps.head.size == arity &&
- ps.head.forall(_.tpe.typeSymbol == ObjectClass)
- }
- val fCaptureParam = js.ParamDef(js.Ident("f"), inputIRType,
- mutable = false)
- val jsArity =
- if (isThisFunction) arity - 1
- else arity
- val jsParams = (1 to jsArity).toList map {
- x => js.ParamDef(js.Ident("arg"+x), jstpe.AnyType,
- mutable = false)
- }
- js.Closure(
- List(fCaptureParam),
- jsParams,
- genApplyMethod(
- fCaptureParam.ref,
- inputClass, applyMeth,
- if (isThisFunction)
- js.This()(jstpe.AnyType) :: jsParams.map(_.ref)
- else
- jsParams.map(_.ref)),
- List(arg))
- }
-
- code match {
- /** Convert a scala.FunctionN f to a js.FunctionN. */
- case F2JS =>
- arg match {
- /* This case will happen every time we have a Scala lambda
- * in js.FunctionN position. We remove the JS function to
- * Scala function wrapper, instead of adding a Scala function
- * to JS function wrapper.
- */
- case JSFunctionToScala(fun, arity) =>
- fun
- case _ =>
- genFunctionToJSFunction(isThisFunction = false)
- }
-
- /** Convert a scala.FunctionN f to a js.ThisFunction{N-1}. */
- case F2JSTHIS =>
- genFunctionToJSFunction(isThisFunction = true)
-
- case DYNSELECT =>
- // js.Dynamic.selectDynamic(arg)
- js.JSBracketSelect(receiver, arg)
-
- case DICT_DEL =>
- // js.Dictionary.delete(arg)
- js.JSDelete(js.JSBracketSelect(receiver, arg))
-
- case ISUNDEF =>
- // js.isUndefined(arg)
- js.BinaryOp(js.BinaryOp.===, arg, js.Undefined())
- case TYPEOF =>
- // js.typeOf(arg)
- js.UnaryOp(js.UnaryOp.typeof, arg)
-
- case OBJPROPS =>
- // js.Object.properties(arg)
- genApplyMethod(
- genLoadModule(RuntimePackageModule),
- RuntimePackageModule.moduleClass,
- Runtime_propertiesOf,
- List(arg))
- }
-
- case List(arg1, arg2) =>
- code match {
- case DYNUPDATE =>
- // js.Dynamic.updateDynamic(arg1)(arg2)
- js.Assign(js.JSBracketSelect(receiver, arg1), arg2)
-
- case HASPROP =>
- // js.Object.hasProperty(arg1, arg2)
- /* Here we have an issue with evaluation order of arg1 and arg2,
- * since the obvious translation is `arg2 in arg1`, but then
- * arg2 is evaluated before arg1. Since this is not a commonly
- * used operator, we don't try to avoid unnessary temp vars, and
- * simply always evaluate arg1 in a temp before doing the `in`.
- */
- val temp = freshLocalIdent()
- js.Block(
- js.VarDef(temp, jstpe.AnyType, mutable = false, arg1),
- js.BinaryOp(js.BinaryOp.in, arg2,
- js.VarRef(temp, mutable = false)(jstpe.AnyType)))
- }
- })
- }
-
- /** Gen JS code for a primitive JS call (to a method of a subclass of js.Any)
- * This is the typed Scala.js to JS bridge feature. Basically it boils
- * down to calling the method without name mangling. But other aspects
- * come into play:
- * * Operator methods are translated to JS operators (not method calls)
- * * apply is translated as a function call, i.e. o() instead of o.apply()
- * * Scala varargs are turned into JS varargs (see genPrimitiveJSArgs())
- * * Getters and parameterless methods are translated as Selects
- * * Setters are translated to Assigns of Selects
- */
- private def genPrimitiveJSCall(tree: Apply, isStat: Boolean): js.Tree = {
- implicit val pos = tree.pos
-
- val sym = tree.symbol
- val Apply(fun @ Select(receiver0, _), args0) = tree
-
- val funName = sym.unexpandedName.decoded
- val receiver = genExpr(receiver0)
- val argArray = genPrimitiveJSArgs(sym, args0)
-
- // valid only for methods that don't have any varargs
- lazy val js.JSArrayConstr(args) = argArray
- lazy val argc = args.length
-
- def hasExplicitJSEncoding =
- sym.hasAnnotation(JSNameAnnotation) ||
- sym.hasAnnotation(JSBracketAccessAnnotation)
-
- val boxedResult = funName match {
- case "unary_+" | "unary_-" | "unary_~" | "unary_!" =>
- assert(argc == 0)
- js.JSUnaryOp(funName.substring(funName.length-1), receiver)
-
- case "+" | "-" | "*" | "/" | "%" | "<<" | ">>" | ">>>" |
- "&" | "|" | "^" | "&&" | "||" | "<" | ">" | "<=" | ">=" =>
- assert(argc == 1)
- js.JSBinaryOp(funName, receiver, args.head)
-
- case "apply" if receiver0.tpe.typeSymbol.isSubClass(JSThisFunctionClass) =>
- js.JSBracketMethodApply(receiver, js.StringLiteral("call"), args)
-
- case "apply" if !hasExplicitJSEncoding =>
- argArray match {
- case js.JSArrayConstr(args) =>
- js.JSFunctionApply(receiver, args)
- case _ =>
- js.JSBracketMethodApply(
- receiver, js.StringLiteral("apply"), List(js.Null(), argArray))
- }
-
- case _ =>
- def jsFunName = jsNameOf(sym)
-
- if (sym.hasFlag(reflect.internal.Flags.DEFAULTPARAM)) {
- js.UndefinedParam()(toIRType(sym.tpe.resultType))
- } else if (jsInterop.isJSGetter(sym)) {
- assert(argc == 0)
- js.JSBracketSelect(receiver, js.StringLiteral(jsFunName))
- } else if (jsInterop.isJSSetter(sym)) {
- assert(argc == 1)
- js.Assign(
- js.JSBracketSelect(receiver,
- js.StringLiteral(jsFunName.stripSuffix("_="))),
- args.head)
- } else if (jsInterop.isJSBracketAccess(sym)) {
- assert(argArray.isInstanceOf[js.JSArrayConstr] && (argc == 1 || argc == 2),
- s"@JSBracketAccess methods should have 1 or 2 non-varargs arguments")
- args match {
- case List(keyArg) =>
- js.JSBracketSelect(receiver, keyArg)
- case List(keyArg, valueArg) =>
- js.Assign(
- js.JSBracketSelect(receiver, keyArg),
- valueArg)
- }
- } else {
- argArray match {
- case js.JSArrayConstr(args) =>
- js.JSBracketMethodApply(
- receiver, js.StringLiteral(jsFunName), args)
- case _ =>
- genApplyJSMethodWithVarargs(receiver,
- js.StringLiteral(jsFunName), argArray)
- }
- }
- }
-
- boxedResult match {
- case js.UndefinedParam() | js.Assign(_, _) =>
- boxedResult
- case _ if isStat =>
- boxedResult
- case _ =>
- fromAny(boxedResult,
- enteringPhase(currentRun.posterasurePhase)(sym.tpe.resultType))
- }
- }
-
- /** Gen JS code to call a primitive JS method with variadic parameters. */
- private def genApplyJSMethodWithVarargs(receiver: js.Tree,
- methodName: js.Tree, argArray: js.Tree)(
- implicit pos: Position): js.Tree = {
- // We need to evaluate `receiver` only once
- val receiverValDef =
- js.VarDef(freshLocalIdent(), receiver.tpe, mutable = false, receiver)
- js.Block(
- receiverValDef,
- js.JSBracketMethodApply(
- js.JSBracketSelect(receiverValDef.ref, methodName),
- js.StringLiteral("apply"),
- List(receiverValDef.ref, argArray)))
- }
-
- /** Gen JS code to instantiate a JS class with variadic parameters. */
- private def genNewJSWithVarargs(jsClass: js.Tree, argArray: js.Tree)(
- implicit pos: Position): js.Tree = {
- genApplyMethod(
- genLoadModule(RuntimePackageModule),
- RuntimePackageModule.moduleClass,
- Runtime_newJSObjectWithVarargs,
- List(jsClass, argArray))
- }
-
- /** Gen JS code for new java.lang.String(...)
- * Proxies calls to method newString on object
- * scala.scalajs.runtime.RuntimeString with proper arguments
- */
- private def genNewString(tree: Apply): js.Tree = {
- implicit val pos = tree.pos
- val Apply(fun @ Select(_, _), args0) = tree
-
- val ctor = fun.symbol
- val args = args0 map genExpr
-
- // Filter members of target module for matching member
- val compMembers = for {
- mem <- RuntimeStringModule.tpe.members
- if mem.name == jsnme.newString && ctor.tpe.matches(mem.tpe)
- } yield mem
-
- if (compMembers.isEmpty) {
- reporter.error(pos,
- s"""Could not find implementation for constructor of java.lang.String
- |with type ${ctor.tpe}. Constructors on java.lang.String
- |are forwarded to the companion object of
- |scala.scalajs.runtime.RuntimeString""".stripMargin)
- js.Undefined()
- } else {
- assert(compMembers.size == 1,
- s"""For constructor with type ${ctor.tpe} on java.lang.String,
- |found multiple companion module members.""".stripMargin)
-
- // Emit call to companion object
- genApplyMethod(
- genLoadModule(RuntimeStringModule),
- RuntimeStringModule.moduleClass,
- compMembers.head,
- args)
- }
- }
-
- /** Gen JS code for calling a method on java.lang.String.
- *
- * Forwards call on java.lang.String to the module
- * scala.scalajs.runtime.RuntimeString.
- */
- private def genStringCall(tree: Apply): js.Tree = {
- implicit val pos = tree.pos
-
- val sym = tree.symbol
-
- // Deconstruct tree and create receiver and argument JS expressions
- val Apply(Select(receiver0, _), args0) = tree
- val receiver = genExpr(receiver0)
- val args = args0 map genExpr
-
- // Emit call to the RuntimeString module
- val (rtModuleClass, methodIdent) = encodeRTStringMethodSym(sym)
- genApplyMethod(
- genLoadModule(rtModuleClass),
- rtModuleClass,
- methodIdent,
- receiver :: args,
- toIRType(tree.tpe))
- }
-
- /** Gen JS code for a new of a raw JS class (subclass of js.Any) */
- private def genPrimitiveJSNew(tree: Apply): js.Tree = {
- implicit val pos = tree.pos
-
- val Apply(fun @ Select(New(tpt), _), args0) = tree
- val cls = tpt.tpe.typeSymbol
- val ctor = fun.symbol
-
- genPrimitiveJSArgs(ctor, args0) match {
- case js.JSArrayConstr(args) =>
- if (cls == JSObjectClass && args.isEmpty) js.JSObjectConstr(Nil)
- else if (cls == JSArrayClass && args.isEmpty) js.JSArrayConstr(Nil)
- else js.JSNew(genPrimitiveJSClass(cls), args)
- case argArray =>
- genNewJSWithVarargs(genPrimitiveJSClass(cls), argArray)
- }
- }
-
- /** Gen JS code representing a JS class (subclass of js.Any) */
- private def genPrimitiveJSClass(sym: Symbol)(
- implicit pos: Position): js.Tree = {
- genGlobalJSObject(sym)
- }
-
- /** Gen JS code representing a JS module (var of the global scope) */
- private def genPrimitiveJSModule(sym: Symbol)(
- implicit pos: Position): js.Tree = {
- genGlobalJSObject(sym)
- }
-
- /** Gen JS code representing a JS object (class or module) in global scope
- */
- private def genGlobalJSObject(sym: Symbol)(
- implicit pos: Position): js.Tree = {
- jsNameOf(sym).split('.').foldLeft(genLoadGlobal()) { (memo, chunk) =>
- js.JSBracketSelect(memo, js.StringLiteral(chunk))
- }
- }
-
- /** Gen actual actual arguments to Scala method call.
- * Returns a list of the transformed arguments.
- *
- * This tries to optimized repeated arguments (varargs) by turning them
- * into js.WrappedArray instead of Scala wrapped arrays.
- */
- private def genActualArgs(sym: Symbol, args: List[Tree])(
- implicit pos: Position): List[js.Tree] = {
- val wereRepeated = exitingPhase(currentRun.typerPhase) {
- sym.tpe.params.map(p => isScalaRepeatedParamType(p.tpe))
- }
-
- if (wereRepeated.size > args.size) {
- // Should not happen, but let's not crash
- args.map(genExpr)
- } else {
- /* Arguments that are in excess compared to the type signature after
- * erasure are lambda-lifted arguments. They cannot be repeated, hence
- * the extension to `false`.
- */
- for ((arg, wasRepeated) <- args.zipAll(wereRepeated, EmptyTree, false)) yield {
- if (wasRepeated) {
- tryGenRepeatedParamAsJSArray(arg, handleNil = false).fold {
- genExpr(arg)
- } { argArray =>
- genNew(WrappedArrayClass, WrappedArray_ctor, List(argArray))
- }
- } else {
- genExpr(arg)
- }
- }
- }
- }
-
- /** Gen actual actual arguments to a primitive JS call
- * This handles repeated arguments (varargs) by turning them into
- * JS varargs, i.e., by expanding them into normal arguments.
- *
- * Returns an only tree which is a JS array of the arguments. In most
- * cases, it will be a js.JSArrayConstr with the expanded arguments. It will
- * not if a Seq is passed to a varargs argument with the syntax seq: _*.
- */
- private def genPrimitiveJSArgs(sym: Symbol, args: List[Tree])(
- implicit pos: Position): js.Tree = {
- val wereRepeated = exitingPhase(currentRun.typerPhase) {
- for {
- params <- sym.tpe.paramss
- param <- params
- } yield isScalaRepeatedParamType(param.tpe)
- }
-
- var reversedParts: List[js.Tree] = Nil
- var reversedPartUnderConstruction: List[js.Tree] = Nil
-
- def closeReversedPartUnderConstruction() = {
- if (!reversedPartUnderConstruction.isEmpty) {
- val part = reversedPartUnderConstruction.reverse
- reversedParts ::= js.JSArrayConstr(part)
- reversedPartUnderConstruction = Nil
- }
- }
-
- val paramTpes = enteringPhase(currentRun.posterasurePhase) {
- for (param <- sym.tpe.params)
- yield param.tpe
- }
-
- for (((arg, wasRepeated), tpe) <- (args zip wereRepeated) zip paramTpes) {
- if (wasRepeated) {
- genPrimitiveJSRepeatedParam(arg) match {
- case js.JSArrayConstr(jsArgs) =>
- reversedPartUnderConstruction =
- jsArgs reverse_::: reversedPartUnderConstruction
- case jsArgArray =>
- closeReversedPartUnderConstruction()
- reversedParts ::= jsArgArray
- }
- } else {
- val unboxedArg = genExpr(arg)
- val boxedArg = unboxedArg match {
- case js.UndefinedParam() => unboxedArg
- case _ => ensureBoxed(unboxedArg, tpe)
- }
- reversedPartUnderConstruction ::= boxedArg
- }
- }
- closeReversedPartUnderConstruction()
-
- // Find js.UndefinedParam at the end of the argument list. No check is
- // performed whether they may be there, since they will only be placed
- // where default arguments can be anyway
- reversedParts = reversedParts match {
- case Nil => Nil
- case js.JSArrayConstr(params) :: others =>
- val nparams =
- params.reverse.dropWhile(_.isInstanceOf[js.UndefinedParam]).reverse
- js.JSArrayConstr(nparams) :: others
- case parts => parts
- }
-
- // Find remaining js.UndefinedParam and replace by js.Undefined. This can
- // happen with named arguments or when multiple argument lists are present
- reversedParts = reversedParts map {
- case js.JSArrayConstr(params) =>
- val nparams = params map {
- case js.UndefinedParam() => js.Undefined()
- case param => param
- }
- js.JSArrayConstr(nparams)
- case part => part
- }
-
- reversedParts match {
- case Nil => js.JSArrayConstr(Nil)
- case List(part) => part
- case _ =>
- val partHead :: partTail = reversedParts.reverse
- js.JSBracketMethodApply(
- partHead, js.StringLiteral("concat"), partTail)
- }
- }
-
- /** Gen JS code for a repeated param of a primitive JS method
- * In this case `arg` has type Seq[T] for some T, but the result should
- * have type js.Array[T]. So this method takes care of the conversion.
- * It is specialized for the shapes of tree generated by the desugaring
- * of repeated params in Scala, so that these produce a js.JSArrayConstr.
- */
- private def genPrimitiveJSRepeatedParam(arg: Tree): js.Tree = {
- tryGenRepeatedParamAsJSArray(arg, handleNil = true) getOrElse {
- /* Fall back to calling runtime.genTraversableOnce2jsArray
- * to perform the conversion.
- */
- implicit val pos = arg.pos
- genApplyMethod(
- genLoadModule(RuntimePackageModule),
- RuntimePackageModule.moduleClass,
- Runtime_genTraversableOnce2jsArray,
- List(genExpr(arg)))
- }
- }
-
- /** Try and gen a js.Array for a repeated param (xs: T*).
- * It is specialized for the shapes of tree generated by the desugaring
- * of repeated params in Scala, so that these produce a js.JSArrayConstr.
- * If `arg` does not have the shape of a generated repeated param, this
- * method returns `None`.
- */
- private def tryGenRepeatedParamAsJSArray(arg: Tree,
- handleNil: Boolean): Option[js.Tree] = {
- implicit val pos = arg.pos
-
- // Given a method `def foo(args: T*)`
- arg match {
- // foo(arg1, arg2, ..., argN) where N > 0
- case MaybeAsInstanceOf(WrapArray(
- MaybeAsInstanceOf(ArrayValue(tpt, elems)))) =>
- /* Value classes in arrays are already boxed, so no need to use
- * the type before erasure.
- */
- val elemTpe = tpt.tpe
- Some(js.JSArrayConstr(elems.map(e => ensureBoxed(genExpr(e), elemTpe))))
-
- // foo()
- case Select(_, _) if handleNil && arg.symbol == NilModule =>
- Some(js.JSArrayConstr(Nil))
-
- // foo(argSeq:_*) - cannot be optimized
- case _ =>
- None
- }
- }
-
- object MaybeAsInstanceOf {
- def unapply(tree: Tree): Some[Tree] = tree match {
- case Apply(TypeApply(asInstanceOf_? @ Select(base, _), _), _)
- if asInstanceOf_?.symbol == Object_asInstanceOf =>
- Some(base)
- case _ =>
- Some(tree)
- }
- }
-
- object WrapArray {
- lazy val isWrapArray: Set[Symbol] = Seq(
- nme.wrapRefArray,
- nme.wrapByteArray,
- nme.wrapShortArray,
- nme.wrapCharArray,
- nme.wrapIntArray,
- nme.wrapLongArray,
- nme.wrapFloatArray,
- nme.wrapDoubleArray,
- nme.wrapBooleanArray,
- nme.wrapUnitArray,
- nme.genericWrapArray).map(getMemberMethod(PredefModule, _)).toSet
-
- def unapply(tree: Apply): Option[Tree] = tree match {
- case Apply(wrapArray_?, List(wrapped))
- if isWrapArray(wrapArray_?.symbol) =>
- Some(wrapped)
- case _ =>
- None
- }
- }
-
- // Synthesizers for raw JS functions ---------------------------------------
-
- /** Try and gen and record JS code for an anonymous function class.
- *
- * Returns true if the class could be rewritten that way, false otherwise.
- *
- * We make the following assumptions on the form of such classes:
- * - It is an anonymous function
- * - Includes being anonymous, final, and having exactly one constructor
- * - It is not a PartialFunction
- * - It has no field other than param accessors
- * - It has exactly one constructor
- * - It has exactly one non-bridge method apply if it is not specialized,
- * or a method apply$...$sp and a forwarder apply if it is specialized.
- * - As a precaution: it is synthetic
- *
- * From a class looking like this:
- *
- * final class <anon>(outer, capture1, ..., captureM) extends AbstractionFunctionN[...] {
- * def apply(param1, ..., paramN) = {
- * <body>
- * }
- * }
- * new <anon>(o, c1, ..., cM)
- *
- * we generate a function maker that emits:
- *
- * lambda<o, c1, ..., cM>[notype](
- * outer, capture1, ..., captureM, param1, ..., paramN) {
- * <body>
- * }
- *
- * so that, at instantiation point, we can write:
- *
- * new AnonFunctionN(functionMaker(this, captured1, ..., capturedM))
- *
- * Trickier things apply when the function is specialized.
- */
- private def tryGenAndRecordAnonFunctionClass(cd: ClassDef): Boolean = {
- implicit val pos = cd.pos
- val sym = cd.symbol
- assert(sym.isAnonymousFunction,
- s"tryGenAndRecordAnonFunctionClass called with non-anonymous function $cd")
-
- withScopedVars(
- currentClassInfoBuilder := new ClassInfoBuilder(sym.asClass),
- currentClassSym := sym
- ) {
- val (functionMakerBase, arity) =
- tryGenAndRecordAnonFunctionClassGeneric(cd) { msg =>
- return false
- }
- val functionMaker = { capturedArgs: List[js.Tree] =>
- JSFunctionToScala(functionMakerBase(capturedArgs), arity)
- }
-
- translatedAnonFunctions +=
- sym -> (functionMaker, currentClassInfoBuilder.get)
- }
- true
- }
-
- /** Constructor and extractor object for a tree that converts a JavaScript
- * function into a Scala function.
- */
- private object JSFunctionToScala {
- private val AnonFunPrefScala =
- "scala.scalajs.runtime.AnonFunction"
- private val AnonFunPrefJS =
- "sjsr_AnonFunction"
-
- def apply(jsFunction: js.Tree, arity: Int)(
- implicit pos: Position): js.Tree = {
- val clsSym = getRequiredClass(AnonFunPrefScala + arity)
- val ctor = clsSym.tpe.member(nme.CONSTRUCTOR)
- genNew(clsSym, ctor, List(jsFunction))
- }
-
- def unapply(tree: js.New): Option[(js.Tree, Int)] = tree match {
- case js.New(jstpe.ClassType(wrapperName), _, List(fun))
- if wrapperName.startsWith(AnonFunPrefJS) =>
- val arityStr = wrapperName.substring(AnonFunPrefJS.length)
- try {
- Some((fun, arityStr.toInt))
- } catch {
- case e: NumberFormatException => None
- }
-
- case _ =>
- None
- }
- }
-
- /** Gen and record JS code for a raw JS function class.
- *
- * This is called when emitting a ClassDef that represents an anonymous
- * class extending `js.FunctionN`. These are generated by the SAM
- * synthesizer when the target type is a `js.FunctionN`. Since JS
- * functions are not classes, we deconstruct the ClassDef, then
- * reconstruct it to be a genuine Closure.
- *
- * Compared to `tryGenAndRecordAnonFunctionClass()`, this function must
- * always succeed, because we really cannot afford keeping them as
- * anonymous classes. The good news is that it can do so, because the
- * body of SAM lambdas is hoisted in the enclosing class. Hence, the
- * apply() method is just a forwarder to calling that hoisted method.
- *
- * From a class looking like this:
- *
- * final class <anon>(outer, capture1, ..., captureM) extends js.FunctionN[...] {
- * def apply(param1, ..., paramN) = {
- * outer.lambdaImpl(param1, ..., paramN, capture1, ..., captureM)
- * }
- * }
- * new <anon>(o, c1, ..., cM)
- *
- * we generate a function maker that emits:
- *
- * lambda<o, c1, ..., cM>[notype](
- * outer, capture1, ..., captureM, param1, ..., paramN) {
- * outer.lambdaImpl(param1, ..., paramN, capture1, ..., captureM)
- * }
- *
- * The function maker is recorded in `translatedAnonFunctions` to be
- * fetched later by the translation for New.
- */
- def genAndRecordRawJSFunctionClass(cd: ClassDef): Unit = {
- val sym = cd.symbol
- assert(isRawJSFunctionDef(sym),
- s"genAndRecordRawJSFunctionClass called with non-JS function $cd")
-
- withScopedVars(
- currentClassInfoBuilder := new ClassInfoBuilder(sym.asClass),
- currentClassSym := sym
- ) {
- val (functionMaker, _) =
- tryGenAndRecordAnonFunctionClassGeneric(cd) { msg =>
- abort(s"Could not generate raw function maker for JS function: $msg")
- }
-
- translatedAnonFunctions +=
- sym -> (functionMaker, currentClassInfoBuilder.get)
- }
- }
-
- /** Code common to tryGenAndRecordAnonFunctionClass and
- * genAndRecordRawJSFunctionClass.
- */
- private def tryGenAndRecordAnonFunctionClassGeneric(cd: ClassDef)(
- fail: (=> String) => Nothing): (List[js.Tree] => js.Tree, Int) = {
- implicit val pos = cd.pos
- val sym = cd.symbol
-
- // First checks
-
- if (sym.isSubClass(PartialFunctionClass))
- fail(s"Cannot rewrite PartialFunction $cd")
- if (instantiatedAnonFunctions contains sym) {
- // when the ordering we're given is evil (it happens!)
- fail(s"Abort function rewrite because it was already instantiated: $cd")
- }
-
- // First step: find the apply method def, and collect param accessors
-
- var paramAccessors: List[Symbol] = Nil
- var applyDef: DefDef = null
-
- def gen(tree: Tree): Unit = {
- tree match {
- case EmptyTree => ()
- case Template(_, _, body) => body foreach gen
- case vd @ ValDef(mods, name, tpt, rhs) =>
- val fsym = vd.symbol
- if (!fsym.isParamAccessor)
- fail(s"Found field $fsym which is not a param accessor in anon function $cd")
-
- if (fsym.isPrivate) {
- paramAccessors ::= fsym
- } else {
- // Uh oh ... an inner something will try to access my fields
- fail(s"Found a non-private field $fsym in $cd")
- }
- case dd: DefDef =>
- val ddsym = dd.symbol
- if (ddsym.isClassConstructor) {
- if (!ddsym.isPrimaryConstructor)
- fail(s"Non-primary constructor $ddsym in anon function $cd")
- } else {
- val name = dd.name.toString
- if (name == "apply" || (ddsym.isSpecialized && name.startsWith("apply$"))) {
- if ((applyDef eq null) || ddsym.isSpecialized)
- applyDef = dd
- } else {
- // Found a method we cannot encode in the rewriting
- fail(s"Found a non-apply method $ddsym in $cd")
- }
- }
- case _ =>
- fail("Illegal tree in gen of genAndRecordAnonFunctionClass(): " + tree)
- }
- }
- gen(cd.impl)
- paramAccessors = paramAccessors.reverse // preserve definition order
-
- if (applyDef eq null)
- fail(s"Did not find any apply method in anon function $cd")
-
- withNewLocalNameScope {
- // Second step: build the list of useful constructor parameters
-
- val ctorParams = sym.primaryConstructor.tpe.params
-
- if (paramAccessors.size != ctorParams.size &&
- !(paramAccessors.size == ctorParams.size-1 &&
- ctorParams.head.unexpandedName == jsnme.arg_outer)) {
- fail(
- s"Have param accessors $paramAccessors but "+
- s"ctor params $ctorParams in anon function $cd")
- }
-
- val hasUnusedOuterCtorParam = paramAccessors.size != ctorParams.size
- val usedCtorParams =
- if (hasUnusedOuterCtorParam) ctorParams.tail
- else ctorParams
- val ctorParamDefs = usedCtorParams map { p =>
- // in the apply method's context
- js.ParamDef(encodeLocalSym(p)(p.pos), toIRType(p.tpe),
- mutable = false)(p.pos)
- }
-
- // Third step: emit the body of the apply method def
-
- val (applyMethod, methodInfoBuilder) = withScopedVars(
- paramAccessorLocals := (paramAccessors zip ctorParamDefs).toMap,
- tryingToGenMethodAsJSFunction := true
- ) {
- try {
- genMethodWithInfoBuilder(applyDef).getOrElse(
- abort(s"Oops, $applyDef did not produce a method"))
- } catch {
- case e: CancelGenMethodAsJSFunction =>
- fail(e.getMessage)
- }
- }
-
- withScopedVars(
- currentMethodInfoBuilder := methodInfoBuilder
- ) {
- // Fourth step: patch the body to unbox parameters and box result
-
- val js.MethodDef(_, params, _, body) = applyMethod
- val (patchedParams, patchedBody) =
- patchFunBodyWithBoxes(applyDef.symbol, params, body)
-
- // Fifth step: build the function maker
-
- val isThisFunction = JSThisFunctionClasses.exists(sym isSubClass _)
- assert(!isThisFunction || patchedParams.nonEmpty,
- s"Empty param list in ThisFunction: $cd")
-
- val functionMaker = { capturedArgs0: List[js.Tree] =>
- val capturedArgs =
- if (hasUnusedOuterCtorParam) capturedArgs0.tail
- else capturedArgs0
- assert(capturedArgs.size == ctorParamDefs.size)
-
- if (isThisFunction) {
- val thisParam :: actualParams = patchedParams
- js.Closure(
- ctorParamDefs,
- actualParams,
- js.Block(
- js.VarDef(thisParam.name, thisParam.ptpe, mutable = false,
- js.This()(thisParam.ptpe)(thisParam.pos))(thisParam.pos),
- patchedBody),
- capturedArgs)
- } else {
- js.Closure(ctorParamDefs, patchedParams, patchedBody, capturedArgs)
- }
- }
-
- val arity = params.size
-
- (functionMaker, arity)
- }
- }
- }
-
- /** Generate JS code for an anonymous function
- *
- * Anonymous functions survive until the backend only under
- * -Ydelambdafy:method
- * and when they do, their body is always of the form
- * EnclosingClass.this.someMethod(arg1, ..., argN, capture1, ..., captureM)
- * where argI are the formal arguments of the lambda, and captureI are
- * local variables or the enclosing def.
- *
- * We translate them by instantiating scala.scalajs.runtime.AnonFunctionN
- * with a JS closure:
- *
- * new ScalaJS.c.sjsr_AnonFunctionN().init___xyz(
- * lambda<this, capture1, ..., captureM>(
- * _this, capture1, ..., captureM, arg1, ..., argN) {
- * _this.someMethod(arg1, ..., argN, capture1, ..., captureM)
- * }
- * )
- *
- * In addition, input params are unboxed before use, and the result of
- * someMethod() is boxed back.
- */
- private def genAnonFunction(originalFunction: Function): js.Tree = {
- implicit val pos = originalFunction.pos
- val Function(paramTrees, Apply(
- targetTree @ Select(receiver, _), allArgs0)) = originalFunction
-
- val target = targetTree.symbol
- val params = paramTrees.map(_.symbol)
-
- val allArgs = allArgs0 map genExpr
-
- val formalArgs = params map { p =>
- js.ParamDef(encodeLocalSym(p)(p.pos), toIRType(p.tpe),
- mutable = false)(p.pos)
- }
-
- val isInImplClass = target.owner.isImplClass
-
- def makeCaptures(actualCaptures: List[js.Tree]) = {
- (actualCaptures map { c => (c: @unchecked) match {
- case js.VarRef(ident, _) =>
- (js.ParamDef(ident, c.tpe, mutable = false)(c.pos),
- js.VarRef(ident, false)(c.tpe)(c.pos))
- }}).unzip
- }
-
- val (allFormalCaptures, body, allActualCaptures) = if (!isInImplClass) {
- val thisActualCapture = genExpr(receiver)
- val thisFormalCapture = js.ParamDef(
- freshLocalIdent("this")(receiver.pos),
- thisActualCapture.tpe, mutable = false)(receiver.pos)
- val thisCaptureArg = thisFormalCapture.ref
- val (actualArgs, actualCaptures) = allArgs.splitAt(formalArgs.size)
- val (formalCaptures, captureArgs) = makeCaptures(actualCaptures)
- val body = genApplyMethod(thisCaptureArg, receiver.tpe, target,
- actualArgs ::: captureArgs)
-
- (thisFormalCapture :: formalCaptures,
- body, thisActualCapture :: actualCaptures)
- } else {
- val (thisActualCapture :: actualArgs, actualCaptures) =
- allArgs.splitAt(formalArgs.size+1)
- val (thisFormalCapture :: formalCaptures, thisCaptureArg :: captureArgs) =
- makeCaptures(thisActualCapture :: actualCaptures)
- val body = genTraitImplApply(target,
- thisCaptureArg :: actualArgs ::: captureArgs)
-
- (thisFormalCapture :: formalCaptures,
- body, thisActualCapture :: actualCaptures)
- }
-
- val (patchedFormalArgs, patchedBody) =
- patchFunBodyWithBoxes(target, formalArgs, body)
- val closure = js.Closure(
- allFormalCaptures,
- patchedFormalArgs,
- patchedBody,
- allActualCaptures)
-
- JSFunctionToScala(closure, params.size)
- }
-
- private def patchFunBodyWithBoxes(methodSym: Symbol,
- params: List[js.ParamDef], body: js.Tree)(
- implicit pos: Position): (List[js.ParamDef], js.Tree) = {
- val methodType = enteringPhase(currentRun.posterasurePhase)(methodSym.tpe)
-
- val (patchedParams, paramsLocal) = (for {
- (param, paramSym) <- params zip methodType.params
- } yield {
- val paramTpe = enteringPhase(currentRun.posterasurePhase)(paramSym.tpe)
- val paramName = param.name
- val js.Ident(name, origName) = paramName
- val newOrigName = origName.getOrElse(name)
- val newNameIdent = freshLocalIdent(newOrigName)(paramName.pos)
- val patchedParam = js.ParamDef(newNameIdent, jstpe.AnyType,
- mutable = false)(param.pos)
- val paramLocal = js.VarDef(paramName, param.ptpe, mutable = false,
- fromAny(patchedParam.ref, paramTpe))
- (patchedParam, paramLocal)
- }).unzip
-
- val patchedBody = js.Block(
- paramsLocal :+ ensureBoxed(body, methodType.resultType))
-
- (patchedParams, patchedBody)
- }
-
- // Utilities ---------------------------------------------------------------
-
- /** Generate a literal "zero" for the requested type */
- def genZeroOf(tpe: Type)(implicit pos: Position): js.Tree = toTypeKind(tpe) match {
- case VOID => abort("Cannot call genZeroOf(VOID)")
- case BOOL => js.BooleanLiteral(false)
- case LONG => js.LongLiteral(0L)
- case INT(_) => js.IntLiteral(0)
- case FloatKind => js.FloatLiteral(0.0f)
- case DoubleKind => js.DoubleLiteral(0.0)
- case _ => js.Null()
- }
-
- /** Generate loading of a module value
- * Can be given either the module symbol, or its module class symbol.
- */
- def genLoadModule(sym0: Symbol)(implicit pos: Position): js.Tree = {
- require(sym0.isModuleOrModuleClass,
- "genLoadModule called with non-module symbol: " + sym0)
- val sym1 = if (sym0.isModule) sym0.moduleClass else sym0
- val sym = // redirect all static methods of String to RuntimeString
- if (sym1 == StringModule) RuntimeStringModule.moduleClass
- else sym1
-
- val isGlobalScope = sym.tpe.typeSymbol isSubClass JSGlobalScopeClass
-
- if (isGlobalScope) genLoadGlobal()
- else if (isRawJSType(sym.tpe)) genPrimitiveJSModule(sym)
- else {
- if (!foreignIsImplClass(sym))
- currentMethodInfoBuilder.accessesModule(sym)
- js.LoadModule(jstpe.ClassType(encodeClassFullName(sym)))
- }
- }
-
- /** Gen JS code to load the global scope. */
- private def genLoadGlobal()(implicit pos: Position): js.Tree =
- js.JSBracketSelect(js.JSEnvInfo(), js.StringLiteral("global"))
-
- /** Generate access to a static member */
- private def genStaticMember(sym: Symbol)(implicit pos: Position) = {
- /* Actually, there is no static member in Scala.js. If we come here, that
- * is because we found the symbol in a Java-emitted .class in the
- * classpath. But the corresponding implementation in Scala.js will
- * actually be a val in the companion module.
- * We cannot use the .class files produced by our reimplementations of
- * these classes (in which the symbol would be a Scala accessor) because
- * that crashes the rest of scalac (at least for some choice symbols).
- * Hence we cheat here.
- */
- import scalaPrimitives._
- import jsPrimitives._
- if (isPrimitive(sym)) {
- getPrimitive(sym) match {
- case UNITVAL => js.Undefined()
- case UNITTYPE => genClassConstant(UnitTpe)
- }
- } else {
- val instance = genLoadModule(sym.owner)
- val method = encodeStaticMemberSym(sym)
- currentMethodInfoBuilder.callsMethod(sym.owner, method)
- js.Apply(instance, method, Nil)(toIRType(sym.tpe))
- }
- }
-
- /** Generate a Class[_] value (e.g. coming from classOf[T]) */
- private def genClassConstant(tpe: Type)(implicit pos: Position): js.Tree = {
- val refType = toReferenceType(tpe)
- currentMethodInfoBuilder.accessesClassData(refType)
- js.ClassOf(refType)
- }
- }
-
- /** Tests whether the given type represents a raw JavaScript type,
- * i.e., whether it extends scala.scalajs.js.Any.
- */
- def isRawJSType(tpe: Type): Boolean =
- tpe.typeSymbol.annotations.find(_.tpe =:= RawJSTypeAnnot.tpe).isDefined
-
- /** Test whether `sym` is the symbol of a raw JS function definition */
- private def isRawJSFunctionDef(sym: Symbol): Boolean =
- sym.isAnonymousClass && AllJSFunctionClasses.exists(sym isSubClass _)
-
- private def isRawJSCtorDefaultParam(sym: Symbol) = {
- sym.hasFlag(reflect.internal.Flags.DEFAULTPARAM) &&
- sym.owner.isModuleClass &&
- isRawJSType(patchedLinkedClassOfClass(sym.owner).tpe) &&
- nme.defaultGetterToMethod(sym.name) == nme.CONSTRUCTOR
- }
-
- private def patchedLinkedClassOfClass(sym: Symbol): Symbol = {
- /* Work around a bug of scalac with linkedClassOfClass where package
- * objects are involved (the companion class would somehow exist twice
- * in the scope, making an assertion fail in Symbol.suchThat).
- * Basically this inlines linkedClassOfClass up to companionClass,
- * then replaces the `suchThat` by a `filter` and `head`.
- */
- val flatOwnerInfo = {
- // inline Symbol.flatOwnerInfo because it is protected
- if (sym.needsFlatClasses)
- sym.info
- sym.owner.rawInfo
- }
- val result = flatOwnerInfo.decl(sym.name).filter(_ isCoDefinedWith sym)
- if (!result.isOverloaded) result
- else result.alternatives.head
- }
-
- private def isStringType(tpe: Type): Boolean =
- tpe.typeSymbol == StringClass
-
- private def isLongType(tpe: Type): Boolean =
- tpe.typeSymbol == LongClass
-
- private lazy val BoxedBooleanClass = boxedClass(BooleanClass)
- private lazy val BoxedByteClass = boxedClass(ByteClass)
- private lazy val BoxedShortClass = boxedClass(ShortClass)
- private lazy val BoxedIntClass = boxedClass(IntClass)
- private lazy val BoxedLongClass = boxedClass(LongClass)
- private lazy val BoxedFloatClass = boxedClass(FloatClass)
- private lazy val BoxedDoubleClass = boxedClass(DoubleClass)
-
- private lazy val NumberClass = requiredClass[java.lang.Number]
-
- private lazy val HijackedNumberClasses =
- Seq(BoxedByteClass, BoxedShortClass, BoxedIntClass, BoxedLongClass,
- BoxedFloatClass, BoxedDoubleClass)
- private lazy val HijackedBoxedClasses =
- Seq(BoxedUnitClass, BoxedBooleanClass) ++ HijackedNumberClasses
-
- protected lazy val isHijackedBoxedClass: Set[Symbol] =
- HijackedBoxedClasses.toSet
-
- private lazy val InlineAnnotationClass = requiredClass[scala.inline]
-
- private def isMaybeJavaScriptException(tpe: Type) =
- JavaScriptExceptionClass isSubClass tpe.typeSymbol
-
- /** Get JS name of Symbol if it was specified with JSName annotation, or
- * infers a default from the Scala name. */
- def jsNameOf(sym: Symbol): String =
- sym.getAnnotation(JSNameAnnotation).flatMap(_.stringArg(0)).getOrElse(
- sym.unexpandedName.decoded)
-
- def isStaticModule(sym: Symbol): Boolean =
- sym.isModuleClass && !sym.isImplClass && !sym.isLifted
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSExports.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSExports.scala
deleted file mode 100644
index 92dc26b..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSExports.scala
+++ /dev/null
@@ -1,751 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Sébastien Doeraene
- */
-
-package scala.scalajs.compiler
-
-import scala.collection.mutable
-
-import scala.tools.nsc._
-import scala.math.PartialOrdering
-import scala.reflect.internal.Flags
-
-import scala.scalajs.ir
-import ir.{Trees => js, Types => jstpe}
-
-import util.ScopedVar
-import ScopedVar.withScopedVars
-
-/** Generation of exports for JavaScript
- *
- * @author Sébastien Doeraene
- */
-trait GenJSExports extends SubComponent { self: GenJSCode =>
- import global._
- import jsAddons._
- import definitions._
- import jsDefinitions._
-
- trait JSExportsPhase { this: JSCodePhase =>
-
- /**
- * Generate exporter methods for a class
- * @param classSym symbol of class we export for
- * @param decldExports symbols exporter methods that have been encountered in
- * the class' tree. This is not the same as classSym.info.delcs since
- * inherited concrete methods from traits should be in this param, too
- */
- def genMemberExports(
- classSym: Symbol,
- decldExports: List[Symbol]): List[js.Tree] = {
-
- val newlyDecldExports = decldExports.filterNot { isOverridingExport _ }
- val newlyDecldExportNames =
- newlyDecldExports.map(_.name.toTermName).toList.distinct
-
- newlyDecldExportNames map { genMemberExport(classSym, _) }
- }
-
- def genConstructorExports(classSym: Symbol): List[js.ConstructorExportDef] = {
- val constructors = classSym.tpe.member(nme.CONSTRUCTOR).alternatives
-
- // Generate exports from constructors and their annotations
- val ctorExports = for {
- ctor <- constructors
- exp <- jsInterop.exportsOf(ctor)
- } yield (exp, ctor)
-
- val exports = for {
- (jsName, specs) <- ctorExports.groupBy(_._1.jsName) // group by exported name
- } yield {
- val (namedExports, normalExports) = specs.partition(_._1.isNamed)
-
- val normalCtors = normalExports.map(s => ExportedSymbol(s._2))
- val namedCtors = for {
- (exp, ctor) <- namedExports
- } yield {
- implicit val pos = exp.pos
- ExportedBody(List(JSAnyTpe),
- genNamedExporterBody(ctor, genFormalArg(1).ref),
- nme.CONSTRUCTOR.toString, pos)
- }
-
- val ctors = normalCtors ++ namedCtors
-
- implicit val pos = ctors.head.pos
-
- val js.MethodDef(_, args, _, body) =
- withNewLocalNameScope(genExportMethod(ctors, jsName))
-
- js.ConstructorExportDef(jsName, args, body)
- }
-
- exports.toList
- }
-
- def genModuleAccessorExports(classSym: Symbol): List[js.ModuleExportDef] = {
- for {
- exp <- jsInterop.exportsOf(classSym)
- } yield {
- implicit val pos = exp.pos
-
- if (exp.isNamed)
- reporter.error(pos, "You may not use @JSNamedExport on an object")
-
- js.ModuleExportDef(exp.jsName)
- }
- }
-
- /** Generate the exporter proxy for a named export */
- def genNamedExporterDef(dd: DefDef): js.MethodDef = {
- implicit val pos = dd.pos
-
- val sym = dd.symbol
-
- val Block(Apply(fun, _) :: Nil, _) = dd.rhs
- val trgSym = fun.symbol
-
- val inArg =
- js.ParamDef(js.Ident("namedParams"), jstpe.AnyType, mutable = false)
- val inArgRef = inArg.ref
-
- val methodIdent = encodeMethodSym(sym)
-
- withScopedVars(
- currentMethodInfoBuilder :=
- currentClassInfoBuilder.addMethod(methodIdent.name)
- ) {
- js.MethodDef(methodIdent, List(inArg), toIRType(sym.tpe.resultType),
- genNamedExporterBody(trgSym, inArg.ref))(None)
- }
- }
-
- private def genNamedExporterBody(trgSym: Symbol, inArg: js.Tree)(
- implicit pos: Position) = {
-
- if (hasRepeatedParam(trgSym)) {
- reporter.error(pos,
- "You may not name-export a method with a *-parameter")
- }
-
- val jsArgs = for {
- (pSym, index) <- trgSym.info.params.zipWithIndex
- } yield {
- val rhs = js.JSBracketSelect(inArg,
- js.StringLiteral(pSym.name.decoded))
- js.VarDef(js.Ident("namedArg$" + index), jstpe.AnyType,
- mutable = false, rhs = rhs)
- }
-
- val jsArgRefs = jsArgs.map(_.ref)
-
- // Generate JS code to prepare arguments (default getters and unboxes)
- val jsArgPrep = genPrepareArgs(jsArgRefs, trgSym)
- val jsResult = genResult(trgSym, jsArgPrep.map(_.ref))
-
- js.Block(jsArgs ++ jsArgPrep :+ jsResult)
- }
-
- private def genMemberExport(classSym: Symbol, name: TermName): js.Tree = {
- val alts = classSym.info.member(name).alternatives
-
- assert(!alts.isEmpty,
- s"Ended up with no alternatives for ${classSym.fullName}::$name. " +
- s"Original set was ${alts} with types ${alts.map(_.tpe)}")
-
- val (jsName, isProp) = jsInterop.jsExportInfo(name)
-
- // Check if we have a conflicting export of the other kind
- val conflicting =
- classSym.info.member(jsInterop.scalaExportName(jsName, !isProp))
-
- if (conflicting != NoSymbol) {
- val kind = if (isProp) "property" else "method"
- val alts = conflicting.alternatives
-
- reporter.error(alts.head.pos,
- s"Exported $kind $jsName conflicts with ${alts.head.fullName}")
- }
-
- withNewLocalNameScope {
- if (isProp)
- genExportProperty(alts, jsName)
- else
- genExportMethod(alts.map(ExportedSymbol), jsName)
- }
- }
-
- private def genExportProperty(alts: List[Symbol], jsName: String) = {
- assert(!alts.isEmpty)
- implicit val pos = alts.head.pos
-
- // Separate getters and setters. Somehow isJSGetter doesn't work here. Hence
- // we just check the parameter list length.
- val (getter, setters) = alts.partition(_.tpe.params.isEmpty)
-
- // if we have more than one getter, something went horribly wrong
- assert(getter.size <= 1,
- s"Found more than one getter to export for name ${jsName}.")
-
- val getTree =
- if (getter.isEmpty) js.EmptyTree
- else genApplyForSym(getter.head)
-
- val setTree =
- if (setters.isEmpty) js.EmptyTree
- else genExportSameArgc(setters.map(ExportedSymbol), 0) // we only have 1 argument
-
- js.PropertyDef(js.StringLiteral(jsName), getTree, genFormalArg(1), setTree)
- }
-
- /** generates the exporter function (i.e. exporter for non-properties) for
- * a given name */
- private def genExportMethod(alts0: List[Exported], jsName: String) = {
- assert(alts0.nonEmpty,
- "need at least one alternative to generate exporter method")
-
- implicit val pos = alts0.head.pos
-
- val alts = {
- // toString() is always exported. We might need to add it here
- // to get correct overloading.
- if (jsName == "toString" && alts0.forall(_.params.nonEmpty))
- ExportedSymbol(Object_toString) :: alts0
- else
- alts0
- }
-
- // Factor out methods with variable argument lists. Note that they can
- // only be at the end of the lists as enforced by PrepJSExports
- val (varArgMeths, normalMeths) = alts.partition(_.hasRepeatedParam)
-
- // Highest non-repeated argument count
- val maxArgc = (
- // We have argc - 1, since a repeated parameter list may also be empty
- // (unlike a normal parameter)
- varArgMeths.map(_.params.size - 1) ++
- normalMeths.map(_.params.size)
- ).max
-
- val formalArgs = genFormalArgs(maxArgc)
-
- // Calculates possible arg counts for normal method
- def argCounts(ex: Exported) = ex match {
- case ExportedSymbol(sym) =>
- val params = sym.tpe.params
- // Find default param
- val dParam = params.indexWhere { _.hasFlag(Flags.DEFAULTPARAM) }
- if (dParam == -1) Seq(params.size)
- else dParam to params.size
- case ex: ExportedBody =>
- List(ex.params.size)
- }
-
- // Generate tuples (argc, method)
- val methodArgCounts = {
- // Normal methods
- for {
- method <- normalMeths
- argc <- argCounts(method)
- } yield (argc, method)
- } ++ {
- // Repeated parameter methods
- for {
- method <- varArgMeths
- argc <- method.params.size - 1 to maxArgc
- } yield (argc, method)
- }
-
- // Create a map: argCount -> methods (methods may appear multiple times)
- val methodByArgCount =
- methodArgCounts.groupBy(_._1).mapValues(_.map(_._2).toSet)
-
- // Create tuples: (methods, argCounts). This will be the cases we generate
- val caseDefinitions =
- methodByArgCount.groupBy(_._2).mapValues(_.keySet)
-
- // Verify stuff about caseDefinitions
- assert({
- val argcs = caseDefinitions.values.flatten.toList
- argcs == argcs.distinct &&
- argcs.forall(_ <= maxArgc)
- }, "every argc should appear only once and be lower than max")
-
- // Generate a case block for each (methods, argCounts) tuple
- val cases = for {
- (methods, argcs) <- caseDefinitions
- if methods.nonEmpty && argcs.nonEmpty
-
- // exclude default case we're generating anyways for varargs
- if methods != varArgMeths.toSet
-
- // body of case to disambiguates methods with current count
- caseBody =
- genExportSameArgc(methods.toList, 0, Some(argcs.min))
-
- // argc in reverse order
- argcList = argcs.toList.sortBy(- _)
- } yield (argcList.map(js.IntLiteral(_)), caseBody)
-
- val hasVarArg = varArgMeths.nonEmpty
-
- def defaultCase = {
- if (!hasVarArg)
- genThrowTypeError()
- else
- genExportSameArgc(varArgMeths, 0)
- }
-
- val body = {
- if (cases.isEmpty)
- defaultCase
- else if (cases.size == 1 && !hasVarArg)
- cases.head._2
- else {
- js.Match(
- js.Unbox(js.JSBracketSelect(
- js.VarRef(js.Ident("arguments"), false)(jstpe.AnyType),
- js.StringLiteral("length")),
- 'I'),
- cases.toList, defaultCase)(jstpe.AnyType)
- }
- }
-
- js.MethodDef(js.StringLiteral(jsName), formalArgs, jstpe.AnyType, body)(None)
- }
-
- /**
- * Resolve method calls to [[alts]] while assuming they have the same
- * parameter count.
- * @param alts Alternative methods
- * @param paramIndex Index where to start disambiguation
- * @param maxArgc only use that many arguments
- */
- private def genExportSameArgc(alts: List[Exported],
- paramIndex: Int, maxArgc: Option[Int] = None): js.Tree = {
-
- implicit val pos = alts.head.pos
-
- if (alts.size == 1)
- alts.head.body
- else if (maxArgc.exists(_ <= paramIndex) ||
- !alts.exists(_.params.size > paramIndex)) {
- // We reach here in three cases:
- // 1. The parameter list has been exhausted
- // 2. The optional argument count restriction has triggered
- // 3. We only have (more than once) repeated parameters left
- // Therefore, we should fail
- reporter.error(pos,
- s"""Cannot disambiguate overloads for exported method ${alts.head.name} with types
- | ${alts.map(_.typeInfo).mkString("\n ")}""".stripMargin)
- js.Undefined()
- } else {
-
- val altsByTypeTest = groupByWithoutHashCode(alts) {
- case ExportedSymbol(alt) =>
- // get parameter type while resolving repeated params
- val tpe = enteringPhase(currentRun.uncurryPhase) {
- val ps = alt.paramss.flatten
- if (ps.size <= paramIndex || isRepeated(ps(paramIndex))) {
- assert(isRepeated(ps.last))
- repeatedToSingle(ps.last.tpe)
- } else {
- enteringPhase(currentRun.posterasurePhase) {
- ps(paramIndex).tpe
- }
- }
- }
-
- typeTestForTpe(tpe)
-
- case ex: ExportedBody =>
- typeTestForTpe(ex.params(paramIndex))
- }
-
- if (altsByTypeTest.size == 1) {
- // Testing this parameter is not doing any us good
- genExportSameArgc(alts, paramIndex+1, maxArgc)
- } else {
- // Sort them so that, e.g., isInstanceOf[String]
- // comes before isInstanceOf[Object]
- val sortedAltsByTypeTest = topoSortDistinctsBy(
- altsByTypeTest)(_._1)(RTTypeTest.Ordering)
-
- val defaultCase = genThrowTypeError()
-
- sortedAltsByTypeTest.foldRight[js.Tree](defaultCase) { (elem, elsep) =>
- val (typeTest, subAlts) = elem
- implicit val pos = subAlts.head.pos
-
- val param = genFormalArg(paramIndex+1)
- val genSubAlts = genExportSameArgc(subAlts, paramIndex+1, maxArgc)
-
- def hasDefaultParam = subAlts.exists {
- case ExportedSymbol(p) =>
- val params = p.tpe.params
- params.size > paramIndex &&
- params(paramIndex).hasFlag(Flags.DEFAULTPARAM)
- case _: ExportedBody => false
- }
-
- val optCond = typeTest match {
- case HijackedTypeTest(boxedClassName, _) =>
- Some(js.IsInstanceOf(param.ref, jstpe.ClassType(boxedClassName)))
-
- case InstanceOfTypeTest(tpe) =>
- Some(genIsInstanceOf(param.ref, tpe))
-
- case NoTypeTest =>
- None
- }
-
- optCond.fold[js.Tree] {
- genSubAlts // note: elsep is discarded, obviously
- } { cond =>
- val condOrUndef = if (!hasDefaultParam) cond else {
- js.If(cond, js.BooleanLiteral(true),
- js.BinaryOp(js.BinaryOp.===, param.ref, js.Undefined()))(
- jstpe.BooleanType)
- }
- js.If(condOrUndef, genSubAlts, elsep)(jstpe.AnyType)
- }
- }
- }
- }
- }
-
- /**
- * Generate a call to the method [[sym]] while using the formalArguments
- * and potentially the argument array. Also inserts default parameters if
- * required.
- */
- private def genApplyForSym(sym: Symbol): js.Tree = {
- implicit val pos = sym.pos
-
- // the (single) type of the repeated parameter if any
- val repeatedTpe = enteringPhase(currentRun.uncurryPhase) {
- for {
- param <- sym.paramss.flatten.lastOption
- if isRepeated(param)
- } yield repeatedToSingle(param.tpe)
- }
-
- val normalArgc = sym.tpe.params.size -
- (if (repeatedTpe.isDefined) 1 else 0)
-
- // optional repeated parameter list
- val jsVarArg = repeatedTpe map { tpe =>
- // Copy arguments that go to vararg into an array, put it in a wrapper
-
- val countIdent = freshLocalIdent("count")
- val count = js.VarRef(countIdent, mutable = false)(jstpe.IntType)
-
- val counterIdent = freshLocalIdent("i")
- val counter = js.VarRef(counterIdent, mutable = true)(jstpe.IntType)
-
- val arrayIdent = freshLocalIdent("varargs")
- val array = js.VarRef(arrayIdent, mutable = false)(jstpe.AnyType)
-
- val arguments = js.VarRef(js.Ident("arguments"),
- mutable = false)(jstpe.AnyType)
- val argLen = js.Unbox(
- js.JSBracketSelect(arguments, js.StringLiteral("length")), 'I')
- val argOffset = js.IntLiteral(normalArgc)
-
- val jsArrayCtor =
- js.JSBracketSelect(
- js.JSBracketSelect(js.JSEnvInfo(), js.StringLiteral("global")),
- js.StringLiteral("Array"))
-
- js.Block(
- // var i = 0
- js.VarDef(counterIdent, jstpe.IntType, mutable = true,
- rhs = js.IntLiteral(0)),
- // val count = arguments.length - <normalArgc>
- js.VarDef(countIdent, jstpe.IntType, mutable = false,
- rhs = js.BinaryOp(js.BinaryOp.Int_-, argLen, argOffset)),
- // val varargs = new Array(count)
- js.VarDef(arrayIdent, jstpe.AnyType, mutable = false,
- rhs = js.JSNew(jsArrayCtor, List(count))),
- // while (i < count)
- js.While(js.BinaryOp(js.BinaryOp.Num_<, counter, count), js.Block(
- // varargs[i] = arguments[<normalArgc> + i];
- js.Assign(
- js.JSBracketSelect(array, counter),
- js.JSBracketSelect(arguments,
- js.BinaryOp(js.BinaryOp.Int_+, argOffset, counter))),
- // i = i + 1 (++i won't work, desugar eliminates it)
- js.Assign(counter, js.BinaryOp(js.BinaryOp.Int_+,
- counter, js.IntLiteral(1)))
- )),
- // new WrappedArray(varargs)
- genNew(WrappedArrayClass, WrappedArray_ctor, List(array))
- )
- }
-
- // normal arguments
- val jsArgs = genFormalArgs(normalArgc)
- val jsArgRefs = jsArgs.map(_.ref)
-
- // Generate JS code to prepare arguments (default getters and unboxes)
- val jsArgPrep = genPrepareArgs(jsArgRefs, sym)
- val jsResult = genResult(sym, jsArgPrep.map(_.ref) ++ jsVarArg)
-
- js.Block(jsArgPrep :+ jsResult)
- }
-
- /** Generate the necessary JavaScript code to prepare the arguments of an
- * exported method (unboxing and default parameter handling)
- */
- private def genPrepareArgs(jsArgs: List[js.VarRef], sym: Symbol)(
- implicit pos: Position): List[js.VarDef] = {
-
- val result = new mutable.ListBuffer[js.VarDef]
-
- val funTpe = enteringPhase(currentRun.posterasurePhase)(sym.tpe)
- for {
- (jsArg, (param, i)) <- jsArgs zip funTpe.params.zipWithIndex
- } yield {
- // Code to verify the type of the argument (if it is defined)
- val verifiedArg = {
- val tpePosterasure =
- enteringPhase(currentRun.posterasurePhase)(param.tpe)
- tpePosterasure match {
- case tpe if isPrimitiveValueType(tpe) =>
- val unboxed = makePrimitiveUnbox(jsArg, tpe)
- // Ensure we don't convert null to a primitive value type
- js.If(js.BinaryOp(js.BinaryOp.===, jsArg, js.Null()),
- genThrowTypeError(s"Found null, expected $tpe"),
- unboxed)(unboxed.tpe)
- case tpe: ErasedValueType =>
- val boxedClass = tpe.valueClazz
- val unboxMethod = boxedClass.derivedValueClassUnbox
- genApplyMethod(
- genAsInstanceOf(jsArg, tpe),
- boxedClass, unboxMethod, Nil)
- case tpe =>
- genAsInstanceOf(jsArg, tpe)
- }
- }
-
- // If argument is undefined and there is a default getter, call it
- val verifiedOrDefault = if (param.hasFlag(Flags.DEFAULTPARAM)) {
- js.If(js.BinaryOp(js.BinaryOp.===, jsArg, js.Undefined()), {
- val trgSym = {
- if (sym.isClassConstructor) sym.owner.companionModule.moduleClass
- else sym.owner
- }
- val defaultGetter = trgSym.tpe.member(
- nme.defaultGetterName(sym.name, i+1))
-
- assert(defaultGetter.exists,
- s"need default getter for method ${sym.fullName}")
- assert(!defaultGetter.isOverloaded)
-
- val trgTree = {
- if (sym.isClassConstructor) genLoadModule(trgSym)
- else js.This()(encodeClassType(trgSym))
- }
-
- // Pass previous arguments to defaultGetter
- genApplyMethod(trgTree, trgSym, defaultGetter,
- result.take(defaultGetter.tpe.params.size).toList.map(_.ref))
- }, {
- // Otherwise, unbox the argument
- verifiedArg
- })(verifiedArg.tpe)
- } else {
- // Otherwise, it is always the unboxed argument
- verifiedArg
- }
-
- result +=
- js.VarDef(js.Ident("prep"+jsArg.ident.name, jsArg.ident.originalName),
- verifiedOrDefault.tpe, mutable = false, verifiedOrDefault)
- }
-
- result.toList
- }
-
- /** Generate the final forwarding call to the exported method.
- * Attention: This method casts the arguments to the right type. The IR
- * checker will not detect if you pass in a wrongly typed argument.
- */
- private def genResult(sym: Symbol,
- args: List[js.Tree])(implicit pos: Position) = {
- val thisType =
- if (sym.owner == ObjectClass) jstpe.ClassType(ir.Definitions.ObjectClass)
- else encodeClassType(sym.owner)
- val call = genApplyMethod(js.This()(thisType), sym.owner, sym, args)
- ensureBoxed(call,
- enteringPhase(currentRun.posterasurePhase)(sym.tpe.resultType))
- }
-
- private sealed abstract class Exported {
- def pos: Position
- def params: List[Type]
- def body: js.Tree
- def name: String
- def typeInfo: String
- def hasRepeatedParam: Boolean
- }
-
- private case class ExportedSymbol(sym: Symbol) extends Exported {
- def pos: Position = sym.pos
- def params: List[Type] = sym.tpe.params.map(_.tpe)
- def body: js.Tree = genApplyForSym(sym)
- def name: String = sym.name.toString
- def typeInfo: String = sym.tpe.toString
- def hasRepeatedParam: Boolean = GenJSExports.this.hasRepeatedParam(sym)
- }
-
- private case class ExportedBody(params: List[Type], body: js.Tree,
- name: String, pos: Position) extends Exported {
- def typeInfo: String = params.mkString("(", ", ", ")")
- val hasRepeatedParam: Boolean = false
- }
- }
-
- private def isOverridingExport(sym: Symbol): Boolean = {
- lazy val osym = sym.nextOverriddenSymbol
- sym.isOverridingSymbol && !osym.owner.isInterface
- }
-
- private sealed abstract class RTTypeTest
-
- private final case class HijackedTypeTest(
- boxedClassName: String, rank: Int) extends RTTypeTest
-
- private final case class InstanceOfTypeTest(tpe: Type) extends RTTypeTest {
- override def equals(that: Any): Boolean = {
- that match {
- case InstanceOfTypeTest(thatTpe) => tpe =:= thatTpe
- case _ => false
- }
- }
- }
-
- private case object NoTypeTest extends RTTypeTest
-
- private object RTTypeTest {
- implicit object Ordering extends PartialOrdering[RTTypeTest] {
- override def tryCompare(lhs: RTTypeTest, rhs: RTTypeTest): Option[Int] = {
- if (lteq(lhs, rhs)) if (lteq(rhs, lhs)) Some(0) else Some(-1)
- else if (lteq(rhs, lhs)) Some(1) else None
- }
-
- override def lteq(lhs: RTTypeTest, rhs: RTTypeTest): Boolean = {
- (lhs, rhs) match {
- // NoTypeTest is always last
- case (_, NoTypeTest) => true
- case (NoTypeTest, _) => false
-
- case (HijackedTypeTest(_, rank1), HijackedTypeTest(_, rank2)) =>
- rank1 <= rank2
-
- case (InstanceOfTypeTest(t1), InstanceOfTypeTest(t2)) =>
- t1 <:< t2
-
- case (_:HijackedTypeTest, _:InstanceOfTypeTest) => true
- case (_:InstanceOfTypeTest, _:HijackedTypeTest) => false
- }
- }
-
- override def equiv(lhs: RTTypeTest, rhs: RTTypeTest): Boolean = {
- lhs == rhs
- }
- }
- }
-
- // Very simple O(n²) topological sort for elements assumed to be distinct
- private def topoSortDistinctsBy[A <: AnyRef, B](coll: List[A])(f: A => B)(
- implicit ord: PartialOrdering[B]): List[A] = {
-
- @scala.annotation.tailrec
- def loop(coll: List[A], acc: List[A]): List[A] = {
- if (coll.isEmpty) acc
- else if (coll.tail.isEmpty) coll.head :: acc
- else {
- val (lhs, rhs) = coll.span(x => !coll.forall(
- y => (x eq y) || !ord.lteq(f(x), f(y))))
- assert(!rhs.isEmpty, s"cycle while ordering $coll")
- loop(lhs ::: rhs.tail, rhs.head :: acc)
- }
- }
-
- loop(coll, Nil)
- }
-
- private def typeTestForTpe(tpe: Type): RTTypeTest = {
- tpe match {
- case tpe: ErasedValueType =>
- InstanceOfTypeTest(tpe.valueClazz.typeConstructor)
-
- case _ =>
- import ir.{Definitions => Defs}
- (toTypeKind(tpe): @unchecked) match {
- case VoidKind => HijackedTypeTest(Defs.BoxedUnitClass, 0)
- case BooleanKind => HijackedTypeTest(Defs.BoxedBooleanClass, 1)
- case ByteKind => HijackedTypeTest(Defs.BoxedByteClass, 2)
- case ShortKind => HijackedTypeTest(Defs.BoxedShortClass, 3)
- case IntKind => HijackedTypeTest(Defs.BoxedIntegerClass, 4)
- case FloatKind => HijackedTypeTest(Defs.BoxedFloatClass, 5)
- case DoubleKind => HijackedTypeTest(Defs.BoxedDoubleClass, 6)
-
- case CharKind => InstanceOfTypeTest(boxedClass(CharClass).tpe)
- case LongKind => InstanceOfTypeTest(boxedClass(LongClass).tpe)
-
- case REFERENCE(cls) =>
- if (cls == StringClass) HijackedTypeTest(Defs.StringClass, 7)
- else if (cls == ObjectClass) NoTypeTest
- else if (isRawJSType(tpe)) {
- cls match {
- case JSUndefinedClass => HijackedTypeTest(Defs.BoxedUnitClass, 0)
- case JSBooleanClass => HijackedTypeTest(Defs.BoxedBooleanClass, 1)
- case JSNumberClass => HijackedTypeTest(Defs.BoxedDoubleClass, 6)
- case JSStringClass => HijackedTypeTest(Defs.StringClass, 7)
- case _ => NoTypeTest
- }
- } else InstanceOfTypeTest(tpe)
-
- case ARRAY(_) => InstanceOfTypeTest(tpe)
- }
- }
- }
-
- // Group-by that does not rely on hashCode(), only equals() - O(n²)
- private def groupByWithoutHashCode[A, B](
- coll: List[A])(f: A => B): List[(B, List[A])] = {
-
- import scala.collection.mutable.ArrayBuffer
- val m = new ArrayBuffer[(B, List[A])]
- m.sizeHint(coll.length)
-
- for (elem <- coll) {
- val key = f(elem)
- val index = m.indexWhere(_._1 == key)
- if (index < 0) m += ((key, List(elem)))
- else m(index) = (key, elem :: m(index)._2)
- }
-
- m.toList
- }
-
- private def genThrowTypeError(msg: String = "No matching overload")(
- implicit pos: Position): js.Tree = {
- js.Throw(js.StringLiteral(msg))
- }
-
- private def genFormalArgs(count: Int)(implicit pos: Position): List[js.ParamDef] =
- (1 to count map genFormalArg).toList
-
- private def genFormalArg(index: Int)(implicit pos: Position): js.ParamDef =
- js.ParamDef(js.Ident("arg$" + index), jstpe.AnyType, mutable = false)
-
- private def hasRepeatedParam(sym: Symbol) =
- enteringPhase(currentRun.uncurryPhase) {
- sym.paramss.flatten.lastOption.exists(isRepeated _)
- }
-
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSFiles.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSFiles.scala
deleted file mode 100644
index f754e70..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/GenJSFiles.scala
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Sébastien Doeraene
- */
-
-package scala.scalajs.compiler
-
-import scala.tools.nsc._
-import scala.tools.nsc.io.AbstractFile
-import scala.reflect.internal.pickling.PickleBuffer
-
-import java.io._
-
-import scala.scalajs.ir
-import ir.Infos._
-
-/** Send JS ASTs to files
- *
- * @author Sébastien Doeraene
- */
-trait GenJSFiles extends SubComponent { self: GenJSCode =>
- import global._
- import jsAddons._
-
- def genIRFile(cunit: CompilationUnit, sym: Symbol, tree: ir.Trees.ClassDef,
- classInfo: ClassInfo): Unit = {
- val outfile = getFileFor(cunit, sym, ".sjsir")
- val output = outfile.bufferedOutput
- try {
- ir.InfoSerializers.serialize(output, classInfo)
- ir.Serializers.serialize(output, tree)
- } finally {
- output.close()
- }
- }
-
- private def getFileFor(cunit: CompilationUnit, sym: Symbol,
- suffix: String) = {
- val baseDir: AbstractFile =
- settings.outputDirs.outputDirFor(cunit.source.file)
-
- val pathParts = sym.fullName.split("[./]")
- val dir = (baseDir /: pathParts.init)(_.subdirectoryNamed(_))
-
- var filename = pathParts.last
- if (sym.isModuleClass && !sym.isImplClass)
- filename = filename + nme.MODULE_SUFFIX_STRING
-
- dir fileNamed (filename + suffix)
- }
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSDefinitions.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSDefinitions.scala
deleted file mode 100644
index b8a483a..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSDefinitions.scala
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Sébastien Doeraene
- */
-
-package scala.scalajs.compiler
-
-import scala.tools.nsc._
-
-/** Core definitions for Scala.js
- *
- * @author Sébastien Doeraene
- */
-trait JSDefinitions { self: JSGlobalAddons =>
- import global._
-
- object jsDefinitions extends JSDefinitionsClass
-
- import definitions._
- import rootMirror._
-
- class JSDefinitionsClass {
-
- lazy val ScalaJSJSPackage = getPackage(newTermNameCached("scala.scalajs.js")) // compat 2.10/2.11
- lazy val JSPackage_undefined = getMemberMethod(ScalaJSJSPackage, newTermName("undefined"))
- lazy val JSPackage_isUndefined = getMemberMethod(ScalaJSJSPackage, newTermName("isUndefined"))
- lazy val JSPackage_typeOf = getMemberMethod(ScalaJSJSPackage, newTermName("typeOf"))
- lazy val JSPackage_debugger = getMemberMethod(ScalaJSJSPackage, newTermName("debugger"))
- lazy val JSPackage_native = getMemberMethod(ScalaJSJSPackage, newTermName("native"))
-
- lazy val ScalaJSJSPrimPackage = getPackage(newTermNameCached("scala.scalajs.js.prim")) // compat 2.10/2.11
-
- lazy val JSAnyClass = getRequiredClass("scala.scalajs.js.Any")
- lazy val JSDynamicClass = getRequiredClass("scala.scalajs.js.Dynamic")
- lazy val JSDynamic_selectDynamic = getMemberMethod(JSDynamicClass, newTermName("selectDynamic"))
- lazy val JSDynamic_updateDynamic = getMemberMethod(JSDynamicClass, newTermName("updateDynamic"))
- lazy val JSDynamic_applyDynamic = getMemberMethod(JSDynamicClass, newTermName("applyDynamic"))
- lazy val JSDictionaryClass = getRequiredClass("scala.scalajs.js.Dictionary")
- lazy val JSDictionary_delete = getMemberMethod(JSDictionaryClass, newTermName("delete"))
- lazy val JSNumberClass = getRequiredClass("scala.scalajs.js.prim.Number")
- lazy val JSBooleanClass = getRequiredClass("scala.scalajs.js.prim.Boolean")
- lazy val JSStringClass = getRequiredClass("scala.scalajs.js.prim.String")
- lazy val JSUndefinedClass = getRequiredClass("scala.scalajs.js.prim.Undefined")
- lazy val JSObjectClass = getRequiredClass("scala.scalajs.js.Object")
- lazy val JSThisFunctionClass = getRequiredClass("scala.scalajs.js.ThisFunction")
-
- lazy val JSGlobalScopeClass = getRequiredClass("scala.scalajs.js.GlobalScope")
-
- lazy val UndefOrClass = getRequiredClass("scala.scalajs.js.UndefOr")
-
- lazy val JSArrayClass = getRequiredClass("scala.scalajs.js.Array")
- lazy val JSArray_apply = getMemberMethod(JSArrayClass, newTermName("apply"))
- lazy val JSArray_update = getMemberMethod(JSArrayClass, newTermName("update"))
-
- lazy val JSFunctionClasses = (0 to 22) map (n => getRequiredClass("scala.scalajs.js.Function"+n))
- lazy val JSThisFunctionClasses = (0 to 21) map (n => getRequiredClass("scala.scalajs.js.ThisFunction"+n))
- lazy val AllJSFunctionClasses = JSFunctionClasses ++ JSThisFunctionClasses
-
- lazy val RuntimeExceptionClass = requiredClass[RuntimeException]
- lazy val JavaScriptExceptionClass = getClassIfDefined("scala.scalajs.js.JavaScriptException")
-
- lazy val JSNameAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSName")
- lazy val JSBracketAccessAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSBracketAccess")
- lazy val JSExportAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSExport")
- lazy val JSExportDescendentObjectsAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSExportDescendentObjects")
- lazy val JSExportDescendentClassesAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSExportDescendentClasses")
- lazy val JSExportAllAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSExportAll")
- lazy val JSExportNamedAnnotation = getRequiredClass("scala.scalajs.js.annotation.JSExportNamed")
-
- lazy val JSAnyTpe = JSAnyClass.toTypeConstructor
- lazy val JSDynamicTpe = JSDynamicClass.toTypeConstructor
- lazy val JSNumberTpe = JSNumberClass.toTypeConstructor
- lazy val JSBooleanTpe = JSBooleanClass.toTypeConstructor
- lazy val JSStringTpe = JSStringClass.toTypeConstructor
- lazy val JSUndefinedTpe = JSUndefinedClass.toTypeConstructor
- lazy val JSObjectTpe = JSObjectClass.toTypeConstructor
-
- lazy val JSGlobalScopeTpe = JSGlobalScopeClass.toTypeConstructor
-
- lazy val JSFunctionTpes = JSFunctionClasses.map(_.toTypeConstructor)
-
- lazy val JSAnyModule = JSAnyClass.companionModule
- def JSAny_fromFunction(arity: Int) = getMemberMethod(JSAnyModule, newTermName("fromFunction"+arity))
-
- lazy val JSDynamicModule = JSDynamicClass.companionModule
- lazy val JSDynamic_newInstance = getMemberMethod(JSDynamicModule, newTermName("newInstance"))
- lazy val JSDynamicLiteral = getMemberModule(JSDynamicModule, newTermName("literal"))
- lazy val JSDynamicLiteral_applyDynamicNamed = getMemberMethod(JSDynamicLiteral, newTermName("applyDynamicNamed"))
- lazy val JSDynamicLiteral_applyDynamic = getMemberMethod(JSDynamicLiteral, newTermName("applyDynamic"))
-
- lazy val JSObjectModule = JSObjectClass.companionModule
- lazy val JSObject_hasProperty = getMemberMethod(JSObjectModule, newTermName("hasProperty"))
- lazy val JSObject_properties = getMemberMethod(JSObjectModule, newTermName("properties"))
-
- lazy val JSArrayModule = JSArrayClass.companionModule
- lazy val JSArray_create = getMemberMethod(JSArrayModule, newTermName("apply"))
-
- lazy val JSThisFunctionModule = JSThisFunctionClass.companionModule
- def JSThisFunction_fromFunction(arity: Int) = getMemberMethod(JSThisFunctionModule, newTermName("fromFunction"+arity))
-
- lazy val RawJSTypeAnnot = getClassIfDefined("scala.scalajs.js.annotation.RawJSType")
-
- lazy val RuntimeStringModule = getRequiredModule("scala.scalajs.runtime.RuntimeString")
- lazy val RuntimeStringModuleClass = RuntimeStringModule.moduleClass
-
- lazy val BooleanReflectiveCallClass = getRequiredClass("scala.scalajs.runtime.BooleanReflectiveCall")
- lazy val NumberReflectiveCallClass = getRequiredClass("scala.scalajs.runtime.NumberReflectiveCall")
- lazy val IntegerReflectiveCallClass = getRequiredClass("scala.scalajs.runtime.IntegerReflectiveCall")
-
- lazy val RuntimePackageModule = getPackageObject("scala.scalajs.runtime")
- lazy val Runtime_wrapJavaScriptException = getMemberMethod(RuntimePackageModule, newTermName("wrapJavaScriptException"))
- lazy val Runtime_unwrapJavaScriptException = getMemberMethod(RuntimePackageModule, newTermName("unwrapJavaScriptException"))
- lazy val Runtime_genTraversableOnce2jsArray = getMemberMethod(RuntimePackageModule, newTermName("genTraversableOnce2jsArray"))
- lazy val Runtime_newJSObjectWithVarargs = getMemberMethod(RuntimePackageModule, newTermName("newJSObjectWithVarargs"))
- lazy val Runtime_propertiesOf = getMemberMethod(RuntimePackageModule, newTermName("propertiesOf"))
-
- lazy val WrappedArrayClass = getRequiredClass("scala.scalajs.js.WrappedArray")
- lazy val WrappedArray_ctor = WrappedArrayClass.primaryConstructor
-
- // This is a def, since similar symbols (arrayUpdateMethod, etc.) are in runDefinitions
- // (rather than definitions) and we weren't sure if it is safe to make this a lazy val
- def ScalaRunTime_isArray = getMemberMethod(ScalaRunTimeModule, newTermName("isArray")).suchThat(_.tpe.params.size == 2)
-
- lazy val BoxesRunTime_boxToCharacter = getMemberMethod(BoxesRunTimeModule, newTermName("boxToCharacter"))
- lazy val BoxesRunTime_unboxToChar = getMemberMethod(BoxesRunTimeModule, newTermName("unboxToChar"))
-
- }
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala
deleted file mode 100644
index bc7f8be..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSEncoding.scala
+++ /dev/null
@@ -1,261 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Sébastien Doeraene
- */
-
-package scala.scalajs.compiler
-
-import scala.collection.mutable
-
-import scala.tools.nsc._
-
-import scala.scalajs.ir
-import ir.{Trees => js, Types => jstpe}
-
-import util.ScopedVar
-import ScopedVar.withScopedVars
-
-/** Encoding of symbol names for JavaScript
- *
- * Some issues that this encoding solves:
- * * Overloading: encode the full signature in the JS name
- * * Same scope for fields and methods of a class
- * * Global access to classes and modules (by their full name)
- *
- * @author Sébastien Doeraene
- */
-trait JSEncoding extends SubComponent { self: GenJSCode =>
- import global._
- import jsAddons._
-
- /** Outer separator string (between parameter types) */
- final val OuterSep = "__"
-
- /** Inner separator character (replace dots in full names) */
- final val InnerSep = "_"
-
- /** Name given to the local Scala.js environment variable */
- final val ScalaJSEnvironmentName = "ScalaJS"
-
- /** Name given to all exported stuff of a class for DCE */
- final val dceExportName = "<exported>"
-
- // Fresh local name generator ----------------------------------------------
-
- private val usedLocalNames = new ScopedVar[mutable.Set[String]]
- private val localSymbolNames = new ScopedVar[mutable.Map[Symbol, String]]
- private val isKeywordOrReserved =
- js.isKeyword ++ Seq("arguments", "eval", ScalaJSEnvironmentName)
-
- def withNewLocalNameScope[A](body: => A): A =
- withScopedVars(
- usedLocalNames := mutable.Set.empty,
- localSymbolNames := mutable.Map.empty
- )(body)
-
- private def freshName(base: String = "x"): String = {
- var suffix = 1
- var longName = base
- while (usedLocalNames(longName) || isKeywordOrReserved(longName)) {
- suffix += 1
- longName = base+"$"+suffix
- }
- usedLocalNames += longName
- longName
- }
-
- def freshLocalIdent()(implicit pos: ir.Position): js.Ident =
- js.Ident(freshName(), None)
-
- def freshLocalIdent(base: String)(implicit pos: ir.Position): js.Ident =
- js.Ident(freshName(base), Some(base))
-
- private def localSymbolName(sym: Symbol): String =
- localSymbolNames.getOrElseUpdate(sym, freshName(sym.name.toString))
-
- // Encoding methods ----------------------------------------------------------
-
- def encodeLabelSym(sym: Symbol)(implicit pos: Position): js.Ident = {
- require(sym.isLabel, "encodeLabelSym called with non-label symbol: " + sym)
- js.Ident(localSymbolName(sym), Some(sym.unexpandedName.decoded))
- }
-
- private lazy val allRefClasses: Set[Symbol] = {
- import definitions._
- (Set(ObjectRefClass, VolatileObjectRefClass) ++
- refClass.values ++ volatileRefClass.values)
- }
-
- def encodeFieldSym(sym: Symbol)(implicit pos: Position): js.Ident = {
- require(sym.owner.isClass && sym.isTerm && !sym.isMethod && !sym.isModule,
- "encodeFieldSym called with non-field symbol: " + sym)
-
- val name0 = encodeMemberNameInternal(sym)
- val name =
- if (name0.charAt(name0.length()-1) != ' ') name0
- else name0.substring(0, name0.length()-1)
-
- /* We have to special-case fields of Ref types (IntRef, ObjectRef, etc.)
- * because they are emitted as private by our .scala source files, but
- * they are considered public at use site since their symbols come from
- * Java-emitted .class files.
- */
- val idSuffix =
- if (sym.isPrivate || allRefClasses.contains(sym.owner))
- sym.owner.ancestors.count(!_.isInterface).toString
- else
- "f"
-
- val encodedName = name + "$" + idSuffix
- js.Ident(mangleJSName(encodedName), Some(sym.unexpandedName.decoded))
- }
-
- def encodeMethodSym(sym: Symbol, reflProxy: Boolean = false)
- (implicit pos: Position): js.Ident = {
- val (encodedName, paramsString) = encodeMethodNameInternal(sym, reflProxy)
- js.Ident(encodedName + paramsString,
- Some(sym.unexpandedName.decoded + paramsString))
- }
-
- def encodeMethodName(sym: Symbol, reflProxy: Boolean = false): String = {
- val (encodedName, paramsString) = encodeMethodNameInternal(sym, reflProxy)
- encodedName + paramsString
- }
-
- /** Encodes a method symbol of java.lang.String for use in RuntimeString.
- *
- * This basically means adding an initial parameter of type
- * java.lang.String, which is the `this` parameter.
- */
- def encodeRTStringMethodSym(sym: Symbol)(
- implicit pos: Position): (Symbol, js.Ident) = {
- require(sym.isMethod, "encodeMethodSym called with non-method symbol: " + sym)
- require(sym.owner == definitions.StringClass)
- require(!sym.isClassConstructor && !sym.isPrivate)
-
- val (encodedName, paramsString) =
- encodeMethodNameInternal(sym, inRTClass = true)
- val methodIdent = js.Ident(encodedName + paramsString,
- Some(sym.unexpandedName.decoded + paramsString))
-
- (jsDefinitions.RuntimeStringModuleClass, methodIdent)
- }
-
- private def encodeMethodNameInternal(sym: Symbol,
- reflProxy: Boolean = false,
- inRTClass: Boolean = false): (String, String) = {
- require(sym.isMethod, "encodeMethodSym called with non-method symbol: " + sym)
-
- def name = encodeMemberNameInternal(sym)
-
- val encodedName = {
- if (sym.isClassConstructor)
- "init" + InnerSep
- else if (foreignIsImplClass(sym.owner))
- encodeClassFullName(sym.owner) + OuterSep + name
- else if (sym.isPrivate)
- mangleJSName(name) + OuterSep + "p" +
- sym.owner.ancestors.count(!_.isInterface).toString
- else
- mangleJSName(name)
- }
-
- val paramsString = makeParamsString(sym, reflProxy, inRTClass)
-
- (encodedName, paramsString)
- }
-
- def encodeStaticMemberSym(sym: Symbol)(implicit pos: Position): js.Ident = {
- require(sym.isStaticMember,
- "encodeStaticMemberSym called with non-static symbol: " + sym)
- js.Ident(
- mangleJSName(encodeMemberNameInternal(sym)) +
- makeParamsString(List(internalName(sym.tpe))),
- Some(sym.unexpandedName.decoded))
- }
-
- def encodeLocalSym(sym: Symbol)(implicit pos: Position): js.Ident = {
- require(!sym.owner.isClass && sym.isTerm && !sym.isMethod && !sym.isModule,
- "encodeLocalSym called with non-local symbol: " + sym)
- js.Ident(mangleJSName(localSymbolName(sym)), Some(sym.unexpandedName.decoded))
- }
-
- def foreignIsImplClass(sym: Symbol): Boolean =
- sym.isModuleClass && nme.isImplClassName(sym.name)
-
- def encodeClassType(sym: Symbol): jstpe.Type = {
- if (sym == definitions.ObjectClass) jstpe.AnyType
- else if (isRawJSType(sym.toTypeConstructor)) jstpe.AnyType
- else {
- assert(sym != definitions.ArrayClass,
- "encodeClassType() cannot be called with ArrayClass")
- jstpe.ClassType(encodeClassFullName(sym))
- }
- }
-
- def encodeClassFullNameIdent(sym: Symbol)(implicit pos: Position): js.Ident = {
- js.Ident(encodeClassFullName(sym), Some(sym.fullName))
- }
-
- def encodeModuleFullNameIdent(sym: Symbol)(implicit pos: Position): js.Ident = {
- js.Ident(encodeModuleFullName(sym), Some(sym.fullName))
- }
-
- def encodeClassFullName(sym: Symbol): String = {
- ir.Definitions.encodeClassName(
- sym.fullName + (if (needsModuleClassSuffix(sym)) "$" else ""))
- }
-
- def needsModuleClassSuffix(sym: Symbol): Boolean =
- sym.isModuleClass && !foreignIsImplClass(sym)
-
- def encodeModuleFullName(sym: Symbol): String =
- ir.Definitions.encodeClassName(sym.fullName + "$").dropRight(1)
-
- private def encodeMemberNameInternal(sym: Symbol): String =
- sym.name.toString.replace("_", "$und")
-
- // Encoding of method signatures
-
- private def makeParamsString(sym: Symbol, reflProxy: Boolean,
- inRTClass: Boolean): String = {
- val tpe = sym.tpe
- val paramTypeNames = tpe.params map (p => internalName(p.tpe))
- val paramAndResultTypeNames = {
- if (sym.isClassConstructor)
- paramTypeNames
- else if (reflProxy)
- paramTypeNames :+ ""
- else {
- val paramAndResultTypeNames0 =
- paramTypeNames :+ internalName(tpe.resultType)
- if (!inRTClass) paramAndResultTypeNames0
- else internalName(sym.owner.toTypeConstructor) +: paramAndResultTypeNames0
- }
- }
- makeParamsString(paramAndResultTypeNames)
- }
-
- private def makeParamsString(paramAndResultTypeNames: List[String]) =
- paramAndResultTypeNames.mkString(OuterSep, OuterSep, "")
-
- /** Computes the internal name for a type. */
- private def internalName(tpe: Type): String = internalName(toTypeKind(tpe))
-
- private def internalName(kind: TypeKind): String = kind match {
- case VOID => "V"
- case kind: ValueTypeKind => kind.primitiveCharCode.toString()
- case NOTHING => ir.Definitions.RuntimeNothingClass
- case NULL => ir.Definitions.RuntimeNullClass
- case REFERENCE(cls) => encodeClassFullName(cls)
- case ARRAY(elem) => "A"+internalName(elem)
- }
-
- /** mangles names that are illegal in JavaScript by prepending a $
- * also mangles names that would collide with these mangled names
- */
- private def mangleJSName(name: String) =
- if (js.isKeyword(name) || name(0).isDigit || name(0) == '$')
- "$" + name
- else name
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSGlobalAddons.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSGlobalAddons.scala
deleted file mode 100644
index 3621050..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSGlobalAddons.scala
+++ /dev/null
@@ -1,244 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Sébastien Doeraene
- */
-
-package scala.scalajs.compiler
-
-import scala.tools.nsc._
-
-import scala.collection.mutable
-
-/** Additions to Global meaningful for the JavaScript backend
- *
- * @author Sébastien Doeraene
- */
-trait JSGlobalAddons extends JSDefinitions
- with Compat210Component {
- val global: Global
-
- import global._
- import jsDefinitions._
- import definitions._
-
- /** JavaScript primitives, used in jscode */
- object jsPrimitives extends JSPrimitives {
- val global: JSGlobalAddons.this.global.type = JSGlobalAddons.this.global
- val jsAddons: ThisJSGlobalAddons =
- JSGlobalAddons.this.asInstanceOf[ThisJSGlobalAddons]
- }
-
- /** global javascript interop related helpers */
- object jsInterop {
- import scala.reflect.NameTransformer
- import scala.reflect.internal.Flags
-
- private val exportPrefix = "$js$exported$"
- private val methodExportPrefix = exportPrefix + "meth$"
- private val propExportPrefix = exportPrefix + "prop$"
-
- case class ExportInfo(jsName: String, pos: Position, isNamed: Boolean)
-
- /** retrieves the names a sym should be exported to from its annotations
- *
- * Note that for accessor symbols, the annotations of the accessed symbol
- * are used, rather than the annotations of the accessor itself.
- */
- def exportsOf(sym: Symbol): List[ExportInfo] = {
- val exports = directExportsOf(sym) ++ inheritedExportsOf(sym)
-
- // Calculate the distinct exports for this symbol (eliminate double
- // occurrences of (name, isNamed) pairs).
- val buf = new mutable.ListBuffer[ExportInfo]
- val seen = new mutable.HashSet[(String, Boolean)]
- for (exp <- exports) {
- if (!seen.contains((exp.jsName, exp.isNamed))) {
- buf += exp
- seen += ((exp.jsName, exp.isNamed))
- }
- }
-
- buf.toList
- }
-
- private def directExportsOf(sym: Symbol): List[ExportInfo] = {
- val trgSym = {
- // For accessors, look on the val/var def
- if (sym.isAccessor) sym.accessed
- // For primary class constructors, look on the class itself
- else if (sym.isPrimaryConstructor && !sym.owner.isModuleClass) sym.owner
- else sym
- }
-
- // Annotations that are directly on the member
- val directAnnots = for {
- annot <- trgSym.annotations
- if annot.symbol == JSExportAnnotation ||
- annot.symbol == JSExportNamedAnnotation
- } yield annot
-
- // Annotations for this member on the whole unit
- val unitAnnots = {
- if (sym.isMethod && sym.isPublic &&
- !sym.isConstructor && !sym.isSynthetic)
- sym.owner.annotations.filter(_.symbol == JSExportAllAnnotation)
- else
- Nil
- }
-
- for {
- annot <- directAnnots ++ unitAnnots
- } yield {
- // Is this a named export or a normal one?
- val named = annot.symbol == JSExportNamedAnnotation
-
- def explicitName = annot.stringArg(0).getOrElse {
- reporter.error(annot.pos,
- s"The argument to ${annot.symbol.name} must be a literal string")
- "dummy"
- }
-
- val name =
- if (annot.args.nonEmpty) explicitName
- else if (sym.isConstructor) decodedFullName(sym.owner)
- else if (sym.isModuleClass) decodedFullName(sym)
- else sym.unexpandedName.decoded.stripSuffix("_=")
-
- // Enforce that methods ending with _= are exported as setters
- if (sym.isMethod && !sym.isConstructor &&
- sym.name.decoded.endsWith("_=") && !isJSSetter(sym)) {
- reporter.error(annot.pos, "A method ending in _= will be exported " +
- s"as setter. But ${sym.name.decoded} does not have the right " +
- "signature to do so (single argument, unit return type).")
- }
-
- // Enforce no __ in name
- if (name.contains("__")) {
- // Get position for error message
- val pos = if (annot.stringArg(0).isDefined)
- annot.args.head.pos
- else trgSym.pos
-
- reporter.error(pos,
- "An exported name may not contain a double underscore (`__`)")
- }
-
- // Make sure we do not override the default export of toString
- if (!sym.isConstructor && name == "toString" && !named &&
- sym.name != nme.toString_ && sym.tpe.params.isEmpty &&
- !isJSGetter(sym)) {
- reporter.error(annot.pos, "You may not export a zero-argument " +
- "method named other than 'toString' under the name 'toString'")
- }
-
- if (named && isJSProperty(sym)) {
- reporter.error(annot.pos,
- "You may not export a getter or a setter as a named export")
- }
-
- ExportInfo(name, annot.pos, named)
- }
- }
-
- private def inheritedExportsOf(sym: Symbol): List[ExportInfo] = {
- // The symbol from which we (potentially) inherit exports. It also
- // gives the exports their name
- val trgSym = {
- if (sym.isModuleClass)
- sym
- else if (sym.isConstructor && sym.isPublic &&
- sym.owner.isConcreteClass && !sym.owner.isModuleClass)
- sym.owner
- else NoSymbol
- }
-
- if (trgSym == NoSymbol) {
- Nil
- } else {
- val trgAnnot =
- if (sym.isModuleClass) JSExportDescendentObjectsAnnotation
- else JSExportDescendentClassesAnnotation
-
- val forcingSym =
- trgSym.ancestors.find(_.annotations.exists(_.symbol == trgAnnot))
-
- val name = decodedFullName(trgSym)
-
- forcingSym.map { fs =>
- // Enfore no __ in name
- if (name.contains("__")) {
- // Get all annotation positions for error message
- reporter.error(sym.pos,
- s"""${trgSym.name} may not have a double underscore (`__`) in its fully qualified
- |name, since it is forced to be exported by a @${trgAnnot.name} on ${fs}""".stripMargin)
- }
-
- ExportInfo(name, sym.pos, false)
- }.toList
- }
- }
-
- /** Just like sym.fullName, but does not encode components */
- private def decodedFullName(sym: Symbol): String = {
- if (sym.isRoot || sym.isRootPackage || sym == NoSymbol) sym.name.decoded
- else if (sym.owner.isEffectiveRoot) sym.name.decoded
- else decodedFullName(sym.effectiveOwner.enclClass) + '.' + sym.name.decoded
- }
-
- /** creates a name for an export specification */
- def scalaExportName(jsName: String, isProp: Boolean): TermName = {
- val pref = if (isProp) propExportPrefix else methodExportPrefix
- val encname = NameTransformer.encode(jsName)
- newTermName(pref + encname)
- }
-
- /** checks if the given symbol is a JSExport */
- def isExport(sym: Symbol): Boolean =
- sym.unexpandedName.startsWith(exportPrefix) &&
- !sym.hasFlag(Flags.DEFAULTPARAM)
-
- /** retrieves the originally assigned jsName of this export and whether it
- * is a property
- */
- def jsExportInfo(name: Name): (String, Boolean) = {
- def dropPrefix(prefix: String) ={
- if (name.startsWith(prefix)) {
- // We can't decode right away due to $ separators
- val enc = name.encoded.substring(prefix.length)
- Some(NameTransformer.decode(enc))
- } else None
- }
-
- dropPrefix(methodExportPrefix).map((_,false)) orElse
- dropPrefix(propExportPrefix).map((_,true)) getOrElse
- sys.error("non-exported name passed to jsInfoSpec")
- }
-
- def isJSProperty(sym: Symbol): Boolean = isJSGetter(sym) || isJSSetter(sym)
-
- /** has this symbol to be translated into a JS getter (both directions)? */
- def isJSGetter(sym: Symbol): Boolean = {
- sym.tpe.params.isEmpty && enteringPhase(currentRun.uncurryPhase) {
- sym.tpe.isInstanceOf[NullaryMethodType]
- }
- }
-
- /** has this symbol to be translated into a JS setter (both directions)? */
- def isJSSetter(sym: Symbol) = {
- sym.unexpandedName.decoded.endsWith("_=") &&
- sym.tpe.resultType.typeSymbol == UnitClass &&
- enteringPhase(currentRun.uncurryPhase) {
- sym.tpe.paramss match {
- case List(List(arg)) => !isScalaRepeatedParamType(arg.tpe)
- case _ => false
- }
- }
- }
-
- /** has this symbol to be translated into a JS bracket access (JS to Scala) */
- def isJSBracketAccess(sym: Symbol) =
- sym.hasAnnotation(JSBracketAccessAnnotation)
-
- }
-
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSPrimitives.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSPrimitives.scala
deleted file mode 100644
index b8c20e6..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSPrimitives.scala
+++ /dev/null
@@ -1,119 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Sébastien Doeraene
- */
-
-package scala.scalajs.compiler
-
-import scala.tools.nsc._
-
-import scala.collection.mutable
-
-/** Extension of ScalaPrimitives for primitives only relevant to the JS backend
- *
- * @author Sébastie Doeraene
- */
-abstract class JSPrimitives {
- val global: Global
-
- type ThisJSGlobalAddons = JSGlobalAddons {
- val global: JSPrimitives.this.global.type
- }
-
- val jsAddons: ThisJSGlobalAddons
-
- import global._
- import jsAddons._
- import definitions._
- import rootMirror._
- import jsDefinitions._
- import scalaPrimitives._
-
- val GETCLASS = 301 // Object.getClass()
-
- val F2JS = 305 // FunctionN to js.FunctionN
- val F2JSTHIS = 306 // FunctionN to js.ThisFunction{N-1}
-
- val DYNNEW = 321 // Instantiate a new JavaScript object
-
- val DYNSELECT = 330 // js.Dynamic.selectDynamic
- val DYNUPDATE = 331 // js.Dynamic.updateDynamic
- val DYNAPPLY = 332 // js.Dynamic.applyDynamic
- val DYNLITN = 333 // js.Dynamic.literal.applyDynamicNamed
- val DYNLIT = 334 // js.Dynamic.literal.applyDynamic
-
- val DICT_DEL = 335 // js.Dictionary.delete
-
- val ARR_CREATE = 337 // js.Array.apply (array literal syntax)
-
- val UNDEFVAL = 342 // js.undefined
- val ISUNDEF = 343 // js.isUndefined
- val TYPEOF = 344 // typeof x
- val DEBUGGER = 345 // js.debugger()
- val HASPROP = 346 // js.Object.hasProperty(o, p), equiv to `p in o` in JS
- val OBJPROPS = 347 // js.Object.properties(o), equiv to `for (p in o)` in JS
- val JS_NATIVE = 348 // js.native. Marker method. Fails if tried to be emitted.
-
- val UNITVAL = 349 // () value, which is undefined
- val UNITTYPE = 350 // BoxedUnit.TYPE (== classOf[Unit])
-
- val ENV_INFO = 353 // __ScalaJSEnv via helper
-
- /** Initialize the map of primitive methods (for GenJSCode) */
- def init(): Unit = initWithPrimitives(addPrimitive)
-
- /** Init the map of primitive methods for Scala.js (for PrepJSInterop) */
- def initPrepJSPrimitives(): Unit = {
- scalaJSPrimitives.clear()
- initWithPrimitives(scalaJSPrimitives.put)
- }
-
- /** Only call from PrepJSInterop. In GenJSCode, use
- * scalaPrimitives.isPrimitive instead
- */
- def isJavaScriptPrimitive(sym: Symbol): Boolean =
- scalaJSPrimitives.contains(sym)
-
- private val scalaJSPrimitives = mutable.Map.empty[Symbol, Int]
-
- private def initWithPrimitives(addPrimitive: (Symbol, Int) => Unit): Unit = {
- addPrimitive(Object_getClass, GETCLASS)
-
- for (i <- 0 to 22)
- addPrimitive(JSAny_fromFunction(i), F2JS)
- for (i <- 1 to 22)
- addPrimitive(JSThisFunction_fromFunction(i), F2JSTHIS)
-
- addPrimitive(JSDynamic_newInstance, DYNNEW)
-
- addPrimitive(JSDynamic_selectDynamic, DYNSELECT)
- addPrimitive(JSDynamic_updateDynamic, DYNUPDATE)
- addPrimitive(JSDynamic_applyDynamic, DYNAPPLY)
- addPrimitive(JSDynamicLiteral_applyDynamicNamed, DYNLITN)
- addPrimitive(JSDynamicLiteral_applyDynamic, DYNLIT)
-
- addPrimitive(JSDictionary_delete, DICT_DEL)
-
- addPrimitive(JSArray_create, ARR_CREATE)
-
- val ntModule = getRequiredModule("scala.reflect.NameTransformer")
-
- addPrimitive(JSPackage_typeOf, TYPEOF)
- addPrimitive(JSPackage_debugger, DEBUGGER)
- addPrimitive(JSPackage_undefined, UNDEFVAL)
- addPrimitive(JSPackage_isUndefined, ISUNDEF)
- addPrimitive(JSPackage_native, JS_NATIVE)
-
- addPrimitive(JSObject_hasProperty, HASPROP)
- addPrimitive(JSObject_properties, OBJPROPS)
-
- addPrimitive(BoxedUnit_UNIT, UNITVAL)
- addPrimitive(BoxedUnit_TYPE, UNITTYPE)
-
- addPrimitive(getMember(RuntimePackageModule,
- newTermName("environmentInfo")), ENV_INFO)
- }
-
- def isJavaScriptPrimitive(code: Int) =
- code >= 300 && code < 360
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSTreeExtractors.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSTreeExtractors.scala
deleted file mode 100644
index a18ad88..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/JSTreeExtractors.scala
+++ /dev/null
@@ -1,66 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Tobias Schlatter
- */
-
-package scala.scalajs.compiler
-
-import scala.annotation.tailrec
-
-import scala.scalajs.ir.Trees._
-import scala.scalajs.ir.Types._
-
-/** Useful extractors for JavaScript trees */
-object JSTreeExtractors {
-
- object jse {
- /**
- * A literally named sequence (like in a call to applyDynamicNamed)
- *
- * Example (Scala): method(("name1", x), ("name2", y))
- */
- object LitNamed {
- def unapply(exprs: List[Tree]) = unapply0(exprs, Nil)
-
- @tailrec
- private def unapply0(
- exprs: List[Tree],
- acc: List[(StringLiteral, Tree)]
- ): Option[List[(StringLiteral, Tree)]] = exprs match {
- case Tuple2(name: StringLiteral, value) :: xs =>
- unapply0(xs, (name, value) :: acc)
- case Nil => Some(acc.reverse)
- case _ => None
- }
- }
-
- /**
- * A literal Tuple2
- *
- * Example (Scala): (x, y)
- * But also (Scala): x -> y
- */
- object Tuple2 {
- def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
- // case (x, y)
- case New(ClassType("T2"), Ident("init___O__O", _),
- List(_1, _2)) =>
- Some((_1, _2))
- // case x -> y
- case Apply(
- LoadModule(ClassType("s_Predef$ArrowAssoc$")),
- Ident("$$minus$greater$extension__O__O__T2", _),
- List(
- Apply(
- LoadModule(ClassType("s_Predef$")),
- Ident("any2ArrowAssoc__O__O", _),
- List(_1)),
- _2)) =>
- Some((_1, _2))
- case _ =>
- None
- }
- }
- }
-
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSExports.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSExports.scala
deleted file mode 100644
index 9223061..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSExports.scala
+++ /dev/null
@@ -1,251 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Tobias Schlatter
- */
-
-package scala.scalajs.compiler
-
-import scala.annotation.tailrec
-
-import scala.tools.nsc.NoPhase
-
-/**
- * Prepare export generation
- *
- * Helpers for transformation of @JSExport annotations
- */
-trait PrepJSExports { this: PrepJSInterop =>
-
- import global._
- import jsAddons._
- import definitions._
- import jsDefinitions._
-
- import scala.reflect.internal.Flags
-
- /** Whether the given symbol has a visibility that allows exporting */
- def hasLegalExportVisibility(sym: Symbol): Boolean =
- sym.isPublic || sym.isProtected && !sym.isProtectedLocal
-
- def genExportMember(ddef: DefDef): List[Tree] = {
- val baseSym = ddef.symbol
- val clsSym = baseSym.owner
-
- val exports = jsInterop.exportsOf(baseSym)
-
- // Helper function for errors
- def err(msg: String) = { reporter.error(exports.head.pos, msg); Nil }
- def memType = if (baseSym.isConstructor) "constructor" else "method"
-
- if (exports.isEmpty)
- Nil
- else if (!hasLegalExportVisibility(baseSym))
- err(s"You may only export public and protected ${memType}s")
- else if (baseSym.isMacro)
- err("You may not export a macro")
- else if (scalaPrimitives.isPrimitive(baseSym))
- err("You may not export a primitive")
- else if (hasIllegalRepeatedParam(baseSym))
- err(s"In an exported $memType, a *-parameter must come last " +
- "(through all parameter lists)")
- else if (hasIllegalDefaultParam(baseSym))
- err(s"In an exported $memType, all parameters with defaults " +
- "must be at the end")
- else if (currentRun.uncurryPhase == NoPhase) {
- /* When using scaladoc, the uncurry phase does not exist. This makes
- * our code down below blow up (see bug #323). So we do not do anything
- * more here if the phase does not exist. It's no big deal because we do
- * not need exports for scaladoc.
- */
- Nil
- } else if (baseSym.isConstructor) {
- // we can generate constructors entirely in the backend, since they
- // do not need inheritance and such. But we want to check their sanity
- // here by previous tests and the following ones.
-
- if (!hasLegalExportVisibility(clsSym))
- err("You may only export public and protected classes")
- else if (clsSym.isLocalToBlock)
- err("You may not export a local class")
- else if (clsSym.isNestedClass)
- err("You may not export a nested class. Create an exported factory " +
- "method in the outer class to work around this limitation.")
- else Nil
-
- } else {
- assert(!baseSym.isBridge)
-
- // Reset interface flag: Any trait will contain non-empty methods
- clsSym.resetFlag(Flags.INTERFACE)
-
- // Actually generate exporter methods
- exports.flatMap { exp =>
- if (exp.isNamed)
- genNamedExport(baseSym, exp.jsName, exp.pos) :: Nil
- else
- genExportDefs(baseSym, exp.jsName, exp.pos)
- }
- }
- }
-
- /** generate an exporter for a DefDef including default parameter methods */
- private def genExportDefs(defSym: Symbol, jsName: String, pos: Position) = {
- val clsSym = defSym.owner
- val scalaName =
- jsInterop.scalaExportName(jsName, jsInterop.isJSProperty(defSym))
-
- // Create symbol for new method
- val expSym = defSym.cloneSymbol
-
- // Set position of symbol
- expSym.pos = pos
-
- // Alter type for new method (lift return type to Any)
- // The return type is lifted, in order to avoid bridge
- // construction and to detect methods whose signature only differs
- // in the return type.
- // Attention: This will cause boxes for primitive value types and value
- // classes. However, since we have restricted the return types, we can
- // always safely remove these boxes again in the back-end.
- if (!defSym.isConstructor)
- expSym.setInfo(retToAny(expSym.tpe))
-
- // Change name for new method
- expSym.name = scalaName
-
- // Update flags
- expSym.setFlag(Flags.SYNTHETIC)
- expSym.resetFlag(
- Flags.DEFERRED | // We always have a body
- Flags.ACCESSOR | // We are never a "direct" accessor
- Flags.CASEACCESSOR | // And a fortiori not a case accessor
- Flags.LAZY | // We are not a lazy val (even if we export one)
- Flags.OVERRIDE // Synthetic methods need not bother with this
- )
-
- // Remove export annotations
- expSym.removeAnnotation(JSExportAnnotation)
- expSym.removeAnnotation(JSExportNamedAnnotation)
-
- // Add symbol to class
- clsSym.info.decls.enter(expSym)
-
- // Construct exporter DefDef tree
- val exporter = genProxyDefDef(clsSym, defSym, expSym, pos)
-
- // Construct exporters for default getters
- val defaultGetters = for {
- (param, i) <- expSym.paramss.flatten.zipWithIndex
- if param.hasFlag(Flags.DEFAULTPARAM)
- } yield genExportDefaultGetter(clsSym, defSym, expSym, i + 1, pos)
-
- exporter :: defaultGetters
- }
-
- /** Generate a dummy DefDef tree for a named export. This tree is captured
- * by GenJSCode again to generate the required JavaScript logic.
- */
- private def genNamedExport(defSym: Symbol, jsName: String, pos: Position) = {
- val clsSym = defSym.owner
- val scalaName = jsInterop.scalaExportName(jsName, false)
-
- // Create symbol for the new exporter method
- val expSym = clsSym.newMethodSymbol(scalaName, pos,
- Flags.SYNTHETIC | Flags.FINAL)
-
- // Mark the symbol to be a named export
- expSym.addAnnotation(JSExportNamedAnnotation)
-
- // Create a single parameter of type Any
- val param = expSym.newValueParameter(newTermName("namedArgs"), pos)
- param.setInfo(AnyTpe)
-
- // Set method type
- expSym.setInfo(MethodType(param :: Nil, AnyClass.tpe))
-
- // Register method to parent
- clsSym.info.decls.enter(expSym)
-
- // Placeholder tree
- def ph = Ident(Predef_???)
-
- // Create a call to the forwarded method with ??? as args
- val sel: Tree = Select(This(clsSym), defSym)
- val call = (sel /: defSym.paramss) {
- (fun, params) => Apply(fun, List.fill(params.size)(ph))
- }
-
- // rhs is a block to prevent boxing of result
- typer.typedDefDef(DefDef(expSym, Block(call, ph)))
- }
-
- private def genExportDefaultGetter(clsSym: Symbol, trgMethod: Symbol,
- exporter: Symbol, paramPos: Int, pos: Position) = {
-
- // Get default getter method we'll copy
- val trgGetter =
- clsSym.tpe.member(nme.defaultGetterName(trgMethod.name, paramPos))
-
- assert(trgGetter.exists)
-
- // Although the following must be true in a correct program, we cannot
- // assert, since a graceful failure message is only generated later
- if (!trgGetter.isOverloaded) {
- val expGetter = trgGetter.cloneSymbol
-
- expGetter.name = nme.defaultGetterName(exporter.name, paramPos)
- expGetter.pos = pos
-
- clsSym.info.decls.enter(expGetter)
-
- genProxyDefDef(clsSym, trgGetter, expGetter, pos)
-
- } else EmptyTree
- }
-
- /** generate a DefDef tree (from [[proxySym]]) that calls [[trgSym]] */
- private def genProxyDefDef(clsSym: Symbol, trgSym: Symbol,
- proxySym: Symbol, pos: Position) = atPos(pos) {
-
- // Helper to ascribe repeated argument lists when calling
- def spliceParam(sym: Symbol) = {
- if (isRepeated(sym))
- Typed(Ident(sym), Ident(tpnme.WILDCARD_STAR))
- else
- Ident(sym)
- }
-
- // Construct proxied function call
- val sel: Tree = Select(This(clsSym), trgSym)
- val rhs = (sel /: proxySym.paramss) {
- (fun,params) => Apply(fun, params map spliceParam)
- }
-
- typer.typedDefDef(DefDef(proxySym, rhs))
- }
-
- /** changes the return type of the method type tpe to Any. returns new type */
- private def retToAny(tpe: Type): Type = tpe match {
- case MethodType(params, result) => MethodType(params, retToAny(result))
- case NullaryMethodType(result) => NullaryMethodType(AnyClass.tpe)
- case PolyType(tparams, result) => PolyType(tparams, retToAny(result))
- case _ => AnyClass.tpe
- }
-
- /** checks whether this type has a repeated parameter elsewhere than at the end
- * of all the params
- */
- private def hasIllegalRepeatedParam(sym: Symbol): Boolean = {
- val params = sym.paramss.flatten
- params.nonEmpty && params.init.exists(isRepeated _)
- }
-
- /** checks whether there are default parameters not at the end of
- * the flattened parameter list
- */
- private def hasIllegalDefaultParam(sym: Symbol): Boolean = {
- val isDefParam = (_: Symbol).hasFlag(Flags.DEFAULTPARAM)
- sym.paramss.flatten.reverse.dropWhile(isDefParam).exists(isDefParam)
- }
-
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSInterop.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSInterop.scala
deleted file mode 100644
index 437576a..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/PrepJSInterop.scala
+++ /dev/null
@@ -1,621 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Tobias Schlatter
- */
-
-package scala.scalajs.compiler
-
-import scala.tools.nsc
-import nsc._
-
-import scala.collection.immutable.ListMap
-import scala.collection.mutable
-
-/** Prepares classes extending js.Any for JavaScript interop
- *
- * This phase does:
- * - Sanity checks for js.Any hierarchy
- * - Annotate subclasses of js.Any to be treated specially
- * - Rewrite calls to scala.Enumeration.Value (include name string)
- * - Create JSExport methods: Dummy methods that are propagated
- * through the whole compiler chain to mark exports. This allows
- * exports to have the same semantics than methods.
- *
- * @author Tobias Schlatter
- */
-abstract class PrepJSInterop extends plugins.PluginComponent
- with PrepJSExports
- with transform.Transform
- with Compat210Component {
- val jsAddons: JSGlobalAddons {
- val global: PrepJSInterop.this.global.type
- }
-
- val scalaJSOpts: ScalaJSOptions
-
- import global._
- import jsAddons._
- import definitions._
- import rootMirror._
- import jsDefinitions._
-
- val phaseName = "jsinterop"
-
- override def newPhase(p: nsc.Phase) = new JSInteropPhase(p)
- class JSInteropPhase(prev: nsc.Phase) extends Phase(prev) {
- override def name = phaseName
- override def description = "Prepare ASTs for JavaScript interop"
- override def run(): Unit = {
- jsPrimitives.initPrepJSPrimitives()
- super.run()
- }
- }
-
- override protected def newTransformer(unit: CompilationUnit) =
- new JSInteropTransformer(unit)
-
- private object jsnme {
- val hasNext = newTermName("hasNext")
- val next = newTermName("next")
- val nextName = newTermName("nextName")
- val x = newTermName("x")
- val Value = newTermName("Value")
- val Val = newTermName("Val")
- }
-
- private object jstpnme {
- val scala_ = newTypeName("scala") // not defined in 2.10's tpnme
- }
-
- class JSInteropTransformer(unit: CompilationUnit) extends Transformer {
-
- // Force evaluation of JSDynamicLiteral: Strangely, we are unable to find
- // nested objects in the JSCode phase (probably after flatten).
- // Therefore we force the symbol of js.Dynamic.literal here in order to
- // have access to it in JSCode.
- JSDynamicLiteral
-
- var inJSAnyMod = false
- var inJSAnyCls = false
- var inScalaCls = false
- /** are we inside a subclass of scala.Enumeration */
- var inScalaEnum = false
- /** are we inside the implementation of scala.Enumeration? */
- var inEnumImpl = false
-
- def jsAnyClassOnly = !inJSAnyCls && allowJSAny
- def allowImplDef = !inJSAnyCls && !inJSAnyMod
- def allowJSAny = !inScalaCls
- def inJSAny = inJSAnyMod || inJSAnyCls
-
- /** DefDefs in class templates that export methods to JavaScript */
- val exporters = mutable.Map.empty[Symbol, mutable.ListBuffer[Tree]]
-
- override def transform(tree: Tree): Tree = postTransform { tree match {
- // Catch special case of ClassDef in ModuleDef
- case cldef: ClassDef if jsAnyClassOnly && isJSAny(cldef) =>
- transformJSAny(cldef)
-
- // Catch forbidden implDefs
- case idef: ImplDef if !allowImplDef =>
- reporter.error(idef.pos, "Traits, classes and objects extending " +
- "js.Any may not have inner traits, classes or objects")
- super.transform(tree)
-
- // Handle js.Anys
- case idef: ImplDef if isJSAny(idef) =>
- transformJSAny(idef)
-
- // Catch the definition of scala.Enumeration itself
- case cldef: ClassDef if cldef.symbol == ScalaEnumClass =>
- enterEnumImpl { super.transform(cldef) }
-
- // Catch Scala Enumerations to transform calls to scala.Enumeration.Value
- case cldef: ClassDef if isScalaEnum(cldef) =>
- enterScalaCls {
- enterScalaEnum {
- super.transform(cldef)
- }
- }
- case idef: ImplDef if isScalaEnum(idef) =>
- enterScalaEnum { super.transform(idef) }
-
- // Catch (Scala) ClassDefs to forbid js.Anys
- case cldef: ClassDef =>
- enterScalaCls { super.transform(cldef) }
-
- // Catch ValorDefDef in js.Any
- case vddef: ValOrDefDef if inJSAny =>
- transformValOrDefDefInRawJSType(vddef)
-
- // Catch ValDefs in enumerations with simple calls to Value
- case ValDef(mods, name, tpt, ScalaEnumValue.NoName(optPar)) if inScalaEnum =>
- val nrhs = ScalaEnumValName(tree.symbol.owner, tree.symbol, optPar)
- treeCopy.ValDef(tree, mods, name, transform(tpt), nrhs)
-
- // Catch Select on Enumeration.Value we couldn't transform but need to
- // we ignore the implementation of scala.Enumeration itself
- case ScalaEnumValue.NoName(_) if !inEnumImpl =>
- reporter.warning(tree.pos,
- """Couldn't transform call to Enumeration.Value.
- |The resulting program is unlikely to function properly as this
- |operation requires reflection.""".stripMargin)
- super.transform(tree)
-
- case ScalaEnumValue.NullName() if !inEnumImpl =>
- reporter.warning(tree.pos,
- """Passing null as name to Enumeration.Value
- |requires reflection at runtime. The resulting
- |program is unlikely to function properly.""".stripMargin)
- super.transform(tree)
-
- case ScalaEnumVal.NoName(_) if !inEnumImpl =>
- reporter.warning(tree.pos,
- """Calls to the non-string constructors of Enumeration.Val
- |require reflection at runtime. The resulting
- |program is unlikely to function properly.""".stripMargin)
- super.transform(tree)
-
- case ScalaEnumVal.NullName() if !inEnumImpl =>
- reporter.warning(tree.pos,
- """Passing null as name to a constructor of Enumeration.Val
- |requires reflection at runtime. The resulting
- |program is unlikely to function properly.""".stripMargin)
- super.transform(tree)
-
- // Catch calls to Predef.classOf[T]. These should NEVER reach this phase
- // but unfortunately do. In normal cases, the typer phase replaces these
- // calls by a literal constant of the given type. However, when we compile
- // the scala library itself and Predef.scala is in the sources, this does
- // not happen.
- //
- // The trees reach this phase under the form:
- //
- // scala.this.Predef.classOf[T]
- //
- // If we encounter such a tree, depending on the plugin options, we fail
- // here or silently fix those calls.
- case TypeApply(
- classOfTree @ Select(Select(This(jstpnme.scala_), nme.Predef), nme.classOf),
- List(tpeArg)) =>
- if (scalaJSOpts.fixClassOf) {
- // Replace call by literal constant containing type
- if (typer.checkClassType(tpeArg)) {
- typer.typed { Literal(Constant(tpeArg.tpe.dealias.widen)) }
- } else {
- reporter.error(tpeArg.pos, s"Type ${tpeArg} is not a class type")
- EmptyTree
- }
- } else {
- reporter.error(classOfTree.pos,
- """This classOf resulted in an unresolved classOf in the jscode
- |phase. This is most likely a bug in the Scala compiler. ScalaJS
- |is probably able to work around this bug. Enable the workaround
- |by passing the fixClassOf option to the plugin.""".stripMargin)
- EmptyTree
- }
-
- // Exporter generation
- case ddef: DefDef =>
- // Generate exporters for this ddef if required
- exporters.getOrElseUpdate(ddef.symbol.owner,
- mutable.ListBuffer.empty) ++= genExportMember(ddef)
-
- super.transform(tree)
-
- // Module export sanity check (export generated in JSCode phase)
- case modDef: ModuleDef =>
- val sym = modDef.symbol
-
- def condErr(msg: String) = {
- for (exp <- jsInterop.exportsOf(sym)) {
- reporter.error(exp.pos, msg)
- }
- }
-
- if (!hasLegalExportVisibility(sym))
- condErr("You may only export public and protected objects")
- else if (sym.isLocalToBlock)
- condErr("You may not export a local object")
- else if (!sym.owner.hasPackageFlag)
- condErr("You may not export a nested object")
-
- super.transform(modDef)
-
- // Fix for issue with calls to js.Dynamic.x()
- // Rewrite (obj: js.Dynamic).x(...) to obj.applyDynamic("x")(...)
- case Select(Select(trg, jsnme.x), nme.apply) if isJSDynamic(trg) =>
- val newTree = atPos(tree.pos) {
- Apply(
- Select(super.transform(trg), newTermName("applyDynamic")),
- List(Literal(Constant("x")))
- )
- }
- typer.typed(newTree, Mode.FUNmode, tree.tpe)
-
-
- // Fix for issue with calls to js.Dynamic.x()
- // Rewrite (obj: js.Dynamic).x to obj.selectDynamic("x")
- case Select(trg, jsnme.x) if isJSDynamic(trg) =>
- val newTree = atPos(tree.pos) {
- Apply(
- Select(super.transform(trg), newTermName("selectDynamic")),
- List(Literal(Constant("x")))
- )
- }
- typer.typed(newTree, Mode.FUNmode, tree.tpe)
-
- case _ => super.transform(tree)
- } }
-
- private def postTransform(tree: Tree) = tree match {
- case Template(parents, self, body) =>
- val clsSym = tree.symbol.owner
- val exports = exporters.get(clsSym).toIterable.flatten
- // Add exports to the template
- treeCopy.Template(tree, parents, self, body ++ exports)
-
- case memDef: MemberDef =>
- val sym = memDef.symbol
- if (sym.isLocalToBlock && !sym.owner.isCaseApplyOrUnapply) {
- // We exclude case class apply (and unapply) to work around SI-8826
- for (exp <- jsInterop.exportsOf(sym)) {
- val msg = {
- val base = "You may not export a local definition"
- if (sym.owner.isPrimaryConstructor)
- base + ". To export a (case) class field, use the " +
- "meta-annotation scala.annotation.meta.field like this: " +
- "@(JSExport @field)."
- else
- base
- }
- reporter.error(exp.pos, msg)
- }
- }
- memDef
-
- case _ => tree
- }
-
- /**
- * Performs checks and rewrites specific to classes / objects extending
- * js.Any
- */
- private def transformJSAny(implDef: ImplDef) = {
- val sym = implDef.symbol
-
- lazy val badParent = sym.info.parents.find(t => !(t <:< JSAnyClass.tpe))
- val inScalaJSJSPackage =
- sym.enclosingPackage == ScalaJSJSPackage ||
- sym.enclosingPackage == ScalaJSJSPrimPackage
-
- implDef match {
- // Check that we do not have a case modifier
- case _ if implDef.mods.hasFlag(Flag.CASE) =>
- reporter.error(implDef.pos, "Classes and objects extending " +
- "js.Any may not have a case modifier")
-
- // Check that we do not extends a trait that does not extends js.Any
- case _ if !inScalaJSJSPackage && !badParent.isEmpty &&
- !isJSLambda(sym) =>
- val badName = badParent.get.typeSymbol.fullName
- reporter.error(implDef.pos, s"${sym.nameString} extends ${badName} " +
- "which does not extend js.Any.")
-
- // Check that we are not an anonymous class
- case cldef: ClassDef
- if cldef.symbol.isAnonymousClass && !isJSLambda(sym) =>
- reporter.error(implDef.pos, "Anonymous classes may not " +
- "extend js.Any")
-
- // Check if we may have a js.Any here
- case cldef: ClassDef if !allowJSAny && !jsAnyClassOnly &&
- !isJSLambda(sym) =>
- reporter.error(implDef.pos, "Classes extending js.Any may not be " +
- "defined inside a class or trait")
-
- case _: ModuleDef if !allowJSAny =>
- reporter.error(implDef.pos, "Objects extending js.Any may not be " +
- "defined inside a class or trait")
-
- case _ if sym.isLocalToBlock && !isJSLambda(sym) =>
- reporter.error(implDef.pos, "Local classes and objects may not " +
- "extend js.Any")
-
- // Check that this is not a class extending js.GlobalScope
- case _: ClassDef if isJSGlobalScope(implDef) &&
- implDef.symbol != JSGlobalScopeClass =>
- reporter.error(implDef.pos, "Only objects may extend js.GlobalScope")
-
- case _ =>
- // We cannot use sym directly, since the symbol
- // of a module is not its type's symbol but the value it declares
- val tSym = sym.tpe.typeSymbol
-
- tSym.setAnnotations(rawJSAnnot :: sym.annotations)
-
- }
-
- if (implDef.isInstanceOf[ModuleDef])
- enterJSAnyMod { super.transform(implDef) }
- else
- enterJSAnyCls { super.transform(implDef) }
- }
-
- /** Verify a ValOrDefDef inside a js.Any */
- private def transformValOrDefDefInRawJSType(tree: ValOrDefDef) = {
- val sym = tree.symbol
-
- val exports = jsInterop.exportsOf(sym)
-
- if (exports.nonEmpty) {
- val memType = if (sym.isConstructor) "constructor" else "method"
- reporter.error(exports.head.pos,
- s"You may not export a $memType of a subclass of js.Any")
- }
-
- if (isNonJSScalaSetter(sym)) {
- // Forbid setters with non-unit return type
- reporter.error(tree.pos, "Setters that do not return Unit are " +
- "not allowed in types extending js.Any")
- }
-
- if (sym.hasAnnotation(NativeAttr)) {
- // Native methods are not allowed
- reporter.error(tree.pos, "Methods in a js.Any may not be @native")
- }
-
- for {
- annot <- sym.getAnnotation(JSNameAnnotation)
- if annot.stringArg(0).isEmpty
- } {
- reporter.error(annot.pos,
- "The argument to JSName must be a literal string")
- }
-
- if (sym.isPrimaryConstructor || sym.isValueParameter ||
- sym.isParamWithDefault || sym.isAccessor && !sym.isDeferred ||
- sym.isParamAccessor || sym.isSynthetic ||
- AllJSFunctionClasses.contains(sym.owner)) {
- /* Ignore (i.e. allow) primary ctor, parameters, default parameter
- * getters, accessors, param accessors, synthetic methods (to avoid
- * double errors with case classes, e.g. generated copy method) and
- * js.Functions and js.ThisFunctions (they need abstract methods for SAM
- * treatment.
- */
- } else if (jsPrimitives.isJavaScriptPrimitive(sym)) {
- // Force rhs of a primitive to be `sys.error("stub")` except for the
- // js.native primitive which displays an elaborate error message
- if (sym != JSPackage_native) {
- tree.rhs match {
- case Apply(trg, Literal(Constant("stub")) :: Nil)
- if trg.symbol == definitions.Sys_error =>
- case _ =>
- reporter.error(tree.pos,
- "The body of a primitive must be `sys.error(\"stub\")`.")
- }
- }
- } else if (sym.isConstructor) {
- // Force secondary ctor to have only a call to the primary ctor inside
- tree.rhs match {
- case Block(List(Apply(trg, _)), Literal(Constant(())))
- if trg.symbol.isPrimaryConstructor &&
- trg.symbol.owner == sym.owner =>
- // everything is fine here
- case _ =>
- reporter.error(tree.pos, "A secondary constructor of a class " +
- "extending js.Any may only call the primary constructor")
- }
- } else {
- // Check that the tree's body is either empty or calls js.native
- tree.rhs match {
- case sel: Select if sel.symbol == JSPackage_native =>
- case _ =>
- val pos = if (tree.rhs != EmptyTree) tree.rhs.pos else tree.pos
- reporter.warning(pos, "Members of traits, classes and objects " +
- "extending js.Any may only contain members that call js.native. " +
- "This will be enforced in 1.0.")
- }
-
- if (sym.tpe.resultType.typeSymbol == NothingClass &&
- tree.tpt.asInstanceOf[TypeTree].original == null) {
- // Warn if resultType is Nothing and not ascribed
- val name = sym.name.decoded.trim
- reporter.warning(tree.pos, s"The type of $name got inferred " +
- "as Nothing. To suppress this warning, explicitly ascribe " +
- "the type.")
- }
- }
-
- super.transform(tree)
- }
-
- private def enterJSAnyCls[T](body: =>T) = {
- val old = inJSAnyCls
- inJSAnyCls = true
- val res = body
- inJSAnyCls = old
- res
- }
-
- private def enterJSAnyMod[T](body: =>T) = {
- val old = inJSAnyMod
- inJSAnyMod = true
- val res = body
- inJSAnyMod = old
- res
- }
-
- private def enterScalaCls[T](body: =>T) = {
- val old = inScalaCls
- inScalaCls = true
- val res = body
- inScalaCls = old
- res
- }
-
- private def enterScalaEnum[T](body: =>T) = {
- val old = inScalaEnum
- inScalaEnum = true
- val res = body
- inScalaEnum = old
- res
- }
-
- private def enterEnumImpl[T](body: =>T) = {
- val old = inEnumImpl
- inEnumImpl = true
- val res = body
- inEnumImpl = old
- res
- }
-
- }
-
- def isJSAny(sym: Symbol): Boolean =
- sym.tpe.typeSymbol isSubClass JSAnyClass
-
- private def isJSAny(implDef: ImplDef): Boolean = isJSAny(implDef.symbol)
-
- private def isJSGlobalScope(implDef: ImplDef) =
- implDef.symbol.tpe.typeSymbol isSubClass JSGlobalScopeClass
-
- private def isJSLambda(sym: Symbol) = sym.isAnonymousClass &&
- AllJSFunctionClasses.exists(sym.tpe.typeSymbol isSubClass _)
-
- private def isScalaEnum(implDef: ImplDef) =
- implDef.symbol.tpe.typeSymbol isSubClass ScalaEnumClass
-
- private def isJSDynamic(tree: Tree) = tree.tpe.typeSymbol == JSDynamicClass
-
- /**
- * is this symbol a setter that has a non-unit return type
- *
- * these setters don't make sense in JS (in JS, assignment returns
- * the assigned value) and are therefore not allowed in facade types
- */
- private def isNonJSScalaSetter(sym: Symbol) = nme.isSetterName(sym.name) && {
- sym.tpe.paramss match {
- case List(List(arg)) =>
- !isScalaRepeatedParamType(arg.tpe) &&
- sym.tpe.resultType.typeSymbol != UnitClass
- case _ => false
- }
- }
-
- trait ScalaEnumFctExtractors {
- protected val methSym: Symbol
-
- protected def resolve(ptpes: Symbol*) = {
- val res = methSym suchThat {
- _.tpe.params.map(_.tpe.typeSymbol) == ptpes.toList
- }
- assert(res != NoSymbol)
- res
- }
-
- protected val noArg = resolve()
- protected val nameArg = resolve(StringClass)
- protected val intArg = resolve(IntClass)
- protected val fullMeth = resolve(IntClass, StringClass)
-
- /**
- * Extractor object for calls to the targeted symbol that do not have an
- * explicit name in the parameters
- *
- * Extracts:
- * - `sel: Select` where sel.symbol is targeted symbol (no arg)
- * - Apply(meth, List(param)) where meth.symbol is targeted symbol (i: Int)
- */
- object NoName {
- def unapply(t: Tree) = t match {
- case sel: Select if sel.symbol == noArg =>
- Some(None)
- case Apply(meth, List(param)) if meth.symbol == intArg =>
- Some(Some(param))
- case _ =>
- None
- }
- }
-
- object NullName {
- def unapply(tree: Tree) = tree match {
- case Apply(meth, List(Literal(Constant(null)))) =>
- meth.symbol == nameArg
- case Apply(meth, List(_, Literal(Constant(null)))) =>
- meth.symbol == fullMeth
- case _ => false
- }
- }
-
- }
-
- private object ScalaEnumValue extends {
- protected val methSym = getMemberMethod(ScalaEnumClass, jsnme.Value)
- } with ScalaEnumFctExtractors
-
- private object ScalaEnumVal extends {
- protected val methSym = {
- val valSym = getMemberClass(ScalaEnumClass, jsnme.Val)
- valSym.tpe.member(nme.CONSTRUCTOR)
- }
- } with ScalaEnumFctExtractors
-
- /**
- * Construct a call to Enumeration.Value
- * @param thisSym ClassSymbol of enclosing class
- * @param nameOrig Symbol of ValDef where this call will be placed
- * (determines the string passed to Value)
- * @param intParam Optional tree with Int passed to Value
- * @return Typed tree with appropriate call to Value
- */
- private def ScalaEnumValName(
- thisSym: Symbol,
- nameOrig: Symbol,
- intParam: Option[Tree]) = {
-
- val defaultName = nameOrig.asTerm.getterName.encoded
-
-
- // Construct the following tree
- //
- // if (nextName != null && nextName.hasNext)
- // nextName.next()
- // else
- // <defaultName>
- //
- val nextNameTree = Select(This(thisSym), jsnme.nextName)
- val nullCompTree =
- Apply(Select(nextNameTree, nme.NE), Literal(Constant(null)) :: Nil)
- val hasNextTree = Select(nextNameTree, jsnme.hasNext)
- val condTree = Apply(Select(nullCompTree, nme.ZAND), hasNextTree :: Nil)
- val nameTree = If(condTree,
- Apply(Select(nextNameTree, jsnme.next), Nil),
- Literal(Constant(defaultName)))
- val params = intParam.toList :+ nameTree
-
- typer.typed {
- Apply(Select(This(thisSym), jsnme.Value), params)
- }
- }
-
- private def rawJSAnnot =
- Annotation(RawJSTypeAnnot.tpe, List.empty, ListMap.empty)
-
- private lazy val ScalaEnumClass = getRequiredClass("scala.Enumeration")
-
- /** checks if the primary constructor of the ClassDef `cldef` does not
- * take any arguments
- */
- private def primCtorNoArg(cldef: ClassDef) =
- getPrimCtor(cldef.symbol.tpe).map(_.paramss == List(List())).getOrElse(true)
-
- /** return the MethodSymbol of the primary constructor of the given type
- * if it exists
- */
- private def getPrimCtor(tpe: Type) =
- tpe.declaration(nme.CONSTRUCTOR).alternatives.collectFirst {
- case ctor: MethodSymbol if ctor.isPrimaryConstructor => ctor
- }
-
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSOptions.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSOptions.scala
deleted file mode 100644
index 72912bf..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSOptions.scala
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Tobias Schlatter
- */
-
-package scala.scalajs.compiler
-
-import java.net.URI
-
-/** This trait allows to query all options to the ScalaJS plugin
- *
- * Also see the help text in ScalaJSPlugin for information about particular
- * options.
- */
-trait ScalaJSOptions {
- import ScalaJSOptions.URIMap
-
- /** should calls to Predef.classOf[T] be fixed in the jsinterop phase.
- * If false, bad calls to classOf will cause an error. */
- def fixClassOf: Boolean
-
- /** which source locations in source maps should be relativized (or where
- * should they be mapped to)? */
- def sourceURIMaps: List[URIMap]
-
-}
-
-object ScalaJSOptions {
- case class URIMap(from: URI, to: Option[URI])
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSPlugin.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSPlugin.scala
deleted file mode 100644
index c3916ab..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/ScalaJSPlugin.scala
+++ /dev/null
@@ -1,143 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Sébastien Doeraene
- */
-
-package scala.scalajs.compiler
-
-import scala.tools.nsc._
-import scala.tools.nsc.plugins.{
- Plugin => NscPlugin, PluginComponent => NscPluginComponent
-}
-import scala.collection.{ mutable, immutable }
-
-import java.net.{ URI, URISyntaxException }
-
-import scala.scalajs.ir.Trees
-
-/** Main entry point for the Scala.js compiler plugin
- *
- * @author Sébastien Doeraene
- */
-class ScalaJSPlugin(val global: Global) extends NscPlugin {
- import global._
-
- val name = "scalajs"
- val description = "Compile to JavaScript"
- val components = {
- if (global.forScaladoc)
- List[NscPluginComponent](PrepInteropComponent)
- else
- List[NscPluginComponent](PrepInteropComponent, GenCodeComponent)
- }
-
- /** Called when the JS ASTs are generated. Override for testing */
- def generatedJSAST(clDefs: List[Trees.Tree]): Unit = {}
-
- /** Addons for JavaScript platform */
- object jsAddons extends {
- val global: ScalaJSPlugin.this.global.type = ScalaJSPlugin.this.global
- } with JSGlobalAddons with Compat210Component
-
- object scalaJSOpts extends ScalaJSOptions {
- import ScalaJSOptions.URIMap
- var fixClassOf: Boolean = false
- lazy val sourceURIMaps: List[URIMap] = {
- if (_sourceURIMaps.nonEmpty)
- _sourceURIMaps.reverse
- else
- relSourceMap.toList.map(URIMap(_, absSourceMap))
- }
- var _sourceURIMaps: List[URIMap] = Nil
- var relSourceMap: Option[URI] = None
- var absSourceMap: Option[URI] = None
- }
-
- object PrepInteropComponent extends {
- val global: ScalaJSPlugin.this.global.type = ScalaJSPlugin.this.global
- val jsAddons: ScalaJSPlugin.this.jsAddons.type = ScalaJSPlugin.this.jsAddons
- val scalaJSOpts = ScalaJSPlugin.this.scalaJSOpts
- override val runsAfter = List("typer")
- override val runsBefore = List("pickle")
- } with PrepJSInterop
-
- object GenCodeComponent extends {
- val global: ScalaJSPlugin.this.global.type = ScalaJSPlugin.this.global
- val jsAddons: ScalaJSPlugin.this.jsAddons.type = ScalaJSPlugin.this.jsAddons
- val scalaJSOpts = ScalaJSPlugin.this.scalaJSOpts
- override val runsAfter = List("mixin")
- override val runsBefore = List("delambdafy", "cleanup", "terminal")
- } with GenJSCode {
- def generatedJSAST(clDefs: List[Trees.Tree]) =
- ScalaJSPlugin.this.generatedJSAST(clDefs)
- }
-
- override def processOptions(options: List[String],
- error: String => Unit): Unit = {
- import ScalaJSOptions.URIMap
- import scalaJSOpts._
-
- for (option <- options) {
- if (option == "fixClassOf") {
- fixClassOf = true
-
- } else if (option.startsWith("mapSourceURI:")) {
- val uris = option.stripPrefix("mapSourceURI:").split("->")
-
- if (uris.length != 1 && uris.length != 2) {
- error("relocateSourceMap needs one or two URIs as argument.")
- } else {
- try {
- val from = new URI(uris.head)
- val to = uris.lift(1).map(str => new URI(str))
- _sourceURIMaps ::= URIMap(from, to)
- } catch {
- case e: URISyntaxException =>
- error(s"${e.getInput} is not a valid URI")
- }
- }
-
- // The following options are deprecated (how do we show this to the user?)
- } else if (option.startsWith("relSourceMap:")) {
- val uriStr = option.stripPrefix("relSourceMap:")
- try { relSourceMap = Some(new URI(uriStr)) }
- catch {
- case e: URISyntaxException => error(s"$uriStr is not a valid URI")
- }
- } else if (option.startsWith("absSourceMap:")) {
- val uriStr = option.stripPrefix("absSourceMap:")
- try { absSourceMap = Some(new URI(uriStr)) }
- catch {
- case e: URISyntaxException => error(s"$uriStr is not a valid URI")
- }
- } else {
- error("Option not understood: " + option)
- }
- }
-
- // Verify constraints
- if (_sourceURIMaps.nonEmpty && relSourceMap.isDefined)
- error("You may not use mapSourceURI and relSourceMap together. " +
- "Use another mapSourceURI option without second URI.")
- else if (_sourceURIMaps.nonEmpty && absSourceMap.isDefined)
- error("You may not use mapSourceURI and absSourceMap together. " +
- "Use another mapSourceURI option.")
- else if (absSourceMap.isDefined && relSourceMap.isEmpty)
- error("absSourceMap requires the use of relSourceMap")
- }
-
- override val optionsHelp: Option[String] = Some(s"""
- | -P:$name:mapSourceURI:FROM_URI[->TO_URI]
- | change the location the source URIs in the emitted IR point to
- | - strips away the prefix FROM_URI (if it matches)
- | - optionally prefixes the TO_URI, where stripping has been performed
- | - any number of occurences are allowed. Processing is done on a first match basis.
- | -P:$name:fixClassOf repair calls to Predef.classOf that reach ScalaJS
- | WARNING: This is a tremendous hack! Expect ugly errors if you use this option.
- |Deprecated options
- | -P:$name:relSourceMap:<URI> relativize emitted source maps with <URI>
- | -P:$name:absSourceMap:<URI> absolutize emitted source maps with <URI>
- | This option requires the use of relSourceMap
- """.stripMargin)
-
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala
deleted file mode 100644
index 774be68..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala
+++ /dev/null
@@ -1,252 +0,0 @@
-/* Scala.js compiler
- * Copyright 2013 LAMP/EPFL
- * @author Sébastien Doeraene
- */
-
-package scala.scalajs.compiler
-
-import scala.tools.nsc._
-
-import scala.scalajs.ir
-import ir.{Definitions, Types}
-
-/** Glue representation of types as seen from the IR but still with a
- * reference to the Symbols.
- *
- * @author Sébastien Doeraene
- */
-trait TypeKinds extends SubComponent { this: GenJSCode =>
- import global._
- import jsAddons._
- import definitions._
-
- lazy val ObjectReference = REFERENCE(definitions.ObjectClass)
-
- lazy val VoidKind = VOID
- lazy val BooleanKind = BOOL
- lazy val CharKind = INT(CharClass)
- lazy val ByteKind = INT(ByteClass)
- lazy val ShortKind = INT(ShortClass)
- lazy val IntKind = INT(IntClass)
- lazy val LongKind = LONG
- lazy val FloatKind = FLOAT(FloatClass)
- lazy val DoubleKind = FLOAT(DoubleClass)
-
- /** TypeKinds for Scala primitive types. */
- lazy val primitiveTypeMap: Map[Symbol, TypeKind] = {
- import definitions._
- Map(
- UnitClass -> VoidKind,
- BooleanClass -> BooleanKind,
- CharClass -> CharKind,
- ByteClass -> ByteKind,
- ShortClass -> ShortKind,
- IntClass -> IntKind,
- LongClass -> LongKind,
- FloatClass -> FloatKind,
- DoubleClass -> DoubleKind
- )
- }
-
- /** Glue representation of types as seen from the IR but still with a
- * reference to the Symbols.
- */
- sealed abstract class TypeKind {
- def isReferenceType = false
- def isArrayType = false
- def isValueType = false
-
- def toIRType: Types.Type
- def toReferenceType: Types.ReferenceType
- }
-
- sealed abstract class TypeKindButArray extends TypeKind {
- protected def typeSymbol: Symbol
-
- override def toReferenceType: Types.ClassType =
- Types.ClassType(encodeClassFullName(typeSymbol))
- }
-
- /** The void, for trees that can only appear in statement position. */
- case object VOID extends TypeKindButArray {
- protected def typeSymbol = UnitClass
- def toIRType: Types.NoType.type = Types.NoType
- }
-
- sealed abstract class ValueTypeKind extends TypeKindButArray {
- override def isValueType = true
-
- val primitiveCharCode: Char = typeSymbol match {
- case BooleanClass => 'Z'
- case CharClass => 'C'
- case ByteClass => 'B'
- case ShortClass => 'S'
- case IntClass => 'I'
- case LongClass => 'J'
- case FloatClass => 'F'
- case DoubleClass => 'D'
- case x => abort("Unknown primitive type: " + x.fullName)
- }
- }
-
- /** Integer number (Byte, Short, Char or Int). */
- case class INT private[TypeKinds] (typeSymbol: Symbol) extends ValueTypeKind {
- def toIRType: Types.IntType.type = Types.IntType
- }
-
- /** Long */
- case object LONG extends ValueTypeKind {
- protected def typeSymbol = definitions.LongClass
- def toIRType: Types.LongType.type = Types.LongType
- }
-
- /** Floating-point number (Float or Double). */
- case class FLOAT private[TypeKinds] (typeSymbol: Symbol) extends ValueTypeKind {
- def toIRType: Types.Type =
- if (typeSymbol == FloatClass) Types.FloatType
- else Types.DoubleType
- }
-
- /** Boolean */
- case object BOOL extends ValueTypeKind {
- protected def typeSymbol = definitions.BooleanClass
- def toIRType: Types.BooleanType.type = Types.BooleanType
- }
-
- /** Nothing */
- case object NOTHING extends TypeKindButArray {
- protected def typeSymbol = definitions.NothingClass
- def toIRType: Types.NothingType.type = Types.NothingType
- override def toReferenceType: Types.ClassType =
- Types.ClassType(Definitions.RuntimeNothingClass)
- }
-
- /** Null */
- case object NULL extends TypeKindButArray {
- protected def typeSymbol = definitions.NullClass
- def toIRType: Types.NullType.type = Types.NullType
- override def toReferenceType: Types.ClassType =
- Types.ClassType(Definitions.RuntimeNullClass)
- }
-
- /** An object */
- case class REFERENCE private[TypeKinds] (typeSymbol: Symbol) extends TypeKindButArray {
- override def toString(): String = "REFERENCE(" + typeSymbol.fullName + ")"
- override def isReferenceType = true
-
- def toIRType: Types.Type = encodeClassType(typeSymbol)
- }
-
- /** An array */
- case class ARRAY private[TypeKinds] (elem: TypeKind) extends TypeKind {
- override def toString = "ARRAY[" + elem + "]"
- override def isArrayType = true
-
- def dimensions: Int = elem match {
- case a: ARRAY => a.dimensions + 1
- case _ => 1
- }
-
- override def toIRType: Types.ArrayType = toReferenceType
-
- override def toReferenceType: Types.ArrayType = {
- Types.ArrayType(
- elementKind.toReferenceType.className,
- dimensions)
- }
-
- /** The ultimate element type of this array. */
- def elementKind: TypeKindButArray = elem match {
- case a: ARRAY => a.elementKind
- case k: TypeKindButArray => k
- }
- }
-
- ////////////////// Conversions //////////////////////////////
-
- def toIRType(t: Type): Types.Type =
- toTypeKind(t).toIRType
-
- def toReferenceType(t: Type): Types.ReferenceType =
- toTypeKind(t).toReferenceType
-
- // The following code is a hard copy-and-paste from backend.icode.TypeKinds
-
- /** Return the TypeKind of the given type
- *
- * Call to .normalize fixes #3003 (follow type aliases). Otherwise,
- * arrayOrClassType below would return ObjectReference.
- */
- def toTypeKind(t: Type): TypeKind = t.normalize match {
- case ThisType(ArrayClass) => ObjectReference
- case ThisType(sym) => newReference(sym)
- case SingleType(_, sym) => primitiveOrRefType(sym)
- case ConstantType(_) => toTypeKind(t.underlying)
- case TypeRef(_, sym, args) => primitiveOrClassType(sym, args)
- case ClassInfoType(_, _, ArrayClass) => abort("ClassInfoType to ArrayClass!")
- case ClassInfoType(_, _, sym) => primitiveOrRefType(sym)
-
- // !!! Iulian says types which make no sense after erasure should not reach here,
- // which includes the ExistentialType, AnnotatedType, RefinedType. I don't know
- // if the first two cases exist because they do or as a defensive measure, but
- // at the time I added it, RefinedTypes were indeed reaching here.
- // !!! Removed in JavaScript backend because I do not know what to do with lub
- //case ExistentialType(_, t) => toTypeKind(t)
- // Apparently, this case does occur (see pos/CustomGlobal.scala)
- case t: AnnotatedType => toTypeKind(t.underlying)
- //case RefinedType(parents, _) => parents map toTypeKind reduceLeft lub
-
- /* This case is not in scalac. We need it for the test
- * run/valueclasses-classtag-existential. I have no idea how icode does
- * not fail this test: we do everything the same as icode up to here.
- */
- case tpe: ErasedValueType => newReference(tpe.valueClazz)
-
- // For sure WildcardTypes shouldn't reach here either, but when
- // debugging such situations this may come in handy.
- // case WildcardType => REFERENCE(ObjectClass)
- case norm => abort(
- "Unknown type: %s, %s [%s, %s] TypeRef? %s".format(
- t, norm, t.getClass, norm.getClass, t.isInstanceOf[TypeRef]
- )
- )
- }
-
- /** Return the type kind of a class, possibly an array type.
- */
- private def arrayOrClassType(sym: Symbol, targs: List[Type]) = sym match {
- case ArrayClass => ARRAY(toTypeKind(targs.head))
- case _ if sym.isClass => newReference(sym)
- case _ =>
- assert(sym.isType, sym) // it must be compiling Array[a]
- ObjectReference
- }
-
- /** Interfaces have to be handled delicately to avoid introducing
- * spurious errors, but if we treat them all as AnyRef we lose too
- * much information.
- */
- private def newReference(sym: Symbol): TypeKind = sym match {
- case NothingClass => NOTHING
- case NullClass => NULL
- case _ =>
- // Can't call .toInterface (at this phase) or we trip an assertion.
- // See PackratParser#grow for a method which fails with an apparent mismatch
- // between "object PackratParsers$class" and "trait PackratParsers"
- if (sym.isImplClass) {
- // pos/spec-List.scala is the sole failure if we don't check for NoSymbol
- val traitSym = sym.owner.info.decl(tpnme.interfaceName(sym.name))
- if (traitSym != NoSymbol)
- REFERENCE(traitSym)
- else
- REFERENCE(sym)
- } else {
- REFERENCE(sym)
- }
- }
-
- private def primitiveOrRefType(sym: Symbol) =
- primitiveTypeMap.getOrElse(sym, newReference(sym))
- private def primitiveOrClassType(sym: Symbol, targs: List[Type]) =
- primitiveTypeMap.getOrElse(sym, arrayOrClassType(sym, targs))
-}
diff --git a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/util/ScopedVar.scala b/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/util/ScopedVar.scala
deleted file mode 100644
index 3924955..0000000
--- a/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/util/ScopedVar.scala
+++ /dev/null
@@ -1,38 +0,0 @@
-package scala.scalajs.compiler.util
-
-import language.implicitConversions
-
-class ScopedVar[A](init: A) {
- import ScopedVar.Assignment
-
- private var value = init
-
- def this()(implicit ev: Null <:< A) = this(ev(null))
-
- def get: A = value
- def :=(newValue: A): Assignment[A] = new Assignment(this, newValue)
-}
-
-object ScopedVar {
- class Assignment[T](scVar: ScopedVar[T], value: T) {
- private[ScopedVar] def push(): AssignmentStackElement[T] = {
- val stack = new AssignmentStackElement(scVar, scVar.value)
- scVar.value = value
- stack
- }
- }
-
- private class AssignmentStackElement[T](scVar: ScopedVar[T], oldValue: T) {
- private[ScopedVar] def pop(): Unit = {
- scVar.value = oldValue
- }
- }
-
- implicit def toValue[T](scVar: ScopedVar[T]): T = scVar.get
-
- def withScopedVars[T](ass: Assignment[_]*)(body: => T): T = {
- val stack = ass.map(_.push())
- try body
- finally stack.reverse.foreach(_.pop())
- }
-}