summaryrefslogtreecommitdiff
path: root/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript')
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/JSDesugaring.scala1525
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala116
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala420
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala569
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala50
-rw-r--r--examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala194
6 files changed, 0 insertions, 2874 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"))
- }
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala
deleted file mode 100644
index 70b81a3..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/LongImpl.scala
+++ /dev/null
@@ -1,116 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-object LongImpl {
- final val RuntimeLongClass = "sjsr_RuntimeLong"
- final val RuntimeLongModuleClass = "sjsr_RuntimeLong$"
-
- private final val SigUnary = "__sjsr_RuntimeLong"
- private final val SigBinary = "__sjsr_RuntimeLong__sjsr_RuntimeLong"
- private final val SigShift = "__I__sjsr_RuntimeLong"
- private final val SigCompare = "__sjsr_RuntimeLong__Z"
-
- final val UNARY_- = "unary$und$minus" + SigUnary
- final val UNARY_~ = "unary$und$tilde" + SigUnary
-
- final val + = "$$plus" + SigBinary
- final val - = "$$minus" + SigBinary
- final val * = "$$times" + SigBinary
- final val / = "$$div" + SigBinary
- final val % = "$$percent" + SigBinary
-
- final val | = "$$bar" + SigBinary
- final val & = "$$amp" + SigBinary
- final val ^ = "$$up" + SigBinary
-
- final val << = "$$less$less" + SigShift
- final val >>> = "$$greater$greater$greater" + SigShift
- final val >> = "$$greater$greater" + SigShift
-
- final val === = "equals" + SigCompare
- final val !== = "notEquals" + SigCompare
- final val < = "$$less" + SigCompare
- final val <= = "$$less$eq" + SigCompare
- final val > = "$$greater" + SigCompare
- final val >= = "$$greater$eq" + SigCompare
-
- final val toInt = "toInt" + "__I"
- final val toDouble = "toDouble" + "__D"
-
- final val byteValue = "byteValue__B"
- final val shortValue = "shortValue__S"
- final val intValue = "intValue__I"
- final val longValue = "longValue__J"
- final val floatValue = "floatValue__F"
- final val doubleValue = "doubleValue__D"
-
- final val equals_ = "equals__O__Z"
- final val hashCode_ = "hashCode__I"
- final val compareTo = "compareTo__jl_Long__I"
- final val compareToO = "compareTo__O__I"
-
- private val OperatorMethods = Set(
- UNARY_-, UNARY_~, this.+, this.-, *, /, %, |, &, ^, <<, >>>, >>,
- ===, !==, <, <=, >, >=, toInt, toDouble)
-
- private val BoxedLongMethods = Set(
- byteValue, shortValue, intValue, longValue, floatValue, doubleValue,
- equals_, hashCode_, compareTo, compareToO)
-
- val AllMethods = OperatorMethods ++ BoxedLongMethods
-
- // Methods used for intrinsics
-
- final val bitCount = "bitCount__I"
- final val signum = "signum__sjsr_RuntimeLong"
- final val numberOfLeadingZeros = "numberOfLeadingZeros__I"
- final val numberOfTrailingZeros = "numberOfTrailingZeros__I"
- final val toBinaryString = "toBinaryString__T"
- final val toHexString = "toHexString__T"
- final val toOctalString = "toOctalString__T"
-
- val AllIntrinsicMethods = Set(
- bitCount, signum, numberOfLeadingZeros, numberOfTrailingZeros,
- toBinaryString, toHexString, toOctalString)
-
- // Constructors
-
- final val initFromParts = "init___I__I__I"
- final val initFromInt = "init___I"
-
- val AllConstructors = Set(
- initFromParts, initFromInt)
-
- // Methods on the companion
-
- final val fromDouble = "fromDouble__D__sjsr_RuntimeLong"
-
- final val Zero = "Zero__sjsr_RuntimeLong"
-
- val AllModuleMethods = Set(
- fromDouble, Zero)
-
- // Boldly copied from library/scala.scalajs.runtime.RuntimeLong
-
- /** Number of relevant bits in l and m each. */
- private final val BITS = 22
- /** Number of relevant bits in l and m together. */
- private final val BITS01 = 2 * BITS
- /** Number of relevant bits in h. */
- private final val BITS2 = 64 - BITS01
- /** Bitmask for l and m. */
- private final val MASK = (1 << BITS) - 1
- /** Bitmask for h. */
- private final val MASK_2 = (1 << BITS2) - 1
-
- def extractParts(value: Long): (Int, Int, Int) =
- (value.toInt & MASK, (value >> BITS).toInt & MASK, (value >> BITS01).toInt & MASK_2)
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala
deleted file mode 100644
index 264c548..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Printers.scala
+++ /dev/null
@@ -1,420 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-import scala.annotation.switch
-
-import scala.util.control.Breaks
-
-import java.io.Writer
-import java.net.URI
-
-import scala.scalajs.ir
-import ir.Position
-import ir.Position.NoPosition
-import ir.Printers.IndentationManager
-import ir.Utils.escapeJS
-
-import Trees._
-
-import scala.scalajs.tools.sourcemap.SourceMapWriter
-
-object Printers {
-
- class JSTreePrinter(protected val out: Writer) extends IndentationManager {
- def printTopLevelTree(tree: Tree) {
- tree match {
- case Skip() =>
- // do not print anything
- case Block(stats) =>
- for (stat <- stats)
- printTopLevelTree(stat)
- case _ =>
- printStat(tree)
- if (shouldPrintSepAfterTree(tree))
- print(";")
- println()
- }
- }
-
- protected def shouldPrintSepAfterTree(tree: Tree): Boolean =
- !tree.isInstanceOf[DocComment]
-
- protected def printBlock(tree: Tree): Unit = {
- val trees = tree match {
- case Block(trees) => trees
- case _ => List(tree)
- }
- print("{"); indent(); println()
- printSeq(trees) { x =>
- printStat(x)
- } { x =>
- if (shouldPrintSepAfterTree(x))
- print(";")
- println()
- }
- undent(); println(); print("}")
- }
-
- protected def printSig(args: List[ParamDef]): Unit = {
- printRow(args, "(", ", ", ")")
- print(" ")
- }
-
- protected def printArgs(args: List[Tree]): Unit = {
- printRow(args, "(", ", ", ")")
- }
-
- def printStat(tree: Tree): Unit =
- printTree(tree, isStat = true)
-
- def printTree(tree: Tree, isStat: Boolean): Unit = {
- tree match {
- case EmptyTree =>
- print("<empty>")
-
- // Comments
-
- case DocComment(text) =>
- val lines = text.split("\n").toList
- if (lines.tail.isEmpty) {
- print("/** ", lines.head, " */")
- } else {
- print("/** ", lines.head); println()
- for (line <- lines.tail) {
- print(" * ", line); println()
- }
- print(" */")
- }
-
- // Definitions
-
- case VarDef(ident, mutable, rhs) =>
- print("var ", ident)
- if (rhs != EmptyTree)
- print(" = ", rhs)
-
- case ParamDef(ident, mutable) =>
- print(ident)
-
- // Control flow constructs
-
- case Skip() =>
- print("/*<skip>*/")
-
- case tree @ Block(trees) =>
- if (isStat)
- printBlock(tree)
- else
- printRow(trees, "(", ", ", ")")
-
- case Labeled(label, body) =>
- print(label, ": ")
- printBlock(body)
-
- case Assign(lhs, rhs) =>
- print(lhs, " = ", rhs)
-
- case Return(expr) =>
- print("return ", expr)
-
- case If(cond, thenp, elsep) =>
- if (isStat) {
- print("if (", cond, ") ")
- printBlock(thenp)
- elsep match {
- case Skip() => ()
- case If(_, _, _) =>
- print(" else ")
- printTree(elsep, isStat)
- case _ =>
- print(" else ")
- printBlock(elsep)
- }
- } else {
- print("(", cond, " ? ", thenp, " : ", elsep, ")")
- }
-
- case While(cond, body, label) =>
- if (label.isDefined)
- print(label.get, ": ")
- print("while (", cond, ") ")
- printBlock(body)
-
- case DoWhile(body, cond, label) =>
- if (label.isDefined)
- print(label.get, ": ")
- print("do ")
- printBlock(body)
- print(" while (", cond, ")")
-
- case Try(block, errVar, handler, finalizer) =>
- print("try ")
- printBlock(block)
- if (handler != EmptyTree) {
- print(" catch (", errVar, ") ")
- printBlock(handler)
- }
- if (finalizer != EmptyTree) {
- print(" finally ")
- printBlock(finalizer)
- }
-
- case Throw(expr) =>
- print("throw ", expr)
-
- case Break(label) =>
- if (label.isEmpty) print("break")
- else print("break ", label.get)
-
- case Continue(label) =>
- if (label.isEmpty) print("continue")
- else print("continue ", label.get)
-
- case Switch(selector, cases, default) =>
- print("switch (", selector, ") ")
- print("{"); indent
- for ((value, body) <- cases) {
- println()
- print("case ", value, ":"); indent; println()
- printStat(body)
- print(";")
- undent
- }
- if (default != EmptyTree) {
- println()
- print("default:"); indent; println()
- printStat(default)
- print(";")
- undent
- }
- undent; println(); print("}")
-
- case Debugger() =>
- print("debugger")
-
- // Expressions
-
- case New(ctor, args) =>
- def containsOnlySelectsFromAtom(tree: Tree): Boolean = tree match {
- case DotSelect(qual, _) => containsOnlySelectsFromAtom(qual)
- case BracketSelect(qual, _) => containsOnlySelectsFromAtom(qual)
- case VarRef(_, _) => true
- case This() => true
- case _ => false // in particular, Apply
- }
- if (containsOnlySelectsFromAtom(ctor))
- print("new ", ctor)
- else
- print("new (", ctor, ")")
- printArgs(args)
-
- case DotSelect(qualifier, item) =>
- print(qualifier, ".", item)
-
- case BracketSelect(qualifier, item) =>
- print(qualifier, "[", item, "]")
-
- case Apply(fun, args) =>
- print(fun)
- printArgs(args)
-
- case Delete(prop) =>
- print("delete ", prop)
-
- case UnaryOp("typeof", lhs) =>
- print("typeof(", lhs, ")")
-
- case UnaryOp(op, lhs) =>
- print("(", op, lhs, ")")
-
- case BinaryOp(op, lhs, rhs) =>
- print("(", lhs, " ", op, " ", rhs, ")")
-
- case ArrayConstr(items) =>
- printRow(items, "[", ", ", "]")
-
- case ObjectConstr(Nil) =>
- print("{}")
-
- case ObjectConstr(fields) =>
- print("{"); indent; println()
- printSeq(fields) {
- case (name, value) => print(name, ": ", value)
- } { _ =>
- print(",")
- println()
- }
- undent; println(); print("}")
-
- // Literals
-
- case Undefined() =>
- print("(void 0)")
-
- case Null() =>
- print("null")
-
- case BooleanLiteral(value) =>
- print(if (value) "true" else "false")
-
- case IntLiteral(value) =>
- if (value >= 0)
- print(value)
- else
- print("(", value, ")")
-
- case DoubleLiteral(value) =>
- if (value == 0 && 1 / value < 0)
- print("(-0)")
- else if (value >= 0)
- print(value)
- else
- print("(", value, ")")
-
- case StringLiteral(value) =>
- print("\"", escapeJS(value), "\"")
-
- // Atomic expressions
-
- case VarRef(ident, _) =>
- print(ident)
-
- case This() =>
- print("this")
-
- case Function(args, body) =>
- print("(function")
- printSig(args)
- printBlock(body)
- print(")")
-
- case _ =>
- print(s"<error, elem of class ${tree.getClass()}>")
- }
- }
-
- protected def printIdent(ident: Ident): Unit =
- printString(escapeJS(ident.name))
-
- def printOne(arg: Any): Unit = arg match {
- case tree: Tree =>
- printTree(tree, isStat = false)
- case ident: Ident =>
- printIdent(ident)
- case arg =>
- printString(if (arg == null) "null" else arg.toString)
- }
-
- protected def printString(s: String): Unit = {
- out.write(s)
- }
-
- // Make it public
- override def println(): Unit = super.println()
-
- def complete(): Unit = ()
- }
-
- class JSTreePrinterWithSourceMap(_out: Writer,
- sourceMap: SourceMapWriter) extends JSTreePrinter(_out) {
-
- private var column = 0
-
- override def printTree(tree: Tree, isStat: Boolean): Unit = {
- val pos = tree.pos
- if (pos.isDefined)
- sourceMap.startNode(column, pos)
-
- super.printTree(tree, isStat)
-
- if (pos.isDefined)
- sourceMap.endNode(column)
- }
-
- override protected def printIdent(ident: Ident): Unit = {
- if (ident.pos.isDefined)
- sourceMap.startNode(column, ident.pos, ident.originalName)
- super.printIdent(ident)
- if (ident.pos.isDefined)
- sourceMap.endNode(column)
- }
-
- override def println(): Unit = {
- super.println()
- sourceMap.nextLine()
- column = this.indentMargin
- }
-
- override protected def printString(s: String): Unit = {
- // assume no EOL char in s, and assume s only has ASCII characters
- super.printString(s)
- column += s.length()
- }
-
- override def complete(): Unit = {
- sourceMap.complete()
- super.complete()
- }
- }
-
- /** Prints a tree to find original locations based on line numbers.
- * @param untilLine last 0-based line the positions should be recorded for
- */
- class ReverseSourceMapPrinter(untilLine: Int)
- extends JSTreePrinter(ReverseSourceMapPrinter.NullWriter) {
-
- private val positions = Array.fill(untilLine+1)(NoPosition)
- private var curLine = 0
-
- private val doneBreak = new Breaks
-
- def apply(x: Int): Position = positions(x)
-
- def reverseSourceMap(tree: Tree): Unit = doneBreak.breakable {
- printTopLevelTree(tree)
- }
-
- override def printTree(tree: Tree, isStat: Boolean): Unit = {
- if (positions(curLine).isEmpty)
- positions(curLine) = tree.pos
-
- super.printTree(tree, isStat)
- }
-
- override protected def printIdent(ident: Ident): Unit = {
- if (positions(curLine).isEmpty)
- positions(curLine) = ident.pos
-
- super.printIdent(ident)
- }
-
- override def println(): Unit = {
- super.println()
- curLine += 1
- if (curLine > untilLine)
- doneBreak.break()
- }
-
- override protected def printString(s: String): Unit = {
- // assume no EOL char in s, and assume s only has ASCII characters
- // therefore, we fully ignore the string
- }
- }
-
- object ReverseSourceMapPrinter {
- private object NullWriter extends Writer {
- def close(): Unit = ()
- def flush(): Unit = ()
- def write(buf: Array[Char], off: Int, len: Int): Unit = ()
- }
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala
deleted file mode 100644
index b249f88..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/ScalaJSClassEmitter.scala
+++ /dev/null
@@ -1,569 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-import scala.scalajs.ir._
-import Position._
-import Transformers._
-import scala.scalajs.ir.Trees._
-import Types._
-
-import scala.scalajs.tools.sem._
-import CheckedBehavior.Unchecked
-
-import scala.scalajs.tools.javascript.{Trees => js}
-
-/** Defines methods to emit Scala.js classes to JavaScript code.
- * The results are completely desugared.
- */
-final class ScalaJSClassEmitter(semantics: Semantics) {
-
- import JSDesugaring._
-
- /** Desugar a Scala.js class into ECMAScript 5 constructs */
- def genClassDef(tree: ClassDef): js.Tree = {
- implicit val pos = tree.pos
- val kind = tree.kind
-
- var reverseParts: List[js.Tree] = Nil
-
- if (kind == ClassKind.TraitImpl) {
- reverseParts ::= genTraitImpl(tree)
- } else {
- if (kind.isClass)
- reverseParts ::= genClass(tree)
- if (kind.isClass || kind == ClassKind.Interface ||
- tree.name.name == Definitions.StringClass)
- reverseParts ::= genInstanceTests(tree)
- reverseParts ::= genArrayInstanceTests(tree)
- reverseParts ::= genTypeData(tree)
- if (kind.isClass)
- reverseParts ::= genSetTypeData(tree)
- if (kind == ClassKind.ModuleClass)
- reverseParts ::= genModuleAccessor(tree)
- if (kind.isClass)
- reverseParts ::= genClassExports(tree)
- }
-
- js.Block(reverseParts.reverse)
- }
-
- def genClass(tree: ClassDef): js.Tree = {
- val className = tree.name.name
- val typeFunctionDef = genConstructor(tree)
- val memberDefs = tree.defs collect {
- case m: MethodDef =>
- genMethod(className, m)
- case p: PropertyDef =>
- genProperty(className, p)
- }
-
- js.Block(typeFunctionDef :: memberDefs)(tree.pos)
- }
-
- /** Generates the JS constructor for a class. */
- def genConstructor(tree: ClassDef): js.Tree = {
- assert(tree.kind.isClass)
-
- val classIdent = tree.name
- val className = classIdent.name
- val tpe = ClassType(className)
-
- assert(tree.parent.isDefined || className == Definitions.ObjectClass,
- "Class $className is missing a parent class")
-
- val ctorFun = {
- val superCtorCall = tree.parent.fold[js.Tree] {
- js.Skip()(tree.pos)
- } { parentIdent =>
- implicit val pos = tree.pos
- js.Apply(
- js.DotSelect(encodeClassVar(parentIdent.name), js.Ident("call")),
- List(js.This()))
- }
- val fieldDefs = for {
- field @ VarDef(name, vtpe, mutable, rhs) <- tree.defs
- } yield {
- implicit val pos = field.pos
- desugarJavaScript(
- Assign(Select(This()(tpe), name, mutable)(vtpe), rhs),
- semantics)
- }
- js.Function(Nil,
- js.Block(superCtorCall :: fieldDefs)(tree.pos))(tree.pos)
- }
-
- {
- implicit val pos = tree.pos
- val typeVar = encodeClassVar(className)
- val docComment = js.DocComment("@constructor")
- val ctorDef = js.Assign(typeVar, ctorFun)
-
- val chainProto = tree.parent.fold[js.Tree] {
- js.Skip()
- } { parentIdent =>
- js.Block(
- js.Assign(typeVar.prototype,
- js.New(js.DotSelect(envField("h"), parentIdent), Nil)),
- genAddToPrototype(className, js.Ident("constructor"), typeVar)
- )
- }
-
- val inheritableCtorDef = {
- val inheritableCtorVar =
- js.DotSelect(envField("h"), classIdent)
- js.Block(
- js.DocComment("@constructor"),
- js.Assign(inheritableCtorVar, js.Function(Nil, js.Skip())),
- js.Assign(inheritableCtorVar.prototype, typeVar.prototype)
- )
- }
-
- js.Block(docComment, ctorDef, chainProto, inheritableCtorDef)
- }
- }
-
- /** Generates a method. */
- def genMethod(className: String, method: MethodDef): js.Tree = {
- implicit val pos = method.pos
- val methodFun = js.Function(method.args.map(transformParamDef),
- desugarBody(method.body, method.resultType == NoType))
- genAddToPrototype(className, method.name, methodFun)
- }
-
- /** Generates a property. */
- def genProperty(className: String, property: PropertyDef): js.Tree = {
- implicit val pos = property.pos
- val classType = ClassType(className)
-
- // defineProperty method
- val defProp =
- js.BracketSelect(js.VarRef(js.Ident("Object"), false),
- js.StringLiteral("defineProperty"))
-
- // class prototype
- val proto = encodeClassVar(className).prototype
-
- // property name
- val name = property.name match {
- case StringLiteral(value) =>
- js.StringLiteral(value)
- case id: Ident =>
- // We need to work around the closure compiler. Call propertyName to
- // get a string representation of the optimized name
- genCallHelper("propertyName",
- js.ObjectConstr(transformIdent(id) -> js.IntLiteral(0) :: Nil))
- }
-
- // Options passed to the defineProperty method
- val descriptor = js.ObjectConstr {
- // Basic config
- val base =
- js.StringLiteral("enumerable") -> js.BooleanLiteral(true) :: Nil
-
- // Optionally add getter
- val wget =
- if (property.getterBody == EmptyTree) base
- else js.StringLiteral("get") ->
- js.Function(Nil, desugarBody(property.getterBody, isStat = false)) :: base
-
- // Optionally add setter
- if (property.setterBody == EmptyTree) wget
- else js.StringLiteral("set") ->
- js.Function(transformParamDef(property.setterArg) :: Nil,
- desugarBody(property.setterBody, isStat = true)) :: wget
- }
-
- js.Apply(defProp, proto :: name :: descriptor :: Nil)
- }
-
- /** Generate `classVar.prototype.name = value` */
- def genAddToPrototype(className: String, name: js.PropertyName,
- value: js.Tree)(implicit pos: Position): js.Tree = {
- val proto = encodeClassVar(className).prototype
- val select = name match {
- case name: js.Ident => js.DotSelect(proto, name)
- case name: js.StringLiteral => js.BracketSelect(proto, name)
- }
- js.Assign(select, value)
- }
-
- /** Generate `classVar.prototype.name = value` */
- def genAddToPrototype(className: String, name: PropertyName,
- value: js.Tree)(implicit pos: Position): js.Tree = {
- val newName = name match {
- case ident: Ident => transformIdent(ident)
- case StringLiteral(value) => js.StringLiteral(value)
- }
- genAddToPrototype(className, newName, value)
- }
-
- def genInstanceTests(tree: ClassDef): js.Tree = {
- import Definitions._
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- val classIdent = transformIdent(tree.name)
- val className = classIdent.name
- val displayName = decodeClassName(className)
-
- val isAncestorOfString =
- AncestorsOfStringClass.contains(className)
- val isAncestorOfHijackedNumberClass =
- AncestorsOfHijackedNumberClasses.contains(className)
- val isAncestorOfBoxedBooleanClass =
- AncestorsOfBoxedBooleanClass.contains(className)
-
- val objParam = js.ParamDef(Ident("obj"), mutable = false)
- val obj = objParam.ref
-
- val createIsStat = {
- envField("is") DOT classIdent :=
- js.Function(List(objParam), js.Return(className match {
- case Definitions.ObjectClass =>
- js.BinaryOp("!==", obj, js.Null())
-
- case Definitions.StringClass =>
- js.UnaryOp("typeof", obj) === js.StringLiteral("string")
-
- case _ =>
- var test = (obj && (obj DOT "$classData") &&
- (obj DOT "$classData" DOT "ancestors" DOT classIdent))
-
- if (isAncestorOfString)
- test = test || (
- js.UnaryOp("typeof", obj) === js.StringLiteral("string"))
- if (isAncestorOfHijackedNumberClass)
- test = test || (
- js.UnaryOp("typeof", obj) === js.StringLiteral("number"))
- if (isAncestorOfBoxedBooleanClass)
- test = test || (
- js.UnaryOp("typeof", obj) === js.StringLiteral("boolean"))
-
- !(!test)
- }))
- }
-
- val createAsStat = if (semantics.asInstanceOfs == Unchecked) {
- js.Skip()
- } else {
- envField("as") DOT classIdent :=
- js.Function(List(objParam), js.Return(className match {
- case Definitions.ObjectClass =>
- obj
-
- case _ =>
- js.If(js.Apply(envField("is") DOT classIdent, List(obj)) ||
- (obj === js.Null()), {
- obj
- }, {
- genCallHelper("throwClassCastException",
- obj, js.StringLiteral(displayName))
- })
- }))
- }
-
- js.Block(createIsStat, createAsStat)
- }
-
- def genArrayInstanceTests(tree: ClassDef): js.Tree = {
- import Definitions._
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- val classIdent = transformIdent(tree.name)
- val className = classIdent.name
- val displayName = decodeClassName(className)
-
- val objParam = js.ParamDef(Ident("obj"), mutable = false)
- val obj = objParam.ref
-
- val depthParam = js.ParamDef(Ident("depth"), mutable = false)
- val depth = depthParam.ref
-
- val createIsArrayOfStat = {
- envField("isArrayOf") DOT classIdent :=
- js.Function(List(objParam, depthParam), className match {
- case Definitions.ObjectClass =>
- val dataVarDef = js.VarDef(Ident("data"), false, {
- obj && (obj DOT "$classData")
- })
- val data = dataVarDef.ref
- js.Block(
- dataVarDef,
- js.If(!data, {
- js.Return(js.BooleanLiteral(false))
- }, {
- val arrayDepthVarDef = js.VarDef(Ident("arrayDepth"), false, {
- (data DOT "arrayDepth") || js.IntLiteral(0)
- })
- val arrayDepth = arrayDepthVarDef.ref
- js.Block(
- arrayDepthVarDef,
- js.Return {
- // Array[A] </: Array[Array[A]]
- !js.BinaryOp("<", arrayDepth, depth) && (
- // Array[Array[A]] <: Array[Object]
- js.BinaryOp(">", arrayDepth, depth) ||
- // Array[Int] </: Array[Object]
- !js.BracketSelect(data DOT "arrayBase", js.StringLiteral("isPrimitive"))
- )
- })
- }))
-
- case _ =>
- js.Return(!(!(obj && (obj DOT "$classData") &&
- ((obj DOT "$classData" DOT "arrayDepth") === depth) &&
- (obj DOT "$classData" DOT "arrayBase" DOT "ancestors" DOT classIdent))))
- })
- }
-
- val createAsArrayOfStat = if (semantics.asInstanceOfs == Unchecked) {
- js.Skip()
- } else {
- envField("asArrayOf") DOT classIdent :=
- js.Function(List(objParam, depthParam), js.Return {
- js.If(js.Apply(envField("isArrayOf") DOT classIdent, List(obj, depth)) ||
- (obj === js.Null()), {
- obj
- }, {
- genCallHelper("throwArrayCastException",
- obj, js.StringLiteral("L"+displayName+";"), depth)
- })
- })
- }
-
- js.Block(createIsArrayOfStat, createAsArrayOfStat)
- }
-
- def genTypeData(tree: ClassDef): js.Tree = {
- import Definitions._
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- val classIdent = transformIdent(tree.name)
- val className = classIdent.name
- val kind = tree.kind
- assert(kind.isType)
-
- val isObjectClass =
- className == ObjectClass
- val isHijackedBoxedClass =
- HijackedBoxedClasses.contains(className)
- val isAncestorOfHijackedClass =
- AncestorsOfHijackedClasses.contains(className)
-
- val parentData = tree.parent.fold[js.Tree] {
- if (isObjectClass) js.Null()
- else js.Undefined()
- } { parent =>
- envField("d") DOT parent
- }
-
- val ancestorsRecord = js.ObjectConstr(
- for (ancestor <- classIdent :: tree.ancestors.map(transformIdent))
- yield (ancestor, js.IntLiteral(1)))
-
- val typeData = js.New(envField("ClassTypeData"), List(
- js.ObjectConstr(List(classIdent -> js.IntLiteral(0))),
- js.BooleanLiteral(kind == ClassKind.Interface),
- js.StringLiteral(decodeClassName(className)),
- parentData,
- ancestorsRecord
- ) ++ (
- // Optional parameter isInstance
- if (isObjectClass) {
- /* Object has special ScalaJS.is.O *and* ScalaJS.isArrayOf.O. */
- List(
- envField("is") DOT classIdent,
- envField("isArrayOf") DOT classIdent)
- } else if (isHijackedBoxedClass) {
- /* Hijacked boxed classes have a special isInstanceOf test. */
- val xParam = js.ParamDef(Ident("x"), mutable = false)
- List(js.Function(List(xParam), js.Return {
- genIsInstanceOf(xParam.ref, ClassType(className))
- }))
- } else if (isAncestorOfHijackedClass || className == StringClass) {
- /* java.lang.String and ancestors of hijacked classes have a normal
- * ScalaJS.is.pack_Class test but with a non-standard behavior. */
- List(envField("is") DOT classIdent)
- } else {
- // For other classes, the isInstance function can be inferred.
- Nil
- }
- ))
-
- envField("d") DOT classIdent := typeData
- }
-
- def genSetTypeData(tree: ClassDef): js.Tree = {
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- assert(tree.kind.isClass)
-
- encodeClassVar(tree.name.name).prototype DOT "$classData" :=
- envField("d") DOT tree.name
- }
-
- def genModuleAccessor(tree: ClassDef): js.Tree = {
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- val classIdent = transformIdent(tree.name)
- val className = classIdent.name
- val tpe = ClassType(className)
-
- require(tree.kind == ClassKind.ModuleClass,
- s"genModuleAccessor called with non-module class: $className")
- assert(className.endsWith("$"))
-
- val moduleName = className.dropRight(1)
- val moduleIdent = Ident(moduleName)
-
- val moduleInstanceVar = envField("n") DOT moduleIdent
- val accessorVar = envField("m") DOT moduleIdent
-
- val createModuleInstanceField = {
- moduleInstanceVar := js.Undefined()
- }
-
- val createAccessor = {
- accessorVar := js.Function(Nil, js.Block(
- js.If(!(moduleInstanceVar), {
- moduleInstanceVar :=
- js.Apply(js.New(encodeClassVar(className), Nil) DOT js.Ident("init___"), Nil)
- }, js.Skip()),
- js.Return(moduleInstanceVar)
- ))
- }
-
- js.Block(createModuleInstanceField, createAccessor)
- }
-
- def genClassExports(tree: ClassDef): js.Tree = {
- val exports = tree.defs collect {
- case e: ConstructorExportDef =>
- genConstructorExportDef(tree, e)
- case e: ModuleExportDef =>
- genModuleExportDef(tree, e)
- }
-
- js.Block(exports)(tree.pos)
- }
-
- def genConstructorExportDef(cd: ClassDef, tree: ConstructorExportDef): js.Tree = {
- import TreeDSL._
-
- implicit val pos = tree.pos
- val classType = ClassType(cd.name.name)
- val ConstructorExportDef(fullName, args, body) = tree
-
- val baseCtor = envField("c") DOT cd.name
- val (createNamespace, expCtorVar) = genCreateNamespaceInExports(fullName)
-
- js.Block(
- createNamespace,
- js.DocComment("@constructor"),
- expCtorVar := js.Function(args.map(transformParamDef), js.Block(
- js.Apply(js.DotSelect(baseCtor, js.Ident("call")), List(js.This())),
- desugarBody(body, isStat = true)
- )),
- expCtorVar DOT "prototype" := baseCtor DOT "prototype"
- )
- }
-
- def genModuleExportDef(cd: ClassDef, tree: ModuleExportDef): js.Tree = {
- import TreeDSL._
-
- implicit val pos = tree.pos
-
- val baseAccessor =
- envField("m") DOT cd.name.name.dropRight(1)
- val (createNamespace, expAccessorVar) =
- genCreateNamespaceInExports(tree.fullName)
-
- js.Block(
- createNamespace,
- expAccessorVar := baseAccessor
- )
- }
-
- def genTraitImpl(tree: ClassDef): js.Tree = {
- val traitImplName = tree.name.name
- val defs = tree.defs collect {
- case m: MethodDef =>
- genTraitImplMethod(traitImplName, m)
- }
- js.Block(defs)(tree.pos)
- }
-
- def genTraitImplMethod(traitImplName: String, tree: MethodDef): js.Tree = {
- implicit val pos = tree.pos
- val MethodDef(name: Ident, args, resultType, body) = tree
- js.Assign(
- js.DotSelect(envField("i"), name),
- js.Function(args.map(transformParamDef),
- desugarBody(body, resultType == NoType)))
- }
-
- /** Generate a dummy parent. Used by ScalaJSOptimizer */
- def genDummyParent(name: String): js.Tree = {
- implicit val pos = Position.NoPosition
-
- js.Block(
- js.DocComment("@constructor (dummy parent)"))
- js.Assign(js.DotSelect(envField("h"), js.Ident(name)),
- js.Function(Nil, js.Skip())
- )
- }
-
- // Helpers
-
- /** Desugars a function body of the IR into ES5 JavaScript. */
- private def desugarBody(tree: Tree, isStat: Boolean): js.Tree = {
- implicit val pos = tree.pos
- val withReturn =
- if (isStat) tree
- else Return(tree)
- desugarJavaScript(withReturn, semantics) match {
- case js.Block(stats :+ js.Return(js.Undefined())) => js.Block(stats)
- case other => other
- }
- }
-
- /** Gen JS code for assigning an rhs to a qualified name in the exports scope.
- * For example, given the qualified name "foo.bar.Something", generates:
- *
- * ScalaJS.e["foo"] = ScalaJS.e["foo"] || {};
- * ScalaJS.e["foo"]["bar"] = ScalaJS.e["foo"]["bar"] || {};
- *
- * Returns (statements, ScalaJS.e["foo"]["bar"]["Something"])
- */
- private def genCreateNamespaceInExports(qualName: String)(
- implicit pos: Position): (js.Tree, js.Tree) = {
- val parts = qualName.split("\\.")
- val statements = List.newBuilder[js.Tree]
- var namespace = envField("e")
- for (i <- 0 until parts.length-1) {
- namespace = js.BracketSelect(namespace, js.StringLiteral(parts(i)))
- statements +=
- js.Assign(namespace, js.BinaryOp("||", namespace, js.ObjectConstr(Nil)))
- }
- val lhs = js.BracketSelect(namespace, js.StringLiteral(parts.last))
- (js.Block(statements.result()), lhs)
- }
-
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala
deleted file mode 100644
index 3ac54d8..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/TreeDSL.scala
+++ /dev/null
@@ -1,50 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-import scala.language.implicitConversions
-
-import scala.scalajs.ir.Position
-
-import Trees._
-
-private[javascript] object TreeDSL {
- implicit class TreeOps(val self: Tree) extends AnyVal {
- /** Select a member */
- def DOT(field: Ident)(implicit pos: Position): DotSelect =
- DotSelect(self, field)
-
- /** Select a member */
- def DOT(field: String)(implicit pos: Position): DotSelect =
- DotSelect(self, Ident(field))
-
- // Some operators that we use
-
- def ===(that: Tree)(implicit pos: Position): Tree =
- BinaryOp("===", self, that)
- def ===(that: String)(implicit pos: Position): Tree =
- BinaryOp("===", self, StringLiteral(that))
-
- def unary_!()(implicit pos: Position): Tree =
- UnaryOp("!", self)
- def &&(that: Tree)(implicit pos: Position): Tree =
- BinaryOp("&&", self, that)
- def ||(that: Tree)(implicit pos: Position): Tree =
- BinaryOp("||", self, that)
-
- // Other constructs
-
- def :=(that: Tree)(implicit pos: Position): Tree =
- Assign(self, that)
- }
-
- def typeof(expr: Tree)(implicit pos: Position): Tree =
- UnaryOp("typeof", expr)
-}
diff --git a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala b/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala
deleted file mode 100644
index 0b86d1b..0000000
--- a/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/javascript/Trees.scala
+++ /dev/null
@@ -1,194 +0,0 @@
-/* __ *\
-** ________ ___ / / ___ __ ____ Scala.js tools **
-** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
-** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
-** /____/\___/_/ |_/____/_/ | |__/ /____/ **
-** |/____/ **
-\* */
-
-
-package scala.scalajs.tools.javascript
-
-import scala.annotation.switch
-
-import scala.scalajs.ir
-import ir.Position
-import ir.Position.NoPosition
-
-object Trees {
- import ir.Trees.requireValidIdent
-
- /** AST node of JavaScript. */
- abstract sealed class Tree {
- val pos: Position
-
- def show: String = {
- val writer = new java.io.StringWriter
- val printer = new Printers.JSTreePrinter(writer)
- printer.printTree(this, isStat = true)
- writer.toString()
- }
- }
-
- case object EmptyTree extends Tree {
- val pos = NoPosition
- }
-
- // Comments
-
- case class DocComment(text: String)(implicit val pos: Position) extends Tree
-
- // Identifiers and properties
-
- sealed trait PropertyName {
- def name: String
- def pos: Position
- }
-
- case class Ident(name: String, originalName: Option[String])(
- implicit val pos: Position) extends PropertyName {
- requireValidIdent(name)
- }
-
- object Ident {
- def apply(name: String)(implicit pos: Position): Ident =
- new Ident(name, Some(name))
- }
-
- // Definitions
-
- case class VarDef(name: Ident, mutable: Boolean, rhs: Tree)(implicit val pos: Position) extends Tree {
- def ref(implicit pos: Position): Tree =
- VarRef(name, mutable = mutable)
- }
-
- case class ParamDef(name: Ident, mutable: Boolean)(implicit val pos: Position) extends Tree {
- def ref(implicit pos: Position): Tree =
- VarRef(name, mutable = mutable)
- }
-
- // Control flow constructs
-
- case class Skip()(implicit val pos: Position) extends Tree
-
- class Block private (val stats: List[Tree])(implicit val pos: Position) extends Tree {
- override def toString(): String =
- stats.mkString("Block(", ",", ")")
- }
-
- object Block {
- def apply(stats: List[Tree])(implicit pos: Position): Tree = {
- val flattenedStats = stats flatMap {
- case Skip() => Nil
- case Block(subStats) => subStats
- case other => other :: Nil
- }
- flattenedStats match {
- case Nil => Skip()
- case only :: Nil => only
- case _ => new Block(flattenedStats)
- }
- }
-
- def apply(stats: Tree*)(implicit pos: Position): Tree =
- apply(stats.toList)
-
- def unapply(block: Block): Some[List[Tree]] = Some(block.stats)
- }
-
- case class Labeled(label: Ident, body: Tree)(implicit val pos: Position) extends Tree
-
- case class Assign(lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree {
- require(lhs match {
- case _:VarRef | _:DotSelect | _:BracketSelect => true
- case _ => false
- }, s"Invalid lhs for Assign: $lhs")
- }
-
- case class Return(expr: Tree)(implicit val pos: Position) extends Tree
-
- case class If(cond: Tree, thenp: Tree, elsep: Tree)(implicit val pos: Position) extends Tree
-
- case class While(cond: Tree, body: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree
-
- case class DoWhile(body: Tree, cond: Tree, label: Option[Ident] = None)(implicit val pos: Position) extends Tree
-
- case class Try(block: Tree, errVar: Ident, handler: Tree, finalizer: Tree)(implicit val pos: Position) extends Tree
-
- case class Throw(expr: Tree)(implicit val pos: Position) extends Tree
-
- case class Break(label: Option[Ident] = None)(implicit val pos: Position) extends Tree
-
- case class Continue(label: Option[Ident] = None)(implicit val pos: Position) extends Tree
-
- case class Switch(selector: Tree, cases: List[(Tree, Tree)], default: Tree)(implicit val pos: Position) extends Tree
-
- case class Debugger()(implicit val pos: Position) extends Tree
-
- // Expressions
-
- case class New(ctor: Tree, args: List[Tree])(implicit val pos: Position) extends Tree
-
- case class DotSelect(qualifier: Tree, item: Ident)(implicit val pos: Position) extends Tree
-
- case class BracketSelect(qualifier: Tree, item: Tree)(implicit val pos: Position) extends Tree
-
- /** Syntactic apply.
- * It is a method call if fun is a dot-select or bracket-select. It is a
- * function call otherwise.
- */
- case class Apply(fun: Tree, args: List[Tree])(implicit val pos: Position) extends Tree
-
- case class Delete(prop: Tree)(implicit val pos: Position) extends Tree {
- require(prop match {
- case _:DotSelect | _:BracketSelect => true
- case _ => false
- }, s"Invalid prop for Delete: $prop")
- }
-
- /** Unary operation (always preserves pureness).
- *
- * Operations which do not preserve pureness are not allowed in this tree.
- * These are notably ++ and --
- */
- case class UnaryOp(op: String, lhs: Tree)(implicit val pos: Position) extends Tree
-
- /** Binary operation (always preserves pureness).
- *
- * Operations which do not preserve pureness are not allowed in this tree.
- * These are notably +=, -=, *=, /= and %=
- */
- case class BinaryOp(op: String, lhs: Tree, rhs: Tree)(implicit val pos: Position) extends Tree
-
- case class ArrayConstr(items: List[Tree])(implicit val pos: Position) extends Tree
-
- case class ObjectConstr(fields: List[(PropertyName, Tree)])(implicit val pos: Position) extends Tree
-
- // Literals
-
- /** Marker for literals. Literals are always pure. */
- sealed trait Literal extends Tree
-
- case class Undefined()(implicit val pos: Position) extends Literal
-
- case class Null()(implicit val pos: Position) extends Literal
-
- case class BooleanLiteral(value: Boolean)(implicit val pos: Position) extends Literal
-
- case class IntLiteral(value: Int)(implicit val pos: Position) extends Literal
-
- case class DoubleLiteral(value: Double)(implicit val pos: Position) extends Literal
-
- case class StringLiteral(value: String)(
- implicit val pos: Position) extends Literal with PropertyName {
- override def name = value
- }
-
- // Atomic expressions
-
- case class VarRef(ident: Ident, mutable: Boolean)(implicit val pos: Position) extends Tree
-
- case class This()(implicit val pos: Position) extends Tree
-
- case class Function(args: List[ParamDef], body: Tree)(implicit val pos: Position) extends Tree
-}