diff options
Diffstat (limited to 'examples/scala-js/compiler/src')
28 files changed, 0 insertions, 8906 deletions
diff --git a/examples/scala-js/compiler/src/main/resources/scalac-plugin.xml b/examples/scala-js/compiler/src/main/resources/scalac-plugin.xml deleted file mode 100644 index 76ff1b7..0000000 --- a/examples/scala-js/compiler/src/main/resources/scalac-plugin.xml +++ /dev/null @@ -1,4 +0,0 @@ -<plugin> - <name>scalajs</name> - <classname>scala.scalajs.compiler.ScalaJSPlugin</classname> -</plugin> 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()) - } -} diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/DiverseErrorsTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/DiverseErrorsTest.scala deleted file mode 100644 index 0fe10f8..0000000 --- a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/DiverseErrorsTest.scala +++ /dev/null @@ -1,31 +0,0 @@ -package scala.scalajs.compiler.test - -import scala.scalajs.compiler.test.util._ -import org.junit.Test - -class DiverseErrorsTest extends DirectTest with TestHelpers { - - override def preamble = - """import scala.scalajs.js - """ - - @Test - def noIsInstanceOnJSRaw = { - - """ - trait JSRaw extends js.Object - - class A { - val a: AnyRef = "asdf" - def x = a.isInstanceOf[JSRaw] - } - """ hasErrors - """ - |newSource1.scala:7: error: isInstanceOf[JSRaw] not supported because it is a raw JS trait - | def x = a.isInstanceOf[JSRaw] - | ^ - """ - - } - -} diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/EnumerationInteropTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/EnumerationInteropTest.scala deleted file mode 100644 index e186cf4..0000000 --- a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/EnumerationInteropTest.scala +++ /dev/null @@ -1,135 +0,0 @@ -package scala.scalajs.compiler.test - -import scala.scalajs.compiler.test.util._ - -import org.junit.Test - -class EnumerationInteropTest extends DirectTest with TestHelpers { - - @Test - def warnIfUnableToTransformValue = { - - """ - class A extends Enumeration { - val a = { - println("oh, oh!") - Value - } - val b = { - println("oh, oh!") - Value(4) - } - } - """ hasWarns - """ - |newSource1.scala:5: warning: Couldn't transform call to Enumeration.Value. - |The resulting program is unlikely to function properly as this - |operation requires reflection. - | Value - | ^ - |newSource1.scala:9: warning: Couldn't transform call to Enumeration.Value. - |The resulting program is unlikely to function properly as this - |operation requires reflection. - | Value(4) - | ^ - """ - - } - - @Test - def warnIfNoNameVal = { - - """ - class A extends Enumeration { - val a = new Val - val b = new Val(10) - } - """ hasWarns - """ - |newSource1.scala:3: warning: Calls to the non-string constructors of Enumeration.Val - |require reflection at runtime. The resulting - |program is unlikely to function properly. - | val a = new Val - | ^ - |newSource1.scala:4: warning: Calls to the non-string constructors of Enumeration.Val - |require reflection at runtime. The resulting - |program is unlikely to function properly. - | val b = new Val(10) - | ^ - """ - - } - - @Test - def warnIfNullValue = { - - """ - class A extends Enumeration { - val a = Value(null) - val b = Value(10, null) - } - """ hasWarns - """ - |newSource1.scala:3: warning: Passing null as name to Enumeration.Value - |requires reflection at runtime. The resulting - |program is unlikely to function properly. - | val a = Value(null) - | ^ - |newSource1.scala:4: warning: Passing null as name to Enumeration.Value - |requires reflection at runtime. The resulting - |program is unlikely to function properly. - | val b = Value(10, null) - | ^ - """ - - } - - @Test - def warnIfNullNewVal = { - - """ - class A extends Enumeration { - val a = new Val(null) - val b = new Val(10, null) - } - """ hasWarns - """ - |newSource1.scala:3: warning: Passing null as name to a constructor of Enumeration.Val - |requires reflection at runtime. The resulting - |program is unlikely to function properly. - | val a = new Val(null) - | ^ - |newSource1.scala:4: warning: Passing null as name to a constructor of Enumeration.Val - |requires reflection at runtime. The resulting - |program is unlikely to function properly. - | val b = new Val(10, null) - | ^ - """ - - } - - @Test - def warnIfExtNoNameVal = { - - """ - class A extends Enumeration { - protected class Val1 extends Val - protected class Val2 extends Val(1) - } - """ warns() // no message checking: position differs in 2.10 and 2.11 - - } - - @Test - def warnIfExtNullNameVal = { - - """ - class A extends Enumeration { - protected class Val1 extends Val(null) - protected class Val2 extends Val(1,null) - } - """ warns() // no message checking: position differs in 2.10 and 2.11 - - } - -} diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSDynamicLiteralTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSDynamicLiteralTest.scala deleted file mode 100644 index bc1a1b4..0000000 --- a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSDynamicLiteralTest.scala +++ /dev/null @@ -1,102 +0,0 @@ -package scala.scalajs.compiler.test - -import scala.scalajs.compiler.test.util._ -import org.junit.Test - -class JSDynamicLiteralTest extends DirectTest with TestHelpers { - - override def preamble = - """import scala.scalajs.js.Dynamic.{ literal => lit } - """ - - @Test - def callApplyOnly = { - - // selectDynamic (with any name) - expr""" - lit.helloWorld - """.fails() // Scala error, no string checking due to versions - - // applyDynamicNamed with wrong method name - expr""" - lit.helloWorld(a = "a") - """ hasErrors - """ - |newSource1.scala:3: error: js.Dynamic.literal does not have a method named helloWorld - | lit.helloWorld(a = "a") - | ^ - """ - - // applyDynamic with wrong method name - expr""" - lit.helloWorld("a" -> "a") - """ hasErrors - """ - |newSource1.scala:3: error: js.Dynamic.literal does not have a method named helloWorld - | lit.helloWorld("a" -> "a") - | ^ - """ - - } - - @Test - def goodTypesOnly = { - - // Bad value type (applyDynamic) - """ - class A { - val x = new Object() - def foo = lit("a" -> x) - } - """.fails() - - // Bad key type (applyDynamic) - """ - class A { - val x = Seq() - def foo = lit(x -> "a") - } - """.fails() - - // Bad value type (applyDynamicNamed) - """ - class A { - val x = new Object() - def foo = lit(a = x) - } - """.fails() - - } - - @Test - def noNonLiteralMethodName = { - - // applyDynamicNamed - """ - class A { - val x = "string" - def foo = lit.applyDynamicNamed(x)() - } - """ hasErrors - """ - |newSource1.scala:5: error: js.Dynamic.literal.applyDynamicNamed may not be called directly - | def foo = lit.applyDynamicNamed(x)() - | ^ - """ - - // applyDynamic - """ - class A { - val x = "string" - def foo = lit.applyDynamic(x)() - } - """ hasErrors - """ - |newSource1.scala:5: error: js.Dynamic.literal.applyDynamic may not be called directly - | def foo = lit.applyDynamic(x)() - | ^ - """ - - } - -} diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportASTTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportASTTest.scala deleted file mode 100644 index 4a2b1af..0000000 --- a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportASTTest.scala +++ /dev/null @@ -1,38 +0,0 @@ -package scala.scalajs.compiler.test - -import util._ - -import org.junit.Test -import org.junit.Assert._ - -import scala.scalajs.ir.{Trees => js} - -class JSExportASTTest extends JSASTTest { - - @Test - def inheritExportMethods: Unit = { - - var props = 0 - - """ - import scala.scalajs.js.annotation.JSExport - - class A { - @JSExport - def foo = 1 - } - - class B extends A { - @JSExport - override def foo = 2 - } - """.traverse { - case js.PropertyDef(js.StringLiteral("foo"), _, _, _) => - props += 1 - } - - assertEquals("Only define the property `foo` once", props, 1) - - } - -} diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala deleted file mode 100644 index c675420..0000000 --- a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSExportTest.scala +++ /dev/null @@ -1,745 +0,0 @@ -package scala.scalajs.compiler.test - -import scala.scalajs.compiler.test.util._ -import org.junit.Test -import org.junit.Ignore - -class JSExportTest extends DirectTest with TestHelpers { - - override def preamble = - """import scala.scalajs.js.annotation._ - """ - - @Test - def noDoubleUnderscoreExport = { - // Normal exports - """ - class A { - @JSExport(name = "__") - def foo = 1 - - @JSExport - def bar__(x: Int) = x - } - - @JSExport - class B__ - """ hasErrors - """ - |newSource1.scala:4: error: An exported name may not contain a double underscore (`__`) - | @JSExport(name = "__") - | ^ - |newSource1.scala:8: error: An exported name may not contain a double underscore (`__`) - | def bar__(x: Int) = x - | ^ - |newSource1.scala:12: error: An exported name may not contain a double underscore (`__`) - | class B__ - | ^ - """ - - // Inherited exports (objects) - """ - @JSExportDescendentObjects - trait A - - package fo__o { - object B extends A - } - """ hasErrors - """ - |newSource1.scala:7: error: B may not have a double underscore (`__`) in its fully qualified - |name, since it is forced to be exported by a @JSExportDescendentObjects on trait A - | object B extends A - | ^ - """ - - // Inherited exports (classes) - """ - @JSExportDescendentClasses - trait A - - package fo__o { - class B(x: Int) extends A { - def this() = this(1) - private def this(s: String) = this(1) - } - } - """ hasErrors - """ - |newSource1.scala:7: error: B may not have a double underscore (`__`) in its fully qualified - |name, since it is forced to be exported by a @JSExportDescendentClasses on trait A - | class B(x: Int) extends A { - | ^ - |newSource1.scala:8: error: B may not have a double underscore (`__`) in its fully qualified - |name, since it is forced to be exported by a @JSExportDescendentClasses on trait A - | def this() = this(1) - | ^ - """ - } - - @Test - def noConflictingExport = { - """ - class Confl { - @JSExport("value") - def hello = "foo" - - @JSExport("value") - def world = "bar" - } - """ fails() // No error test, Scala version dependent error messages - - """ - class Confl { - class Box[T](val x: T) - - @JSExport - def ub(x: Box[String]): String = x.x - @JSExport - def ub(x: Box[Int]): Int = x.x - } - """ fails() // No error test, Scala version dependent error messages - - """ - class Confl { - @JSExport - def rtType(x: scala.scalajs.js.prim.Number) = x - - @JSExport - def rtType(x: Double) = x - } - """ fails() // Error message depends on Scala version - - """ - class Confl { - @JSExport - def foo(x: Int)(ys: Int*) = x - - @JSExport - def foo(x: Int*) = x - } - """ hasErrors - """ - |newSource1.scala:7: error: Cannot disambiguate overloads for exported method $js$exported$meth$foo with types - | (x: Seq)Object - | (x: Int, ys: Seq)Object - | @JSExport - | ^ - """ - - """ - class Confl { - @JSExport - def foo(x: Int = 1) = x - @JSExport - def foo(x: String*) = x - } - """ hasErrors - """ - |newSource1.scala:4: error: Cannot disambiguate overloads for exported method $js$exported$meth$foo with types - | (x: Int)Object - | (x: Seq)Object - | @JSExport - | ^ - """ - - """ - class Confl { - @JSExport - def foo(x: scala.scalajs.js.prim.Number, y: String)(z: Int = 1) = x - @JSExport - def foo(x: Double, y: String)(z: String*) = x - } - """ fails() // Error message depends on Scala version - - """ - class A { - @JSExport - def a(x: scala.scalajs.js.Any) = 1 - - @JSExport - def a(x: Any) = 2 - } - """ fails() // Error message depends on Scala version - - } - - @Test - def noExportLocal = { - // Local class - """ - class A { - def method = { - @JSExport - class A - } - } - """ hasErrors - """ - |newSource1.scala:5: error: You may not export a local class - | @JSExport - | ^ - """ - - // Local object - """ - class A { - def method = { - @JSExport - object A - } - } - """ hasErrors - """ - |newSource1.scala:5: error: You may not export a local object - | @JSExport - | ^ - """ - - // Local method - """ - class A { - def method = { - @JSExport - def foo = 1 - } - } - """ hasErrors - """ - |newSource1.scala:5: error: You may not export a local definition - | @JSExport - | ^ - """ - - // Local val - """ - class A { - def method = { - @JSExport - val x = 1 - } - } - """ hasErrors - """ - |newSource1.scala:5: error: You may not export a local definition - | @JSExport - | ^ - """ - - // Local var - """ - class A { - def method = { - @JSExport - var x = 1 - } - } - """ hasErrors - """ - |newSource1.scala:5: error: You may not export a local definition - | @JSExport - | ^ - """ - - } - - @Test - def infoExportLocal = { - - """ - class A(@JSExport val x: Int) - """ hasErrors - """ - |newSource1.scala:3: error: You may not export a local definition. To export a (case) class field, use the meta-annotation scala.annotation.meta.field like this: @(JSExport @field). - | class A(@JSExport val x: Int) - | ^ - """ - - } - - @Test - def noMiddleVarArg = { - - """ - class A { - @JSExport - def method(xs: Int*)(ys: String) = 1 - } - """ hasErrors - """ - |newSource1.scala:4: error: In an exported method, a *-parameter must come last (through all parameter lists) - | @JSExport - | ^ - """ - - } - - @Test - def noMiddleDefaultParam = { - - """ - class A { - @JSExport - def method(x: Int = 1)(y: String) = 1 - } - """ hasErrors - """ - |newSource1.scala:4: error: In an exported method, all parameters with defaults must be at the end - | @JSExport - | ^ - """ - - } - - @Test - def noExportTrait = { - - """ - @JSExport - trait Test - """ hasErrors - """ - |newSource1.scala:3: error: You may not export a trait - | @JSExport - | ^ - """ - - } - - @Test - def noExportNonPublicClassOrObject = { - - """ - @JSExport - private class A - - @JSExport - protected[this] class B - """ hasErrors - """ - |newSource1.scala:3: error: You may only export public and protected classes - | @JSExport - | ^ - |newSource1.scala:6: error: You may only export public and protected classes - | @JSExport - | ^ - """ - - """ - @JSExport - private object A - - @JSExport - protected[this] object B - """ hasErrors - """ - |newSource1.scala:3: error: You may only export public and protected objects - | @JSExport - | ^ - |newSource1.scala:6: error: You may only export public and protected objects - | @JSExport - | ^ - """ - - } - - @Test - def noExportNonPublicMember = { - - """ - class A { - @JSExport - private def foo = 1 - - @JSExport - protected[this] def bar = 2 - } - """ hasErrors - """ - |newSource1.scala:4: error: You may only export public and protected methods - | @JSExport - | ^ - |newSource1.scala:7: error: You may only export public and protected methods - | @JSExport - | ^ - """ - - } - - @Test - def noExportNestedClass = { - - """ - class A { - @JSExport - class Nested - } - """ hasErrors - """ - |newSource1.scala:4: error: You may not export a nested class. Create an exported factory method in the outer class to work around this limitation. - | @JSExport - | ^ - """ - - """ - object A { - @JSExport - class Nested - } - """ hasErrors - """ - |newSource1.scala:4: error: You may not export a nested class. Create an exported factory method in the outer class to work around this limitation. - | @JSExport - | ^ - """ - - } - - @Test - def noExportNestedObject = { - - """ - class A { - @JSExport - object Nested - } - """ hasErrors - """ - |newSource1.scala:4: error: You may not export a nested object - | @JSExport - | ^ - """ - - """ - object A { - @JSExport - object Nested - } - """ hasErrors - """ - |newSource1.scala:4: error: You may not export a nested object - | @JSExport - | ^ - """ - - } - - @Test - def noExportJSRaw = { - - """ - import scala.scalajs.js - - @JSExport - object A extends js.Object - """ hasErrors - """ - |newSource1.scala:5: error: You may not export a class extending js.Any - | @JSExport - | ^ - """ - - """ - import scala.scalajs.js - - @JSExport - class A extends js.Object - """ hasErrors - """ - |newSource1.scala:5: error: You may not export a constructor of a subclass of js.Any - | @JSExport - | ^ - """ - - } - - @Test - def noExportJSRawMember = { - - """ - import scala.scalajs.js - - class A extends js.Object { - @JSExport - def foo: Int = js.native - } - """ hasErrors - """ - |newSource1.scala:6: error: You may not export a method of a subclass of js.Any - | @JSExport - | ^ - """ - - } - - @Test - def noBadSetterType = { - - // Bad param list - """ - class A { - @JSExport - def foo_=(x: Int, y: Int) = () - } - """ hasErrors - """ - |newSource1.scala:4: error: A method ending in _= will be exported as setter. But foo_= does not have the right signature to do so (single argument, unit return type). - | @JSExport - | ^ - """ - - // Bad return type - """ - class A { - @JSExport - def foo_=(x: Int) = "string" - } - """ hasErrors - """ - |newSource1.scala:4: error: A method ending in _= will be exported as setter. But foo_= does not have the right signature to do so (single argument, unit return type). - | @JSExport - | ^ - """ - - } - - @Test - def noBadToStringExport = { - - """ - class A { - @JSExport("toString") - def a(): Int = 5 - } - """ hasErrors - """ - |newSource1.scala:4: error: You may not export a zero-argument method named other than 'toString' under the name 'toString' - | @JSExport("toString") - | ^ - """ - - } - - @Test - def noBadNameExportAll = { - - """ - @JSExportAll - class A { - val __f = 1 - def a_= = 2 - } - """ hasErrors - """ - |newSource1.scala:5: error: An exported name may not contain a double underscore (`__`) - | val __f = 1 - | ^ - |newSource1.scala:3: error: A method ending in _= will be exported as setter. But a_= does not have the right signature to do so (single argument, unit return type). - | @JSExportAll - | ^ - """ - - } - - @Test - def noConflictingMethodAndProperty = { - - // Basic case - """ - class A { - @JSExport("a") - def bar() = 2 - - @JSExport("a") - val foo = 1 - } - """ hasErrors - """ - |newSource1.scala:7: error: Exported method a conflicts with A.$js$exported$prop$a - | @JSExport("a") - | ^ - |newSource1.scala:4: error: Exported property a conflicts with A.$js$exported$meth$a - | @JSExport("a") - | ^ - """ - - // Inherited case - """ - class A { - @JSExport("a") - def bar() = 2 - } - - class B extends A { - @JSExport("a") - def foo_=(x: Int): Unit = () - - @JSExport("a") - val foo = 1 - } - """ hasErrors - """ - |newSource1.scala:4: error: Exported property a conflicts with A.$js$exported$meth$a - | @JSExport("a") - | ^ - """ - - } - - @Test - def noOverrideNamedExport = { - - """ - class A { - @JSExportNamed - def foo(x: Int, y: Int) = 1 - } - - class B extends A { - @JSExportNamed - override def foo(x: Int, y: Int) = 2 - } - """ hasErrors - """ - |newSource1.scala:9: error: overriding method $js$exported$meth$foo in class A of type (namedArgs: Any)Any; - | method $js$exported$meth$foo cannot override final member - | @JSExportNamed - | ^ - """ - - } - - @Test - def noConflictNamedExport = { - - // Normal method - """ - class A { - @JSExportNamed - def foo(x: Int, y: Int) = 1 - - @JSExport - def foo(x: scala.scalajs.js.Any) = 2 - } - """ fails() // No error test, Scala version dependent error messages - - // Ctors - """ - class A { - @JSExportNamed - def this(x: Int) = this() - - @JSExport - def this(x: scala.scalajs.js.Any) = this - - @JSExportNamed - def this(x: Long) = this() - } - """ fails() // No error test, Scala version dependent error messages - - } - - @Test - def noNamedExportObject = { - - """ - @JSExportNamed - object A - """ hasErrors - """ - |newSource1.scala:3: error: You may not use @JSNamedExport on an object - | @JSExportNamed - | ^ - """ - - } - - @Test - def noNamedExportVarArg = { - - """ - class A { - @JSExportNamed - def foo(a: Int*) = 1 - } - """ hasErrors - """ - |newSource1.scala:4: error: You may not name-export a method with a *-parameter - | @JSExportNamed - | ^ - """ - - } - - @Test - def noNamedExportProperty = { - - // Getter - """ - class A { - @JSExportNamed - def a = 1 - } - """ hasErrors - """ - |newSource1.scala:4: error: You may not export a getter or a setter as a named export - | @JSExportNamed - | ^ - """ - - - // Setter - """ - class A { - @JSExportNamed - def a_=(x: Int) = () - } - """ hasErrors - """ - |newSource1.scala:4: error: You may not export a getter or a setter as a named export - | @JSExportNamed - | ^ - """ - - } - - @Test - def gracefulDoubleDefaultFail = { - // This used to blow up (i.e. not just fail), because PrepJSExports asked - // for the symbol of the default parameter getter of [[y]], and asserted its - // not overloaded. Since the Scala compiler only fails later on this, the - // assert got triggered and made the compiler crash - """ - class A { - @JSExport - def foo(x: String, y: String = "hello") = x - def foo(x: Int, y: String = "bar") = x - } - """ fails() - } - - @Test - def noNonLiteralExportNames = { - - """ - object A { - val a = "Hello" - final val b = "World" - } - - class B { - @JSExport(A.a) - def foo = 1 - @JSExport(A.b) - def bar = 1 - } - """ hasErrors - """ - |newSource1.scala:9: error: The argument to JSExport must be a literal string - | @JSExport(A.a) - | ^ - """ - - } - -} diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSInteropTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSInteropTest.scala deleted file mode 100644 index 99c274f..0000000 --- a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/JSInteropTest.scala +++ /dev/null @@ -1,350 +0,0 @@ -package scala.scalajs.compiler.test - -import scala.scalajs.compiler.test.util._ - -import org.junit.Test -import org.junit.Ignore - -class JSInteropTest extends DirectTest with TestHelpers { - - override def preamble = - """import scala.scalajs.js - """ - - @Test - def noInnerClassTraitObject: Unit = { - - val objs = List("class", "trait", "object") - - for { - outer <- objs - inner <- objs - } yield { - s""" - $outer A extends js.Object { - $inner A - } - """ hasErrors - s""" - |newSource1.scala:4: error: Traits, classes and objects extending js.Any may not have inner traits, classes or objects - | $inner A - | ${" " * inner.length}^ - """ - } - - } - - @Test - def noBadSetters = { - - """ - class A extends js.Object { - def foo_=(x: Int): Int = js.native - } - """ hasErrors - """ - |newSource1.scala:4: error: Setters that do not return Unit are not allowed in types extending js.Any - | def foo_=(x: Int): Int = js.native - | ^ - """ - - } - - @Test - def onlyJSRawTraits = { - - """ - trait A - class B extends js.Object with A - """ hasErrors - """ - |newSource1.scala:4: error: B extends A which does not extend js.Any. - | class B extends js.Object with A - | ^ - """ - - """ - trait A - class B extends js.Object with Serializable - """ hasErrors - """ - |newSource1.scala:4: error: B extends scala.Serializable which does not extend js.Any. - | class B extends js.Object with Serializable - | ^ - """ - - } - - @Test - def noAnonymousClass = { - - """ - class A { - val x = new js.Object { - def a: Int = js.native - } - } - """ hasErrors - """ - |newSource1.scala:4: error: Anonymous classes may not extend js.Any - | val x = new js.Object { - | ^ - """ - - } - - @Test - def noCaseClassObject = { - - """ - case class A(x: Int) extends js.Object - """ hasErrors - """ - |newSource1.scala:3: error: Classes and objects extending js.Any may not have a case modifier - | case class A(x: Int) extends js.Object - | ^ - """ - - """ - case object B extends js.Object - """ hasErrors - """ - |newSource1.scala:3: error: Classes and objects extending js.Any may not have a case modifier - | case object B extends js.Object - | ^ - """ - - } - - @Test - def notNested: Unit = { - - val outers = List("class", "trait") - val inners = List("trait", "class", "object") - - for { - outer <- outers - inner <- inners - } yield { - - val errTrg = if (inner == "object") "Objects" else "Classes" - - s""" - $outer A { - $inner Inner extends js.Object - } - """ hasErrors - s""" - |newSource1.scala:4: error: $errTrg extending js.Any may not be defined inside a class or trait - | $inner Inner extends js.Object - | ${" " * inner.length}^ - """ - } - - } - - @Test - def noGlobalScopeClass = { - - """ - class A extends js.GlobalScope - """ hasErrors - """ - |newSource1.scala:3: error: Only objects may extend js.GlobalScope - | class A extends js.GlobalScope - | ^ - """ - - """ - trait A extends js.GlobalScope - """ hasErrors - """ - |newSource1.scala:3: error: Only objects may extend js.GlobalScope - | trait A extends js.GlobalScope - | ^ - """ - - } - - @Test - def noLocalClass = { - - """ - object A { - def a = { - class B extends js.Object - } - } - """ hasErrors - """ - |newSource1.scala:5: error: Local classes and objects may not extend js.Any - | class B extends js.Object - | ^ - """ - - } - - @Test - def noLocalObject = { - - """ - object A { - def a = { - object B extends js.Object - } - } - """ hasErrors - """ - |newSource1.scala:5: error: Local classes and objects may not extend js.Any - | object B extends js.Object - | ^ - """ - - } - - @Test - def noExtendJSAny = { - - """ - class A extends js.Any - """ hasErrors - """ - |newSource1.scala:3: error: illegal inheritance from sealed trait Any - | class A extends js.Any - | ^ - """ - - } - - @Test - def noNativeInJSAny = { - - """ - class A extends js.Object { - @native - def value: Int = js.native - } - """ hasErrors - """ - |newSource1.scala:5: error: Methods in a js.Any may not be @native - | def value: Int = js.native - | ^ - """ - - } - - @Test - def warnJSAnyBody = { - - """ - class A extends js.Object { - def value: Int = ??? - val x: Int = ??? - } - """ hasWarns - """ - |newSource1.scala:4: warning: Members of traits, classes and objects extending js.Any may only contain members that call js.native. This will be enforced in 1.0. - | def value: Int = ??? - | ^ - |newSource1.scala:5: warning: Members of traits, classes and objects extending js.Any may only contain members that call js.native. This will be enforced in 1.0. - | val x: Int = ??? - | ^ - """ - - """ - trait A extends js.Object { - def value: Int - val x: Int - } - """ hasWarns - """ - |newSource1.scala:4: warning: Members of traits, classes and objects extending js.Any may only contain members that call js.native. This will be enforced in 1.0. - | def value: Int - | ^ - |newSource1.scala:5: warning: Members of traits, classes and objects extending js.Any may only contain members that call js.native. This will be enforced in 1.0. - | val x: Int - | ^ - """ - - } - - @Test - def noCallSecondaryCtor = { - - """ - class A(x: Int, y: Int) extends js.Object { - def this(x: Int) = this(x, 5) - def this() = this(7) - } - """ hasErrors - """ - |newSource1.scala:5: error: A secondary constructor of a class extending js.Any may only call the primary constructor - | def this() = this(7) - | ^ - """ - - } - - @Test - def noUseJsNative = { - - """ - class A { - def foo = js.native - } - """ hasErrors - """ - |newSource1.scala:4: error: js.native may only be used as stub implementation in facade types - | def foo = js.native - | ^ - """ - - } - - @Test - def warnNothingRaw = { - - """ - class A extends js.Object { - def foo = js.native - val bar = js.native - } - """ hasWarns - """ - |newSource1.scala:4: warning: The type of foo got inferred as Nothing. To suppress this warning, explicitly ascribe the type. - | def foo = js.native - | ^ - |newSource1.scala:5: warning: The type of bar got inferred as Nothing. To suppress this warning, explicitly ascribe the type. - | val bar = js.native - | ^ - """ - - } - - @Test - def noNonLiteralJSName = { - - """ - import js.annotation.JSName - - object A { - val a = "Hello" - final val b = "World" - } - - class B extends js.Object { - @JSName(A.a) - def foo: Int = js.native - @JSName(A.b) - def bar: Int = js.native - } - """ hasErrors - """ - |newSource1.scala:11: error: The argument to JSName must be a literal string - | @JSName(A.a) - | ^ - """ - - } - -} diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/OptimizationTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/OptimizationTest.scala deleted file mode 100644 index 7f15c7a..0000000 --- a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/OptimizationTest.scala +++ /dev/null @@ -1,109 +0,0 @@ -package scala.scalajs.compiler.test - -import util._ - -import org.junit.Test - -import scala.scalajs.ir.{Trees => js, Types => jstpe} - -class OptimizationTest extends JSASTTest { - - @Test - def unwrapScalaFunWrapper: Unit = { - - // Make sure we do not wrap and unwrap right away - """ - import scala.scalajs.js - - class A { - val jsFun: js.Function = (x: Int) => x * 2 - } - """. - hasNot("runtime.AnonFunction ctor") { - case js.New(jstpe.ClassType("sjsr_AnonFunction1"), _, _) => - } - - // Make sure our wrapper matcher has the right name - """ - import scala.scalajs.js - - class A { - val scalaFun = (x: Int) => x * 2 - val jsFun: js.Function = scalaFun - } - """. - has("runtime.AnonFunction ctor") { - case js.New(jstpe.ClassType("sjsr_AnonFunction1"), _, _) => - } - - /* Make sure js.Array(...) is optimized away completely for several kinds - * of data types. - */ - """ - import scala.scalajs.js - - class VC(val x: Int) extends AnyVal - - class A { - val a = js.Array(5, 7, 9, -3) - val b = js.Array("hello", "world") - val c = js.Array('a', 'b') - val d = js.Array(Nil) - val e = js.Array(new VC(151189)) - } - """. - hasNot("any of the wrapArray methods") { - case js.Apply(_, js.Ident(name, _), _) - if name.startsWith("wrap") && name.endsWith("__scm_WrappedArray") => - } - - /* Make sure varargs are optimized to use js.WrappedArray instead of - * scm.WrappedArray, for various data types. - */ - """ - import scala.scalajs.js - - class VC(val x: Int) extends AnyVal - - class A { - val a = List(5, 7, 9, -3) - val b = List("hello", "world") - val c = List('a', 'b') - val d = List(Nil) - val e = List(new VC(151189)) - } - """. - hasNot("any of the wrapArray methods") { - case js.Apply(_, js.Ident(name, _), _) - if name.startsWith("wrap") && name.endsWith("__scm_WrappedArray") => - } - - // Make sure our wrapper matcher has the right name - """ - import scala.scalajs.js - - class A { - val a: Seq[Int] = new Array[Int](5) - } - """. - has("one of the wrapArray methods") { - case js.Apply(_, js.Ident(name, _), _) - if name.startsWith("wrap") && name.endsWith("__scm_WrappedArray") => - } - - // Verify the optimized emitted code for 'new js.Object' and 'new js.Array' - """ - import scala.scalajs.js - - class A { - val o = new js.Object - val a = new js.Array - } - """. - hasNot("any reference to the global scope") { - case js.JSEnvInfo() => - } - - } - -} diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/PositionTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/PositionTest.scala deleted file mode 100644 index e25399b..0000000 --- a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/PositionTest.scala +++ /dev/null @@ -1,37 +0,0 @@ -package scala.scalajs.compiler.test - -import util.JSASTTest - -import org.junit.Test -import org.junit.Assert._ - -import scala.reflect.internal.util.BatchSourceFile - -import scala.scalajs.ir.{Trees => js} - -class PositionTest extends JSASTTest { - - @Test - def virtualFilePosition = { - - val name = "<foo with illegal URI chars: %%>" - val source = new BatchSourceFile(name, - """class A { def x = 1 }""") - - var found = false - sourceAST(source) traverse { - case lit: js.IntLiteral => - found = true - assertEquals( - "Scheme of virtual file URI should be `virtualfile'", - "virtualfile", lit.pos.source.getScheme) - assertEquals( - "Scheme specific part of virtual file URI should be its path", - name, lit.pos.source.getSchemeSpecificPart) - } - - assertTrue("Should have IntLiteral tree", found) - - } - -} diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/DirectTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/DirectTest.scala deleted file mode 100644 index 8289129..0000000 --- a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/DirectTest.scala +++ /dev/null @@ -1,70 +0,0 @@ -package scala.scalajs.compiler.test.util - -import scala.tools.nsc._ -import reporters.{Reporter, ConsoleReporter} -import scala.reflect.internal.util.{ SourceFile, BatchSourceFile } - -import scala.scalajs.compiler.ScalaJSPlugin - -import scala.collection.mutable - -/** This is heavily inspired by scala's partest suite's DirectTest */ -abstract class DirectTest { - - /** these arguments are always added to the args passed to newSettings */ - def extraArgs: List[String] = Nil - - /** create settings objects for test from arg string */ - def newSettings(args: List[String]) = { - val s = new Settings - s processArguments (args, true) - s - } - - def newScalaJSCompiler(args: String*): Global = { - val settings = newSettings( - List( - "-d", testOutputPath, - "-bootclasspath", scalaLibPath, - "-classpath", scalaJSLibPath) ++ - extraArgs ++ args.toList) - - lazy val global: Global = new Global(settings, newReporter(settings)) { - override lazy val plugins = newScalaJSPlugin(global) :: Nil - } - - global - } - - def newScalaJSPlugin(global: Global): ScalaJSPlugin = - new ScalaJSPlugin(global) - - def newReporter(settings: Settings) = new ConsoleReporter(settings) - - def newSources(codes: String*) = codes.toList.zipWithIndex map { - case (src, idx) => new BatchSourceFile(s"newSource${idx + 1}.scala", src) - } - - def withRun[T](global: Global)(f: global.Run => T): T = { - global.reporter.reset() - f(new global.Run) - } - - def compileSources(global: Global)(sources: SourceFile*): Boolean = { - withRun(global)(_ compileSources sources.toList) - !global.reporter.hasErrors - } - - def compileString(global: Global)(sourceCode: String): Boolean = - compileSources(global)(newSources(sourceCode): _*) - - def compileString(sourceCode: String): Boolean = - compileString(defaultGlobal)(sourceCode) - - lazy val defaultGlobal = newScalaJSCompiler() - - def testOutputPath = sys.props("scala.scalajs.compiler.test.output") - def scalaJSLibPath = sys.props("scala.scalajs.compiler.test.scalajslib") - def scalaLibPath = sys.props("scala.scalajs.compiler.test.scalalib") - -} diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/JSASTTest.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/JSASTTest.scala deleted file mode 100644 index d3dfd75..0000000 --- a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/JSASTTest.scala +++ /dev/null @@ -1,100 +0,0 @@ -package scala.scalajs.compiler.test.util - -import language.implicitConversions - -import scala.tools.nsc._ -import scala.reflect.internal.util.SourceFile - -import scala.util.control.ControlThrowable - -import org.junit.Assert._ - -import scala.scalajs.compiler.{ScalaJSPlugin, JSTreeExtractors} -import JSTreeExtractors.jse -import scala.scalajs.ir -import ir.{Trees => js} - -abstract class JSASTTest extends DirectTest { - - private var lastAST: JSAST = _ - - class JSAST(val clDefs: List[js.Tree]) { - type Pat = PartialFunction[js.Tree, Unit] - - class PFTraverser(pf: Pat) extends ir.Traversers.Traverser { - private case object Found extends ControlThrowable - - private[this] var finding = false - - def find: Boolean = { - finding = true - try { - clDefs.map(traverse) - false - } catch { - case Found => true - } - } - - def traverse(): Unit = { - finding = false - clDefs.map(traverse) - } - - override def traverse(tree: js.Tree): Unit = { - if (finding && pf.isDefinedAt(tree)) - throw Found - - if (!finding) - pf.lift(tree) - - super.traverse(tree) - } - } - - def has(trgName: String)(pf: Pat): this.type = { - val tr = new PFTraverser(pf) - assertTrue(s"AST should have $trgName", tr.find) - this - } - - def hasNot(trgName: String)(pf: Pat): this.type = { - val tr = new PFTraverser(pf) - assertFalse(s"AST should not have $trgName", tr.find) - this - } - - def traverse(pf: Pat): this.type = { - val tr = new PFTraverser(pf) - tr.traverse() - this - } - - def show: this.type = { - clDefs foreach println _ - this - } - - } - - implicit def string2ast(str: String): JSAST = stringAST(str) - - override def newScalaJSPlugin(global: Global) = new ScalaJSPlugin(global) { - override def generatedJSAST(cld: List[js.Tree]): Unit = { - lastAST = new JSAST(cld) - } - } - - def stringAST(code: String): JSAST = stringAST(defaultGlobal)(code) - def stringAST(global: Global)(code: String): JSAST = { - compileString(global)(code) - lastAST - } - - def sourceAST(source: SourceFile): JSAST = sourceAST(defaultGlobal)(source) - def sourceAST(global: Global)(source: SourceFile): JSAST = { - compileSources(global)(source) - lastAST - } - -} diff --git a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/TestHelpers.scala b/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/TestHelpers.scala deleted file mode 100644 index adad89c..0000000 --- a/examples/scala-js/compiler/src/test/scala/scala/scalajs/compiler/test/util/TestHelpers.scala +++ /dev/null @@ -1,68 +0,0 @@ -package scala.scalajs.compiler.test.util - -import java.io._ -import scala.tools.nsc._ - -import reporters.ConsoleReporter - -import org.junit.Assert._ - -import scala.util.matching.Regex - -trait TestHelpers extends DirectTest { - - private[this] val errBuffer = new CharArrayWriter - - override def newReporter(settings: Settings) = { - val in = new BufferedReader(new StringReader("")) - val out = new PrintWriter(errBuffer) - new ConsoleReporter(settings, in, out) - } - - /** will be prefixed to every code that is compiled. use for imports */ - def preamble = "" - - /** pimps a string to compile it and apply the specified test */ - implicit class CompileTests(val code: String) { - - def hasErrors(expected: String) = { - val reps = repResult { - assertFalse("snippet shouldn't compile", compileString(preamble + code)) - } - assertEquals("should have right errors", - expected.stripMargin.trim, reps.trim) - } - - def hasWarns(expected: String) = { - val reps = repResult { - assertTrue("snippet should compile", compileString(preamble + code)) - } - assertEquals("should have right warnings", - expected.stripMargin.trim, reps.trim) - } - - def fails() = - assertFalse("snippet shouldn't compile", compileString(preamble + code)) - - def warns() = { - val reps = repResult { - assertTrue("snippet should compile", compileString(preamble + code)) - } - assertFalse("should have warnings", reps.isEmpty) - } - - def succeeds() = - assertTrue("snippet should compile", compileString(preamble + code)) - - private def repResult(body: => Unit) = { - errBuffer.reset() - body - errBuffer.toString - } - } - - implicit class CodeWrappers(sc: StringContext) { - def expr() = new CompileTests(s"class A { ${sc.parts.mkString} }") - } - -} |