summaryrefslogtreecommitdiff
path: root/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala')
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala1525
1 files changed, 0 insertions, 1525 deletions
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala
deleted file mode 100644
index b4d4005..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala
+++ /dev/null
@@ -1,1525 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-import scala.language.implicitConversions
-
-import scala.annotation.switch
-
-import scala.scalajs.ir._
-import Position._
-import Transformers._
-import scala.scalajs.ir.Trees._
-import scala.scalajs.ir.Types._
-
-import scala.scalajs.tools.sem._
-import CheckedBehavior._
-
-import scala.scalajs.tools.javascript.{Trees => js}
-
-/** Desugaring of the IR to regular ES5 JavaScript.
- *
- * The major difference between the IR and JS is that most constructs can be
- * used in expression position. The main work of the desugaring is to
- * unnest complex constructs in expression position so that they become
- * statements.
- *
- * The general idea is two-folded:
- * 1) Unnest complex constructs in "argument position":
- * When a complex construct is used in a non-rhs expression position
- * (argument to a function, operand, condition of an if, etc.), that we
- * call "argument position", declare a variable before the statement,
- * assign the complex construct to it and then use that variable in the
- * argument position instead.
- * 2) Push LHS's inside complex RHS's:
- * When an rhs is a complex construct, push the lhs inside the complex
- * construct. Are considered lhs:
- * * Assign, i.e., `x =`
- * * VarDef, i.e., `var x =`
- * * Return, i.e., `return`
- * * (EmptyTree is also used as a trick for code reuse)
- * In fact, think that, in this context, LHS means: what to do with the
- * result of evaluating the RHS.
- *
- * --------------------------------------------------------------------------
- *
- * Typical example, consider the method call:
- *
- * obj.meth({
- * var x = foo(42);
- * x*x
- * });
- *
- * According to rule 1), the block that is passed as a parameter to obj.meth
- * is first extracted in a synthetic var:
- *
- * var x\$1 = {
- * var x = foo(42);
- * x*x
- * }
- * obj.meth(x\$1);
- *
- * Then, according to rule 2), the lhs `var x\$1 =` is pushed inside the block:
- *
- * {
- * var x = foo(42);
- * var x\$1 = x*x;
- * }
- * obj.meth(x\$1);
- *
- * Because bare blocks are non-significant in JS, this is equivalent to
- *
- * var x = foo(42);
- * var x\$1 = x*x;
- * obj.meth(x\$1);
- *
- * --------------------------------------------------------------------------
- *
- * JSDesugaring does all this in a single pass, but it helps to think that:
- * * Rule 1) is implemented by unnest(), and used most notably in
- * * transformStat() for statement-only constructs
- * * pushLhsInto() for statement-or-expression constructs
- * * Rule 2) is implemented by pushLhsInto()
- * * Emitting the class structure is delegated to [[ScalaJSClassEmitter]].
- *
- * There are a few other things that JSDesugaring takes care of:
- * * Transform Scala expressions into their JS equivalent, taking the
- * Scala.js class encoding into account.
- * * And tiny details.
- *
- * @author Sébastien Doeraene
- */
-object JSDesugaring {
-
- private final val ScalaJSEnvironmentName = "ScalaJS"
-
- /** Desugars a statement of the IR into ES5 JavaScript. */
- def desugarJavaScript(tree: Tree, semantics: Semantics): js.Tree = {
- new JSDesugar(semantics).transformStat(tree)
- }
-
- private[javascript] implicit def transformIdent(ident: Ident): js.Ident =
- js.Ident(ident.name, ident.originalName)(ident.pos)
-
- private[javascript] def transformParamDef(paramDef: ParamDef): js.ParamDef =
- js.ParamDef(paramDef.name, paramDef.mutable)(paramDef.pos)
-
- private class JSDesugar(semantics: Semantics) {
-
- // Synthetic variables
-
- var syntheticVarCounter: Int = 0
-
- def newSyntheticVar()(implicit pos: Position): Ident = {
- syntheticVarCounter += 1
- Ident("jsx$" + syntheticVarCounter, None)
- }
-
- def resetSyntheticVarCounterIn[A](f: => A): A = {
- val savedCounter = syntheticVarCounter
- syntheticVarCounter = 0
- try f
- finally syntheticVarCounter = savedCounter
- }
-
- // Record names
-
- def makeRecordFieldIdent(recIdent: Ident, fieldIdent: Ident)(
- implicit pos: Position): Ident =
- makeRecordFieldIdent(recIdent.name, recIdent.originalName,
- fieldIdent.name, fieldIdent.originalName)
-
- def makeRecordFieldIdent(recIdent: Ident,
- fieldName: String, fieldOrigiName: Option[String])(
- implicit pos: Position): Ident =
- makeRecordFieldIdent(recIdent.name, recIdent.originalName,
- fieldName, fieldOrigiName)
-
- def makeRecordFieldIdent(recName: String, recOrigName: Option[String],
- fieldName: String, fieldOrigName: Option[String])(
- implicit pos: Position): Ident = {
- val name = recName + "_$_" + fieldName
- val originalName = Some(recOrigName.getOrElse(recName) + "." +
- fieldOrigName.getOrElse(fieldName))
- Ident(name, originalName)
- }
-
- // LHS'es for labeled expressions
-
- var labeledExprLHSes: Map[Ident, Tree] = Map.empty
-
- // Now the work
-
- /** Desugar a statement of the IR into ES5 JS */
- def transformStat(tree: Tree): js.Tree = {
- implicit val pos = tree.pos
-
- tree match {
- // Statement-only language constructs
-
- case Skip() =>
- js.Skip()
-
- case VarDef(varIdent, RecordType(fields), recMutable, EmptyTree) =>
- js.Block(for {
- RecordType.Field(fieldName, fieldOrigName, tpe, fieldMutable) <- fields
- } yield {
- transformStat {
- VarDef(makeRecordFieldIdent(varIdent, fieldName, fieldOrigName),
- tpe, recMutable || fieldMutable, EmptyTree)
- }
- })
-
- case VarDef(name, _, mutable, EmptyTree) =>
- js.VarDef(name, mutable, js.EmptyTree)
-
- case VarDef(_, _, _, rhs) =>
- pushLhsInto(tree, rhs)
-
- case Assign(RecordFieldVarRef(lhs), rhs) =>
- pushLhsInto(Assign(lhs, EmptyTree), rhs)
-
- case Assign(select @ Select(qualifier, item, mutable), rhs) =>
- unnest(qualifier, rhs) { (newQualifier, newRhs) =>
- js.Assign(
- js.DotSelect(transformExpr(newQualifier), item)(select.pos),
- transformExpr(newRhs))
- }
-
- case Assign(select @ ArraySelect(array, index), rhs) =>
- unnest(List(array, index, rhs)) {
- case List(newArray, newIndex, newRhs) =>
- js.Assign(
- js.BracketSelect(js.DotSelect(transformExpr(newArray),
- js.Ident("u"))(select.pos),
- transformExpr(newIndex))(select.pos),
- transformExpr(newRhs))
- }
-
- case Assign(select @ JSDotSelect(qualifier, item), rhs) =>
- unnest(qualifier, rhs) { (newQualifier, newRhs) =>
- js.Assign(
- js.DotSelect(transformExpr(newQualifier), item)(select.pos),
- transformExpr(newRhs))
- }
-
- case Assign(select @ JSBracketSelect(qualifier, item), rhs) =>
- unnest(List(qualifier, item, rhs)) {
- case List(newQualifier, newItem, newRhs) =>
- js.Assign(
- js.BracketSelect(transformExpr(newQualifier),
- transformExpr(newItem))(select.pos),
- transformExpr(newRhs))
- }
-
- case Assign(_ : VarRef, rhs) =>
- pushLhsInto(tree, rhs)
-
- case Assign(_, _) =>
- sys.error(s"Illegal Assign in transformStat: $tree")
-
- case StoreModule(cls, value) =>
- assert(cls.className.endsWith("$"),
- s"Trying to store module for non-module class $cls")
- val moduleName = cls.className.dropRight(1)
- unnest(value) { newValue =>
- js.Assign(
- js.DotSelect(envField("n"), Ident(moduleName)),
- transformExpr(newValue))
- }
-
- case While(cond, body, label) =>
- /* We cannot simply unnest(cond) here, because that would eject the
- * evaluation of the condition out of the loop.
- */
- val newLabel = label.map(transformIdent)
- if (isExpression(cond)) {
- js.While(transformExpr(cond), transformStat(body), newLabel)
- } else {
- js.While(js.BooleanLiteral(true), {
- unnest(cond) { newCond =>
- js.If(transformExpr(newCond), transformStat(body), js.Break())
- }
- }, newLabel)
- }
-
- case DoWhile(body, cond, label) =>
- /* We cannot simply unnest(cond) here, because that would eject the
- * evaluation of the condition out of the loop.
- */
- val newLabel = label.map(transformIdent)
- if (isExpression(cond)) {
- js.DoWhile(transformStat(body), transformExpr(cond), newLabel)
- } else {
- /* This breaks 'continue' statements for this loop, but we don't
- * care because we never emit continue statements for do..while
- * loops.
- */
- js.While(js.BooleanLiteral(true), {
- js.Block(transformStat(body), {
- unnest(cond) { newCond =>
- js.If(transformExpr(newCond), js.Skip(), js.Break())
- }
- })
- }, newLabel)
- }
-
- case Debugger() =>
- js.Debugger()
-
- case JSDelete(JSDotSelect(obj, prop)) =>
- unnest(obj) { (newObj) =>
- js.Delete(js.DotSelect(transformExpr(newObj), prop))
- }
-
- case JSDelete(JSBracketSelect(obj, prop)) =>
- unnest(obj, prop) { (newObj, newProp) =>
- js.Delete(js.BracketSelect(
- transformExpr(newObj), transformExpr(newProp)))
- }
-
- // Treat 'return' as an LHS
-
- case Return(expr, label) =>
- pushLhsInto(tree, expr)
-
- /* Anything else is an expression => pushLhsInto(EmptyTree, _)
- * In order not to duplicate all the code of pushLhsInto() here, we
- * use a trick: EmptyTree is a dummy LHS that says "do nothing
- * with the result of the rhs".
- * This is exactly what an expression statement is doing: it evaluates
- * the expression, but does nothing with its result.
- */
-
- case _ =>
- pushLhsInto(EmptyTree, tree)
- }
- }
-
- private object RecordFieldVarRef {
- def unapply(tree: Tree): Option[VarRef] = {
- tree match {
- case Select(RecordVarRef(VarRef(recIdent, recMutable)),
- fieldIdent, fieldMutable) =>
- implicit val pos = tree.pos
- Some(VarRef(makeRecordFieldIdent(recIdent, fieldIdent),
- recMutable || fieldMutable)(tree.tpe))
- case _ =>
- None
- }
- }
- }
-
- private object RecordVarRef {
- def unapply(tree: Tree): Option[VarRef] = {
- if (!tree.tpe.isInstanceOf[RecordType]) None
- else {
- tree match {
- case tree: VarRef => Some(tree)
- case Select(RecordVarRef(VarRef(recIdent, recMutable)),
- fieldIdent, fieldMutable) =>
- implicit val pos = tree.pos
- Some(VarRef(makeRecordFieldIdent(recIdent, fieldIdent),
- recMutable || fieldMutable)(tree.tpe))
- }
- }
- }
- }
-
- /** Unnest complex constructs in argument position in temporary variables
- *
- * If all the arguments are JS expressions, there is nothing to do.
- * Any argument that is not a JS expression must be unnested and stored
- * in a temporary variable before the statement produced by `makeStat`.
- *
- * But *this changes the evaluation order!* In order not to lose it, it
- * is necessary to also unnest arguments that are expressions but that
- * are supposed to be evaluated before the argument-to-be-unnested and
- * could have side-effects or even whose evaluation could be influenced
- * by the side-effects of another unnested argument.
- *
- * Without deep effect analysis, which we do not do, we need to take
- * a very pessimistic approach, and unnest any expression that contains
- * an identifier (except those after the last non-expression argument).
- * Hence the predicate `isPureExpressionWithoutIdent`.
- */
- def unnest(args: List[Tree])(
- makeStat: List[Tree] => js.Tree): js.Tree = {
- if (args forall isExpression) makeStat(args)
- else {
- val extractedStatements = new scala.collection.mutable.ListBuffer[js.Tree]
-
- /* Attention! Everything must be processed recursively
- * *right-to-left*! Indeed, the point is that noExtractYet will tell
- * whether anything supposed to be evaluated *after* the currently
- * being processed expression has been (at least partly) extracted
- * in temporary variables (or simply statements, in the Block case).
- * If nothing has, we can keep more in place without having to extract
- * that expression in a temporary variable.
- */
-
- def rec(arg: Tree): Tree = {
- def noExtractYet = extractedStatements.isEmpty
-
- if (if (noExtractYet) isExpression(arg) else isPureExpression(arg)) {
- arg
- } else {
- implicit val pos = arg.pos
- arg match {
- case Block(stats :+ expr) =>
- val result = rec(expr) // right-to-left, remember?
- // Put the stats in a Block because ++=: is not smart
- js.Block(stats.map(transformStat)) +=: extractedStatements
- result
-
- case UnaryOp(op, lhs) =>
- UnaryOp(op, rec(lhs))
- case BinaryOp(op, lhs, rhs) =>
- val newRhs = rec(rhs)
- BinaryOp(op, rec(lhs), newRhs)
- case JSBinaryOp(op, lhs, rhs) =>
- val newRhs = rec(rhs)
- JSBinaryOp(op, rec(lhs), newRhs)
- case JSUnaryOp(op, lhs) =>
- JSUnaryOp(op, rec(lhs))
- case IsInstanceOf(expr, tpe) =>
- IsInstanceOf(rec(expr), tpe)
-
- case AsInstanceOf(expr, tpe)
- if noExtractYet || semantics.asInstanceOfs == Unchecked =>
- AsInstanceOf(rec(expr), tpe)
- case Unbox(expr, tpe)
- if noExtractYet || semantics.asInstanceOfs == Unchecked =>
- Unbox(rec(expr), tpe)
-
- case NewArray(tpe, lengths) =>
- NewArray(tpe, recs(lengths))
- case ArrayValue(tpe, elems) =>
- ArrayValue(tpe, recs(elems))
- case JSArrayConstr(items) =>
- JSArrayConstr(recs(items))
- case JSObjectConstr(items) =>
- val newValues = recs(items.map(_._2))
- JSObjectConstr(items.map(_._1) zip newValues)
- case Closure(captureParams, params, body, captureValues) =>
- Closure(captureParams, params, body, recs(captureValues))
-
- case New(cls, constr, args) if noExtractYet =>
- New(cls, constr, recs(args))
- case Select(qualifier, item, mutable) if noExtractYet =>
- Select(rec(qualifier), item, mutable)(arg.tpe)
- case Apply(receiver, method, args) if noExtractYet =>
- val newArgs = recs(args)
- Apply(rec(receiver), method, newArgs)(arg.tpe)
- case StaticApply(receiver, cls, method, args) if noExtractYet =>
- val newArgs = recs(args)
- StaticApply(rec(receiver), cls, method, newArgs)(arg.tpe)
- case TraitImplApply(impl, method, args) if noExtractYet =>
- TraitImplApply(impl, method, recs(args))(arg.tpe)
- case ArrayLength(array) if noExtractYet =>
- ArrayLength(rec(array))
- case ArraySelect(array, index) if noExtractYet =>
- val newIndex = rec(index)
- ArraySelect(rec(array), newIndex)(arg.tpe)
- case CallHelper(helper, args) if noExtractYet =>
- CallHelper(helper, recs(args))(arg.tpe)
-
- case If(cond, thenp, elsep)
- if noExtractYet && isExpression(thenp) && isExpression(elsep) =>
- If(rec(cond), thenp, elsep)(arg.tpe)
-
- case _ =>
- val temp = newSyntheticVar()
- val computeTemp =
- pushLhsInto(VarDef(temp, arg.tpe, mutable = false, EmptyTree), arg)
- computeTemp +=: extractedStatements
- VarRef(temp, mutable = false)(arg.tpe)
- }
- }
- }
-
- def recs(args: List[Tree]): List[Tree] = {
- // This is a right-to-left map
- args.foldRight[List[Tree]](Nil) { (arg, acc) =>
- rec(arg) :: acc
- }
- }
-
- val newArgs = recs(args)
-
- assert(extractedStatements.nonEmpty,
- "Reached computeTemps with no temp to compute")
-
- val newStatement = makeStat(newArgs)
- js.Block(extractedStatements.result() ::: List(newStatement))(newStatement.pos)
- }
- }
-
- /** Same as above, for a single argument */
- def unnest(arg: Tree)(
- makeStat: Tree => js.Tree): js.Tree = {
- unnest(List(arg)) {
- case List(newArg) => makeStat(newArg)
- }
- }
-
- /** Same as above, for two arguments */
- def unnest(lhs: Tree, rhs: Tree)(
- makeStat: (Tree, Tree) => js.Tree): js.Tree = {
- unnest(List(lhs, rhs)) {
- case List(newLhs, newRhs) => makeStat(newLhs, newRhs)
- }
- }
-
- /** Same as above, for one head argument and a list of arguments */
- def unnest(arg0: Tree, args: List[Tree])(
- makeStat: (Tree, List[Tree]) => js.Tree): js.Tree = {
- unnest(arg0 :: args) { newArgs =>
- makeStat(newArgs.head, newArgs.tail)
- }
- }
-
- /** Common implementation for the functions below.
- * A pure expression can be moved around or executed twice, because it
- * will always produce the same result and never have side-effects.
- * A side-effect free expression can be elided if its result is not used.
- */
- private def isExpressionInternal(tree: Tree,
- allowUnpure: Boolean, allowSideEffects: Boolean): Boolean = {
-
- require(!allowSideEffects || allowUnpure)
-
- def test(tree: Tree): Boolean = tree match {
- // Atomic expressions
- case _: Literal => true
- case _: This => true
- case _: JSEnvInfo => true
-
- // Vars and fields (side-effect free, pure if immutable)
- case VarRef(_, mutable) =>
- allowUnpure || !mutable
- case Select(qualifier, item, mutable) =>
- (allowUnpure || !mutable) && test(qualifier)
-
- // Expressions preserving pureness
- case Block(trees) => trees forall test
- case If(cond, thenp, elsep) => test(cond) && test(thenp) && test(elsep)
- case BinaryOp(_, lhs, rhs) => test(lhs) && test(rhs)
- case UnaryOp(_, lhs) => test(lhs)
- case JSBinaryOp(_, lhs, rhs) => test(lhs) && test(rhs)
- case JSUnaryOp(_, lhs) => test(lhs)
- case ArrayLength(array) => test(array)
- case IsInstanceOf(expr, _) => test(expr)
-
- // Expressions preserving side-effect freedom
- case NewArray(tpe, lengths) =>
- allowUnpure && (lengths forall test)
- case ArrayValue(tpe, elems) =>
- allowUnpure && (elems forall test)
- case ArraySelect(array, index) =>
- allowUnpure && test(array) && test(index)
- case JSArrayConstr(items) =>
- allowUnpure && (items forall test)
- case JSObjectConstr(items) =>
- allowUnpure && (items forall (item => test(item._2)))
- case Closure(captureParams, params, body, captureValues) =>
- allowUnpure && (captureValues forall test)
-
- // Scala expressions that can always have side-effects
- case New(cls, constr, args) =>
- allowSideEffects && (args forall test)
- case LoadModule(cls) => // unfortunately
- allowSideEffects
- case Apply(receiver, method, args) =>
- allowSideEffects && test(receiver) && (args forall test)
- case StaticApply(receiver, cls, method, args) =>
- allowSideEffects && test(receiver) && (args forall test)
- case TraitImplApply(impl, method, args) =>
- allowSideEffects && (args forall test)
- case GetClass(arg) =>
- allowSideEffects && test(arg)
- case CallHelper(helper, args) =>
- allowSideEffects && (args forall test)
-
- // Casts
- case AsInstanceOf(expr, _) =>
- (allowSideEffects || semantics.asInstanceOfs == Unchecked) && test(expr)
- case Unbox(expr, _) =>
- (allowSideEffects || semantics.asInstanceOfs == Unchecked) && test(expr)
-
- // Because the env is a frozen object, env["global"] is pure
- case JSBracketSelect(JSEnvInfo(), StringLiteral("global")) => true
-
- // JavaScript expressions that can always have side-effects
- case JSNew(fun, args) =>
- allowSideEffects && test(fun) && (args forall test)
- case JSDotSelect(qualifier, item) =>
- allowSideEffects && test(qualifier)
- case JSBracketSelect(qualifier, item) =>
- allowSideEffects && test(qualifier) && test(item)
- case JSFunctionApply(fun, args) =>
- allowSideEffects && test(fun) && (args forall test)
- case JSDotMethodApply(receiver, method, args) =>
- allowSideEffects && test(receiver) && (args forall test)
- case JSBracketMethodApply(receiver, method, args) =>
- allowSideEffects && test(receiver) && test(method) && (args forall test)
-
- // Non-expressions
- case _ => false
- }
- test(tree)
- }
-
- /** Test whether the given tree is a standard JS expression.
- */
- def isExpression(tree: Tree): Boolean =
- isExpressionInternal(tree, allowUnpure = true, allowSideEffects = true)
-
- /** Test whether the given tree is a side-effect-free standard JS expression.
- */
- def isSideEffectFreeExpression(tree: Tree): Boolean =
- isExpressionInternal(tree, allowUnpure = true, allowSideEffects = false)
-
- /** Test whether the given tree is a pure standard JS expression.
- */
- def isPureExpression(tree: Tree): Boolean =
- isExpressionInternal(tree, allowUnpure = false, allowSideEffects = false)
-
- def doVarDef(ident: Ident, tpe: Type, mutable: Boolean, rhs: Tree): js.Tree = {
- implicit val pos = rhs.pos
- tpe match {
- case RecordType(fields) =>
- val elems = (rhs: @unchecked) match {
- case RecordValue(_, elems) =>
- elems
- case VarRef(rhsIdent, rhsMutable) =>
- for (RecordType.Field(fName, fOrigName, fTpe, fMutable) <- fields)
- yield VarRef(makeRecordFieldIdent(rhsIdent, fName, fOrigName),
- rhsMutable || fMutable)(fTpe)
- }
- js.Block(for {
- (RecordType.Field(fName, fOrigName, fTpe, fMutable),
- fRhs) <- fields zip elems
- } yield {
- doVarDef(makeRecordFieldIdent(ident, fName, fOrigName),
- fTpe, mutable || fMutable, fRhs)
- })
-
- case _ =>
- js.VarDef(ident, mutable, transformExpr(rhs))
- }
- }
-
- def doAssign(lhs: Tree, rhs: Tree): js.Tree = {
- implicit val pos = rhs.pos
- lhs.tpe match {
- case RecordType(fields) =>
- val VarRef(ident, mutable) = lhs
- val elems = (rhs: @unchecked) match {
- case VarRef(rhsIdent, rhsMutable) =>
- for (RecordType.Field(fName, fOrigName, fTpe, fMutable) <- fields)
- yield VarRef(makeRecordFieldIdent(rhsIdent, fName, fOrigName),
- rhsMutable || fMutable)(fTpe)
- }
- js.Block(for {
- (RecordType.Field(fName, fOrigName, fTpe, fMutable),
- fRhs) <- fields zip elems
- } yield {
- doAssign(VarRef(makeRecordFieldIdent(ident, fName, fOrigName),
- mutable || fMutable)(fTpe), fRhs)
- })
-
- case _ =>
- js.Assign(transformExpr(lhs), transformExpr(rhs))
- }
- }
-
- /** Push an lhs into a (potentially complex) rhs
- * lhs can be either a EmptyTree, a VarDef, a Assign or a
- * Return
- */
- def pushLhsInto(lhs: Tree, rhs: Tree): js.Tree = {
- implicit val rhsPos = rhs.pos
-
- /** Push the current lhs further into a deeper rhs */
- @inline def redo(newRhs: Tree) = pushLhsInto(lhs, newRhs)
-
- if (rhs.tpe == NothingType && lhs != EmptyTree) {
- /* A touch of peephole dead code elimination.
- * Actually necessary to handle pushing an lhs into an infinite loop,
- * for example.
- */
- val transformedRhs = pushLhsInto(EmptyTree, rhs)
- lhs match {
- case VarDef(name, _, mutable, _) =>
- /* We still need to declare the var, in case it is used somewhere
- * else in the function, where we can't dce it.
- */
- js.Block(js.VarDef(name, true, js.EmptyTree), transformedRhs)
- case _ =>
- transformedRhs
- }
- } else (rhs match {
- // Handle the Block before testing whether it is an expression
-
- case Block(stats :+ expr) =>
- js.Block((stats map transformStat) :+ redo(expr))
-
- // Base case, rhs is already a regular JS expression
-
- case _ if isExpression(rhs) =>
- (lhs: @unchecked) match {
- case EmptyTree =>
- if (isSideEffectFreeExpression(rhs)) js.Skip()
- else transformExpr(rhs)
- case VarDef(name, tpe, mutable, _) =>
- doVarDef(name, tpe, mutable, rhs)
- case Assign(lhs, _) =>
- doAssign(lhs, rhs)
- case Return(_, None) =>
- js.Return(transformExpr(rhs))
- case Return(_, label @ Some(l)) =>
- labeledExprLHSes(l) match {
- case newLhs @ Return(_, _) =>
- pushLhsInto(newLhs, rhs) // no need to break here
- case newLhs =>
- js.Block(pushLhsInto(newLhs, rhs),
- js.Break(label.map(transformIdent)))
- }
- }
-
- // Almost base case with RecordValue
-
- case RecordValue(recTpe, elems) =>
- (lhs: @unchecked) match {
- case EmptyTree =>
- js.Block(elems map transformStat)
- case VarDef(name, tpe, mutable, _) =>
- unnest(elems) { newElems =>
- doVarDef(name, tpe, mutable, RecordValue(recTpe, newElems))
- }
- case Assign(lhs, _) =>
- unnest(elems) { newElems =>
- val temp = newSyntheticVar()
- js.Block(
- doVarDef(temp, recTpe, false, RecordValue(recTpe, newElems)),
- doAssign(lhs, VarRef(temp, false)(recTpe)))
- }
- case Return(_, label @ Some(l)) =>
- val newLhs = labeledExprLHSes(l)
- js.Block(pushLhsInto(newLhs, rhs),
- js.Break(label.map(transformIdent)))
- }
-
- // Control flow constructs
-
- case Labeled(label, tpe, body) =>
- val savedMap = labeledExprLHSes
- labeledExprLHSes = labeledExprLHSes + (label -> lhs)
- try {
- lhs match {
- case Return(_, _) => redo(body)
- case _ => js.Labeled(label, redo(body))
- }
- } finally {
- labeledExprLHSes = savedMap
- }
-
- case Return(expr, _) =>
- pushLhsInto(rhs, expr)
-
- case Continue(label) =>
- js.Continue(label.map(transformIdent))
-
- case If(cond, thenp, elsep) =>
- unnest(cond) { newCond =>
- js.If(transformExpr(newCond), redo(thenp), redo(elsep))
- }
-
- case Try(block, errVar, handler, finalizer) =>
- val newHandler =
- if (handler == EmptyTree) js.EmptyTree else redo(handler)
- val newFinalizer =
- if (finalizer == EmptyTree) js.EmptyTree else transformStat(finalizer)
-
- if (newHandler != js.EmptyTree && newFinalizer != js.EmptyTree) {
- /* The Google Closure Compiler wrongly eliminates finally blocks, if
- * the catch block throws an exception.
- * Issues: #563, google/closure-compiler#186
- *
- * Therefore, we desugar
- *
- * try { ... } catch { ... } finally { ... }
- *
- * into
- *
- * try { try { ... } catch { ... } } finally { ... }
- */
- js.Try(js.Try(redo(block), errVar, newHandler, js.EmptyTree),
- errVar, js.EmptyTree, newFinalizer)
- } else
- js.Try(redo(block), errVar, newHandler, newFinalizer)
-
- // TODO Treat throw as an LHS?
- case Throw(expr) =>
- unnest(expr) { newExpr =>
- js.Throw(transformExpr(newExpr))
- }
-
- /** Matches are desugared into switches
- *
- * A match is different from a switch in two respects, both linked
- * to match being designed to be used in expression position in
- * Extended-JS.
- *
- * * There is no fall-through from one case to the next one, hence,
- * no break statement.
- * * Match supports _alternatives_ explicitly (with a switch, one
- * would use the fall-through behavior to implement alternatives).
- */
- case Match(selector, cases, default) =>
- unnest(selector) { newSelector =>
- val newCases = {
- for {
- (values, body) <- cases
- newValues = (values map transformExpr)
- // add the break statement
- newBody = js.Block(redo(body), js.Break())
- // desugar alternatives into several cases falling through
- caze <- (newValues.init map (v => (v, js.Skip()))) :+ (newValues.last, newBody)
- } yield {
- caze
- }
- }
- val newDefault =
- if (default == EmptyTree) js.EmptyTree
- else redo(default)
- js.Switch(transformExpr(newSelector), newCases, newDefault)
- }
-
- // Scala expressions (if we reach here their arguments are not expressions)
-
- case New(cls, ctor, args) =>
- unnest(args) { newArgs =>
- redo(New(cls, ctor, newArgs))
- }
-
- case Select(qualifier, item, mutable) =>
- unnest(qualifier) { newQualifier =>
- redo(Select(newQualifier, item, mutable)(rhs.tpe))
- }
-
- case Apply(receiver, method, args) =>
- unnest(receiver, args) { (newReceiver, newArgs) =>
- redo(Apply(newReceiver, method, newArgs)(rhs.tpe))
- }
-
- case StaticApply(receiver, cls, method, args) =>
- unnest(receiver, args) { (newReceiver, newArgs) =>
- redo(StaticApply(newReceiver, cls, method, newArgs)(rhs.tpe))
- }
-
- case TraitImplApply(impl, method, args) =>
- unnest(args) { newArgs =>
- redo(TraitImplApply(impl, method, newArgs)(rhs.tpe))
- }
-
- case UnaryOp(op, lhs) =>
- unnest(lhs) { newLhs =>
- redo(UnaryOp(op, newLhs))
- }
-
- case BinaryOp(op, lhs, rhs) =>
- unnest(lhs, rhs) { (newLhs, newRhs) =>
- redo(BinaryOp(op, newLhs, newRhs))
- }
-
- case NewArray(tpe, lengths) =>
- unnest(lengths) { newLengths =>
- redo(NewArray(tpe, newLengths))
- }
-
- case ArrayValue(tpe, elems) =>
- unnest(elems) { newElems =>
- redo(ArrayValue(tpe, newElems))
- }
-
- case ArrayLength(array) =>
- unnest(array) { newArray =>
- redo(ArrayLength(newArray))
- }
-
- case ArraySelect(array, index) =>
- unnest(array, index) { (newArray, newIndex) =>
- redo(ArraySelect(newArray, newIndex)(rhs.tpe))
- }
-
- case IsInstanceOf(expr, cls) =>
- unnest(expr) { newExpr =>
- redo(IsInstanceOf(newExpr, cls))
- }
-
- case AsInstanceOf(expr, cls) =>
- if (semantics.asInstanceOfs == Unchecked) {
- redo(expr)
- } else {
- unnest(expr) { newExpr =>
- redo(AsInstanceOf(newExpr, cls))
- }
- }
-
- case Unbox(expr, charCode) =>
- unnest(expr) { newExpr =>
- redo(Unbox(newExpr, charCode))
- }
-
- case GetClass(expr) =>
- unnest(expr) { newExpr =>
- redo(GetClass(newExpr))
- }
-
- case CallHelper(helper, args) =>
- unnest(args) { newArgs =>
- redo(CallHelper(helper, newArgs)(rhs.tpe))
- }
-
- // JavaScript expressions (if we reach here their arguments are not expressions)
-
- case JSNew(ctor, args) =>
- unnest(ctor :: args) { newCtorAndArgs =>
- val newCtor :: newArgs = newCtorAndArgs
- redo(JSNew(newCtor, newArgs))
- }
-
- case JSFunctionApply(fun, args) =>
- unnest(fun :: args) { newFunAndArgs =>
- val newFun :: newArgs = newFunAndArgs
- redo(JSFunctionApply(newFun, newArgs))
- }
-
- case JSDotMethodApply(receiver, method, args) =>
- unnest(receiver :: args) { newReceiverAndArgs =>
- val newReceiver :: newArgs = newReceiverAndArgs
- redo(JSDotMethodApply(newReceiver, method, newArgs))
- }
-
- case JSBracketMethodApply(receiver, method, args) =>
- unnest(receiver :: method :: args) { newReceiverAndArgs =>
- val newReceiver :: newMethod :: newArgs = newReceiverAndArgs
- redo(JSBracketMethodApply(newReceiver, newMethod, newArgs))
- }
-
- case JSDotSelect(qualifier, item) =>
- unnest(qualifier) { newQualifier =>
- redo(JSDotSelect(newQualifier, item))
- }
-
- case JSBracketSelect(qualifier, item) =>
- unnest(qualifier, item) { (newQualifier, newItem) =>
- redo(JSBracketSelect(newQualifier, newItem))
- }
-
- case JSUnaryOp(op, lhs) =>
- unnest(lhs) { newLhs =>
- redo(JSUnaryOp(op, newLhs))
- }
-
- case JSBinaryOp("&&", lhs, rhs) =>
- if (lhs.tpe == BooleanType) {
- redo(If(lhs, rhs, BooleanLiteral(false))(AnyType))
- } else {
- unnest(lhs) { newLhs =>
- redo(If(newLhs, rhs, newLhs)(AnyType))
- }
- }
-
- case JSBinaryOp("||", lhs, rhs) =>
- if (lhs.tpe == BooleanType) {
- redo(If(lhs, BooleanLiteral(true), rhs)(AnyType))
- } else {
- unnest(lhs) { newLhs =>
- redo(If(newLhs, newLhs, rhs)(AnyType))
- }
- }
-
- case JSBinaryOp(op, lhs, rhs) =>
- unnest(lhs, rhs) { (newLhs, newRhs) =>
- redo(JSBinaryOp(op, newLhs, newRhs))
- }
-
- case JSArrayConstr(items) =>
- unnest(items) { newItems =>
- redo(JSArrayConstr(newItems))
- }
-
- case JSObjectConstr(fields) =>
- val names = fields map (_._1)
- val items = fields map (_._2)
- unnest(items) { newItems =>
- redo(JSObjectConstr(names.zip(newItems)))
- }
-
- // Closures
-
- case Closure(captureParams, params, body, captureValues) =>
- unnest(captureValues) { newCaptureValues =>
- redo(Closure(captureParams, params, body, newCaptureValues))
- }
-
- case _ =>
- if (lhs == EmptyTree) {
- /* Go "back" to transformStat() after having dived into
- * expression statements. Remember that (lhs == EmptyTree)
- * is a trick that we use to "add" all the code of pushLhsInto()
- * to transformStat().
- */
- rhs match {
- case _:Skip | _:VarDef | _:Assign | _:While | _:DoWhile |
- _:Debugger | _:JSDelete | _:StoreModule | _:ClassDef =>
- transformStat(rhs)
- case _ =>
- sys.error("Illegal tree in JSDesugar.pushLhsInto():\n" +
- "lhs = " + lhs + "\n" + "rhs = " + rhs +
- " of class " + rhs.getClass)
- }
- } else {
- sys.error("Illegal tree in JSDesugar.pushLhsInto():\n" +
- "lhs = " + lhs + "\n" + "rhs = " + rhs +
- " of class " + rhs.getClass)
- }
- })
- }
-
- // Desugar Scala operations to JavaScript operations -----------------------
-
- /** Desugar an expression of the IR into ES5 JS */
- def transformExpr(tree: Tree): js.Tree = {
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- def or0(tree: js.Tree): js.Tree =
- js.BinaryOp("|", tree, js.IntLiteral(0))
-
- tree match {
- // Control flow constructs
-
- case Block(stats :+ expr) =>
- js.Block((stats map transformStat) :+ transformExpr(expr))
-
- // Note that these work even if thenp/elsep is not a BooleanType
- case If(cond, BooleanLiteral(true), elsep) =>
- js.BinaryOp("||", transformExpr(cond), transformExpr(elsep))
- case If(cond, thenp, BooleanLiteral(false)) =>
- js.BinaryOp("&&", transformExpr(cond), transformExpr(thenp))
-
- case If(cond, thenp, elsep) =>
- js.If(transformExpr(cond), transformExpr(thenp), transformExpr(elsep))
-
- // Scala expressions
-
- case New(cls, ctor, args) =>
- js.Apply(js.New(encodeClassVar(cls.className), Nil) DOT ctor,
- args map transformExpr)
-
- case LoadModule(cls) =>
- genLoadModule(cls.className)
-
- case RecordFieldVarRef(VarRef(name, mutable)) =>
- js.VarRef(name, mutable)
-
- case Select(qualifier, item, _) =>
- transformExpr(qualifier) DOT item
-
- case Apply(receiver, method, args) =>
- val newReceiver = transformExpr(receiver)
- val newArgs = args map transformExpr
- if (isMaybeHijackedClass(receiver.tpe) &&
- !Definitions.isReflProxyName(method.name)) {
- val helperName = hijackedClassMethodToHelperName(method.name)
- genCallHelper(helperName, newReceiver :: newArgs: _*)
- } else {
- js.Apply(newReceiver DOT method, newArgs)
- }
-
- case StaticApply(receiver, cls, method, args) =>
- val fun = encodeClassVar(cls.className).prototype DOT method
- js.Apply(fun DOT "call", (receiver :: args) map transformExpr)
-
- case TraitImplApply(impl, method, args) =>
- js.Apply(envField("i") DOT method, args map transformExpr)
-
- case UnaryOp(op, lhs) =>
- import UnaryOp._
- val newLhs = transformExpr(lhs)
- (op: @switch) match {
- case `typeof` => js.UnaryOp("typeof", newLhs)
- case Boolean_! => js.UnaryOp("!", newLhs)
- case DoubleToInt => js.BinaryOp("|", newLhs, js.IntLiteral(0))
-
- case LongToInt => genLongMethodApply(newLhs, LongImpl.toInt)
- case LongToDouble => genLongMethodApply(newLhs, LongImpl.toDouble)
-
- case DoubleToFloat => genFround(newLhs)
-
- case IntToLong =>
- genNewLong(LongImpl.initFromInt, newLhs)
- case DoubleToLong =>
- genLongModuleApply(LongImpl.fromDouble, newLhs)
- }
-
- case BinaryOp(op, lhs, rhs) =>
- import BinaryOp._
- val lhs1 = lhs match {
- case UnaryOp(UnaryOp.DoubleToInt, inner)
- if op == Int_& || op == Int_<< =>
- /* This case is emitted typically by conversions from
- * Float/Double to Char/Byte/Short. We have to introduce an
- * (int) cast in the IR so that it typechecks, but in JavaScript
- * this is redundant because & and << already convert both their
- * operands to ints. So we get rid of the conversion here.
- */
- inner
- case _ =>
- lhs
- }
-
- val newLhs = transformExpr(lhs1)
- val newRhs = transformExpr(rhs)
-
- (op: @switch) match {
- case === | Num_== | Boolean_== => js.BinaryOp("===", newLhs, newRhs)
- case !== | Num_!= | Boolean_!= => js.BinaryOp("!==", newLhs, newRhs)
-
- case String_+ =>
- if (lhs.tpe == StringType || rhs.tpe == StringType)
- js.BinaryOp("+", newLhs, newRhs)
- else
- js.BinaryOp("+", js.BinaryOp("+", js.StringLiteral(""), newLhs), newRhs)
-
- case `in` => js.BinaryOp("in", newLhs, newRhs)
- case `instanceof` => js.BinaryOp("instanceof", newLhs, newRhs)
-
- case Int_+ => or0(js.BinaryOp("+", newLhs, newRhs))
- case Int_- =>
- lhs match {
- case IntLiteral(0) => or0(js.UnaryOp("-", newRhs))
- case _ => or0(js.BinaryOp("-", newLhs, newRhs))
- }
- case Int_* => genCallHelper("imul", newLhs, newRhs)
- case Int_/ => or0(js.BinaryOp("/", newLhs, newRhs))
- case Int_% => js.BinaryOp("%", newLhs, newRhs)
-
- case Int_| => js.BinaryOp("|", newLhs, newRhs)
- case Int_& => js.BinaryOp("&", newLhs, newRhs)
- case Int_^ =>
- lhs match {
- case IntLiteral(-1) => js.UnaryOp("~", newRhs)
- case _ => js.BinaryOp("^", newLhs, newRhs)
- }
- case Int_<< => js.BinaryOp("<<", newLhs, newRhs)
- case Int_>>> => or0(js.BinaryOp(">>>", newLhs, newRhs))
- case Int_>> => js.BinaryOp(">>", newLhs, newRhs)
-
- case Float_+ => genFround(js.BinaryOp("+", newLhs, newRhs))
- case Float_- =>
- genFround(lhs match {
- case DoubleLiteral(0.0) => js.UnaryOp("-", newRhs)
- case _ => js.BinaryOp("-", newLhs, newRhs)
- })
- case Float_* => genFround(js.BinaryOp("*", newLhs, newRhs))
- case Float_/ => genFround(js.BinaryOp("/", newLhs, newRhs))
- case Float_% => genFround(js.BinaryOp("%", newLhs, newRhs))
-
- case Double_+ => js.BinaryOp("+", newLhs, newRhs)
- case Double_- =>
- lhs match {
- case DoubleLiteral(0.0) => js.UnaryOp("-", newRhs)
- case _ => js.BinaryOp("-", newLhs, newRhs)
- }
- case Double_* => js.BinaryOp("*", newLhs, newRhs)
- case Double_/ => js.BinaryOp("/", newLhs, newRhs)
- case Double_% => js.BinaryOp("%", newLhs, newRhs)
-
- case Num_< => js.BinaryOp("<", newLhs, newRhs)
- case Num_<= => js.BinaryOp("<=", newLhs, newRhs)
- case Num_> => js.BinaryOp(">", newLhs, newRhs)
- case Num_>= => js.BinaryOp(">=", newLhs, newRhs)
-
- case Long_+ => genLongMethodApply(newLhs, LongImpl.+, newRhs)
- case Long_- =>
- lhs match {
- case LongLiteral(0L) => genLongMethodApply(newRhs, LongImpl.UNARY_-)
- case _ => genLongMethodApply(newLhs, LongImpl.-, newRhs)
- }
- case Long_* => genLongMethodApply(newLhs, LongImpl.*, newRhs)
- case Long_/ => genLongMethodApply(newLhs, LongImpl./, newRhs)
- case Long_% => genLongMethodApply(newLhs, LongImpl.%, newRhs)
-
- case Long_| => genLongMethodApply(newLhs, LongImpl.|, newRhs)
- case Long_& => genLongMethodApply(newLhs, LongImpl.&, newRhs)
- case Long_^ =>
- lhs match {
- case LongLiteral(-1L) => genLongMethodApply(newRhs, LongImpl.UNARY_~)
- case _ => genLongMethodApply(newLhs, LongImpl.^, newRhs)
- }
- case Long_<< => genLongMethodApply(newLhs, LongImpl.<<, newRhs)
- case Long_>>> => genLongMethodApply(newLhs, LongImpl.>>>, newRhs)
- case Long_>> => genLongMethodApply(newLhs, LongImpl.>>, newRhs)
-
- case Long_== => genLongMethodApply(newLhs, LongImpl.===, newRhs)
- case Long_!= => genLongMethodApply(newLhs, LongImpl.!==, newRhs)
- case Long_< => genLongMethodApply(newLhs, LongImpl.<, newRhs)
- case Long_<= => genLongMethodApply(newLhs, LongImpl.<=, newRhs)
- case Long_> => genLongMethodApply(newLhs, LongImpl.>, newRhs)
- case Long_>= => genLongMethodApply(newLhs, LongImpl.>=, newRhs)
-
- case Boolean_| => !(!js.BinaryOp("|", newLhs, newRhs))
- case Boolean_& => !(!js.BinaryOp("&", newLhs, newRhs))
- }
-
- case NewArray(tpe, lengths) =>
- genCallHelper("newArrayObject",
- genClassDataOf(tpe), js.ArrayConstr(lengths map transformExpr))
-
- case ArrayValue(tpe, elems) =>
- genCallHelper("makeNativeArrayWrapper",
- genClassDataOf(tpe), js.ArrayConstr(elems map transformExpr))
-
- case ArrayLength(array) =>
- js.BracketSelect(js.DotSelect(transformExpr(array),
- Ident("u")), js.StringLiteral("length"))
-
- case ArraySelect(array, index) =>
- js.BracketSelect(js.DotSelect(transformExpr(array),
- Ident("u")), transformExpr(index))
-
- case IsInstanceOf(expr, cls) =>
- genIsInstanceOf(transformExpr(expr), cls)
-
- case AsInstanceOf(expr, cls) =>
- val newExpr = transformExpr(expr)
- if (semantics.asInstanceOfs == Unchecked) newExpr
- else genAsInstanceOf(newExpr, cls)
-
- case Unbox(expr, charCode) =>
- val newExpr = transformExpr(expr)
-
- if (semantics.asInstanceOfs == Unchecked) {
- (charCode: @switch) match {
- case 'Z' => !(!newExpr)
- case 'B' | 'S' | 'I' => js.BinaryOp("|", newExpr, js.IntLiteral(0))
- case 'J' => genCallHelper("uJ", newExpr)
- case 'F' => genFround(newExpr)
- case 'D' => js.UnaryOp("+", newExpr)
- }
- } else {
- genCallHelper("u"+charCode, newExpr)
- }
-
- case GetClass(expr) =>
- genCallHelper("objectGetClass", transformExpr(expr))
-
- case CallHelper(helper, args) =>
- genCallHelper(helper, args map transformExpr: _*)
-
- // JavaScript expressions
-
- case JSBracketSelect(JSEnvInfo(), StringLiteral("global")) =>
- // Shortcut for this field which is heavily used
- envField("g")
-
- case JSNew(constr, args) =>
- js.New(transformExpr(constr), args map transformExpr)
-
- case JSDotSelect(qualifier, item) =>
- js.DotSelect(transformExpr(qualifier), item)
-
- case JSBracketSelect(qualifier, item) =>
- js.BracketSelect(transformExpr(qualifier), transformExpr(item))
-
- case JSFunctionApply(fun, args) =>
- /* Protect the fun so that if it is, e.g.,
- * path.f
- * we emit
- * (0, path.f)(args...)
- * instead of
- * path.f(args...)
- * If we emit the latter, then `this` will be bound to `path` in
- * `f`, which is sometimes extremely harmful (e.g., for builtin
- * methods of `window`).
- */
- val transformedFun = transformExpr(fun)
- val protectedFun = transformedFun match {
- case _:js.DotSelect | _:js.BracketSelect =>
- js.Block(js.IntLiteral(0), transformedFun)
- case _ =>
- transformedFun
- }
- js.Apply(protectedFun, args map transformExpr)
-
- case JSDotMethodApply(receiver, method, args) =>
- js.Apply(js.DotSelect(transformExpr(receiver), method),
- args map transformExpr)
-
- case JSBracketMethodApply(receiver, method, args) =>
- js.Apply(js.BracketSelect(transformExpr(receiver),
- transformExpr(method)), args map transformExpr)
-
- case JSUnaryOp(op, lhs) =>
- js.UnaryOp(op, transformExpr(lhs))
-
- case JSBinaryOp(op, lhs, rhs) =>
- js.BinaryOp(op, transformExpr(lhs), transformExpr(rhs))
-
- case JSArrayConstr(items) =>
- js.ArrayConstr(items map transformExpr)
-
- case JSObjectConstr(fields) =>
- js.ObjectConstr(fields map {
- case (name: Ident, value) =>
- (transformIdent(name), transformExpr(value))
- case (StringLiteral(name), value) =>
- (js.StringLiteral(name), transformExpr(value))
- })
-
- case JSEnvInfo() =>
- envField("env")
-
- // Literals
-
- case Undefined() => js.Undefined()
- case Null() => js.Null()
- case BooleanLiteral(value) => js.BooleanLiteral(value)
- case IntLiteral(value) => js.IntLiteral(value)
- case FloatLiteral(value) => js.DoubleLiteral(value.toDouble)
- case DoubleLiteral(value) => js.DoubleLiteral(value)
- case StringLiteral(value) => js.StringLiteral(value)
-
- case LongLiteral(0L) =>
- genLongModuleApply(LongImpl.Zero)
- case LongLiteral(value) =>
- val (l, m, h) = LongImpl.extractParts(value)
- genNewLong(LongImpl.initFromParts,
- js.IntLiteral(l), js.IntLiteral(m), js.IntLiteral(h))
-
- case ClassOf(cls) =>
- js.Apply(js.DotSelect(genClassDataOf(cls), Ident("getClassOf")), Nil)
-
- // Atomic expressions
-
- case VarRef(name, mutable) =>
- js.VarRef(name, mutable)
-
- case This() =>
- js.This()
-
- case Closure(captureParams, params, body, captureValues) =>
- val transformedBody = {
- val withReturn = Return(body, None)
- transformStat(withReturn) match {
- case js.Block(stats :+ js.Return(js.Undefined())) => js.Block(stats)
- case other => other
- }
- }
-
- val innerFunction =
- js.Function(params.map(transformParamDef), transformedBody)
-
- if (captureParams.isEmpty) {
- innerFunction
- } else {
- js.Apply(
- js.Function(captureParams.map(transformParamDef), {
- js.Return(innerFunction)
- }),
- captureValues.map(transformExpr))
- }
-
- // Invalid trees
-
- case _ =>
- sys.error("Invalid tree in JSDesugar.transformExpr() "+
- s"of class ${tree.getClass}")
- }
- }
-
- def isMaybeHijackedClass(tpe: Type): Boolean = tpe match {
- case ClassType(cls) =>
- Definitions.HijackedClasses.contains(cls) ||
- Definitions.AncestorsOfHijackedClasses.contains(cls)
- case AnyType | UndefType | BooleanType | IntType | LongType |
- FloatType | DoubleType | StringType =>
- true
- case _ =>
- false
- }
-
- val hijackedClassMethodToHelperName: Map[String, String] = Map(
- "toString__T" -> "objectToString",
- "clone__O" -> "objectClone",
- "finalize__V" -> "objectFinalize",
- "notify__V" -> "objectNotify",
- "notifyAll__V" -> "objectNotifyAll",
- "equals__O__Z" -> "objectEquals",
- "hashCode__I" -> "objectHashCode",
-
- "length__I" -> "charSequenceLength",
- "charAt__I__C" -> "charSequenceCharAt",
- "subSequence__I__I__jl_CharSequence" -> "charSequenceSubSequence",
-
- "compareTo__O__I" -> "comparableCompareTo",
- "compareTo__jl_Boolean__I" -> "comparableCompareTo",
- "compareTo__jl_Byte__I" -> "comparableCompareTo",
- "compareTo__jl_Short__I" -> "comparableCompareTo",
- "compareTo__jl_Integer__I" -> "comparableCompareTo",
- "compareTo__jl_Long__I" -> "comparableCompareTo",
- "compareTo__jl_Float__I" -> "comparableCompareTo",
- "compareTo__jl_Double__I" -> "comparableCompareTo",
- "compareTo__jl_String__I" -> "comparableCompareTo",
-
- "booleanValue__Z" -> "booleanBooleanValue",
-
- "byteValue__B" -> "numberByteValue",
- "shortValue__S" -> "numberShortValue",
- "intValue__I" -> "numberIntValue",
- "longValue__J" -> "numberLongValue",
- "floatValue__F" -> "numberFloatValue",
- "doubleValue__D" -> "numberDoubleValue",
-
- "isNaN__Z" -> "isNaN",
- "isInfinite__Z" -> "isInfinite"
- )
-
- def genClassDataOf(cls: ReferenceType)(implicit pos: Position): js.Tree = {
- cls match {
- case ClassType(className) =>
- encodeClassField("d", className)
- case ArrayType(base, dims) =>
- (1 to dims).foldLeft(encodeClassField("d", base)) { (prev, _) =>
- js.Apply(js.DotSelect(prev, js.Ident("getArrayOf")), Nil)
- }
- }
- }
-
- private def genFround(arg: js.Tree)(implicit pos: Position): js.Tree = {
- genCallHelper("fround", arg)
- }
-
- private def genNewLong(ctor: String, args: js.Tree*)(
- implicit pos: Position): js.Tree = {
- import TreeDSL._
- js.Apply(
- js.New(encodeClassVar(LongImpl.RuntimeLongClass), Nil) DOT ctor,
- args.toList)
- }
-
- private def genLongMethodApply(receiver: js.Tree, methodName: String,
- args: js.Tree*)(implicit pos: Position): js.Tree = {
- import TreeDSL._
- js.Apply(receiver DOT methodName, args.toList)
- }
-
- private def genLongModuleApply(methodName: String, args: js.Tree*)(
- implicit pos: Position): js.Tree = {
- import TreeDSL._
- js.Apply(
- genLoadModule(LongImpl.RuntimeLongModuleClass) DOT methodName,
- args.toList)
- }
-
- private def genLoadModule(moduleClass: String)(
- implicit pos: Position): js.Tree = {
- import TreeDSL._
- assert(moduleClass.endsWith("$"),
- s"Trying to load module for non-module class $moduleClass")
- val moduleName = moduleClass.dropRight(1)
- js.Apply(envField("m") DOT moduleName, Nil)
- }
-
- }
-
- // Helpers
-
- private[javascript] def genIsInstanceOf(expr: js.Tree, cls: ReferenceType)(
- implicit pos: Position): js.Tree =
- genIsAsInstanceOf(expr, cls, test = true)
-
- private def genAsInstanceOf(expr: js.Tree, cls: ReferenceType)(
- implicit pos: Position): js.Tree =
- genIsAsInstanceOf(expr, cls, test = false)
-
- private def genIsAsInstanceOf(expr: js.Tree, cls: ReferenceType, test: Boolean)(
- implicit pos: Position): js.Tree = {
- import Definitions._
- import TreeDSL._
-
- cls match {
- case ClassType(className0) =>
- val className =
- if (className0 == BoxedLongClass) LongImpl.RuntimeLongClass
- else className0
-
- if (HijackedBoxedClasses.contains(className)) {
- if (test) {
- className match {
- case BoxedUnitClass => expr === js.Undefined()
- case BoxedBooleanClass => typeof(expr) === "boolean"
- case BoxedByteClass => genCallHelper("isByte", expr)
- case BoxedShortClass => genCallHelper("isShort", expr)
- case BoxedIntegerClass => genCallHelper("isInt", expr)
- case BoxedFloatClass => genCallHelper("isFloat", expr)
- case BoxedDoubleClass => typeof(expr) === "number"
- }
- } else {
- className match {
- case BoxedUnitClass => genCallHelper("asUnit", expr)
- case BoxedBooleanClass => genCallHelper("asBoolean", expr)
- case BoxedByteClass => genCallHelper("asByte", expr)
- case BoxedShortClass => genCallHelper("asShort", expr)
- case BoxedIntegerClass => genCallHelper("asInt", expr)
- case BoxedFloatClass => genCallHelper("asFloat", expr)
- case BoxedDoubleClass => genCallHelper("asDouble", expr)
- }
- }
- } else {
- js.Apply(
- envField(if (test) "is" else "as") DOT js.Ident(className),
- List(expr))
- }
-
- case ArrayType(base, depth) =>
- js.Apply(
- envField(if (test) "isArrayOf" else "asArrayOf") DOT js.Ident(base),
- List(expr, js.IntLiteral(depth)))
- }
- }
-
- private[javascript] def genCallHelper(helperName: String, args: js.Tree*)(
- implicit pos: Position): js.Tree =
- js.Apply(envField(helperName), args.toList)
-
- private[javascript] def encodeClassVar(className: String)(
- implicit pos: Position): js.Tree =
- encodeClassField("c", className)
-
- private[javascript] def encodeClassField(field: String, className: String)(
- implicit pos: Position): js.Tree =
- js.DotSelect(envField(field), js.Ident(className))
-
- private[javascript] def envField(field: String)(implicit pos: Position): js.Tree =
- js.DotSelect(js.VarRef(js.Ident(ScalaJSEnvironmentName), false),
- js.Ident(field))
-
- private[javascript] implicit class MyTreeOps(val self: js.Tree) {
- def prototype(implicit pos: Position): js.Tree =
- js.DotSelect(self, js.Ident("prototype"))
- }
-}