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