All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalajs.ir.Transformers.scala Maven / Gradle / Ivy

/*
 * Scala.js (https://www.scala-js.org/)
 *
 * Copyright EPFL.
 *
 * Licensed under Apache License 2.0
 * (https://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package org.scalajs.ir

import Trees._
import Types._
import Version.Unversioned

object Transformers {

  abstract class Transformer {
    final def transformStats(trees: List[Tree]): List[Tree] =
      trees.map(transformStat(_))

    final def transformStat(tree: Tree): Tree =
      transform(tree, isStat = true)

    final def transformExpr(tree: Tree): Tree =
      transform(tree, isStat = false)

    def transformExprOrJSSpread(tree: TreeOrJSSpread): TreeOrJSSpread = {
      implicit val pos = tree.pos

      tree match {
        case JSSpread(items) => JSSpread(transformExpr(items))
        case tree: Tree      => transformExpr(tree)
      }
    }

    def transform(tree: Tree, isStat: Boolean): Tree = {
      implicit val pos = tree.pos

      tree match {
        // Definitions

        case VarDef(ident, originalName, vtpe, mutable, rhs) =>
          VarDef(ident, originalName, vtpe, mutable, transformExpr(rhs))

        // Control flow constructs

        case Block(stats) =>
          Block(stats.init.map(transformStat) :+ transform(stats.last, isStat))

        case Labeled(label, tpe, body) =>
          Labeled(label, tpe, transform(body, isStat))

        case Assign(lhs, rhs) =>
          Assign(transformExpr(lhs).asInstanceOf[AssignLhs], transformExpr(rhs))

        case Return(expr, label) =>
          Return(transformExpr(expr), label)

        case If(cond, thenp, elsep) =>
          If(transformExpr(cond), transform(thenp, isStat),
              transform(elsep, isStat))(tree.tpe)

        case While(cond, body) =>
          While(transformExpr(cond), transformStat(body))

        case ForIn(obj, keyVar, keyVarOriginalName, body) =>
          ForIn(transformExpr(obj), keyVar, keyVarOriginalName,
              transformStat(body))

        case TryCatch(block, errVar, errVarOriginalName, handler) =>
          TryCatch(transform(block, isStat), errVar, errVarOriginalName,
              transform(handler, isStat))(tree.tpe)

        case TryFinally(block, finalizer) =>
          TryFinally(transform(block, isStat), transformStat(finalizer))

        case Throw(expr) =>
          Throw(transformExpr(expr))

        case Match(selector, cases, default) =>
          Match(transformExpr(selector),
              cases map (c => (c._1, transform(c._2, isStat))),
              transform(default, isStat))(tree.tpe)

        // Scala expressions

        case New(className, ctor, args) =>
          New(className, ctor, args map transformExpr)

        case Select(qualifier, field) =>
          Select(transformExpr(qualifier), field)(tree.tpe)

        case Apply(flags, receiver, method, args) =>
          Apply(flags, transformExpr(receiver), method,
              args map transformExpr)(tree.tpe)

        case ApplyStatically(flags, receiver, className, method, args) =>
          ApplyStatically(flags, transformExpr(receiver), className, method,
              args map transformExpr)(tree.tpe)

        case ApplyStatic(flags, className, method, args) =>
          ApplyStatic(flags, className, method, args map transformExpr)(tree.tpe)

        case ApplyDynamicImport(flags, className, method, args) =>
          ApplyDynamicImport(flags, className, method, args.map(transformExpr))

        case UnaryOp(op, lhs) =>
          UnaryOp(op, transformExpr(lhs))

        case BinaryOp(op, lhs, rhs) =>
          BinaryOp(op, transformExpr(lhs), transformExpr(rhs))

        case NewArray(tpe, length) =>
          NewArray(tpe, transformExpr(length))

        case ArrayValue(tpe, elems) =>
          ArrayValue(tpe, elems map transformExpr)

        case ArrayLength(array) =>
          ArrayLength(transformExpr(array))

        case ArraySelect(array, index) =>
          ArraySelect(transformExpr(array), transformExpr(index))(tree.tpe)

        case RecordValue(tpe, elems) =>
          RecordValue(tpe, elems map transformExpr)

        case RecordSelect(record, field) =>
          RecordSelect(transformExpr(record), field)(tree.tpe)

        case IsInstanceOf(expr, testType) =>
          IsInstanceOf(transformExpr(expr), testType)

        case AsInstanceOf(expr, tpe) =>
          AsInstanceOf(transformExpr(expr), tpe)

        case GetClass(expr) =>
          GetClass(transformExpr(expr))

        case Clone(expr) =>
          Clone(transformExpr(expr))

        case IdentityHashCode(expr) =>
          IdentityHashCode(transformExpr(expr))

        case WrapAsThrowable(expr) =>
          WrapAsThrowable(transformExpr(expr))

        case UnwrapFromThrowable(expr) =>
          UnwrapFromThrowable(transformExpr(expr))

        // JavaScript expressions

        case JSNew(ctor, args) =>
          JSNew(transformExpr(ctor), args.map(transformExprOrJSSpread))

        case JSPrivateSelect(qualifier, field) =>
          JSPrivateSelect(transformExpr(qualifier), field)

        case JSSelect(qualifier, item) =>
          JSSelect(transformExpr(qualifier), transformExpr(item))

        case JSFunctionApply(fun, args) =>
          JSFunctionApply(transformExpr(fun), args.map(transformExprOrJSSpread))

        case JSMethodApply(receiver, method, args) =>
          JSMethodApply(transformExpr(receiver), transformExpr(method),
              args.map(transformExprOrJSSpread))

        case JSSuperSelect(superClass, qualifier, item) =>
          JSSuperSelect(superClass, transformExpr(qualifier),
              transformExpr(item))

        case JSSuperMethodCall(superClass, receiver, method, args) =>
          JSSuperMethodCall(superClass, transformExpr(receiver),
              transformExpr(method), args.map(transformExprOrJSSpread))

        case JSSuperConstructorCall(args) =>
          JSSuperConstructorCall(args.map(transformExprOrJSSpread))

        case JSImportCall(arg) =>
          JSImportCall(transformExpr(arg))

        case JSDelete(qualifier, item) =>
          JSDelete(transformExpr(qualifier), transformExpr(item))

        case JSUnaryOp(op, lhs) =>
          JSUnaryOp(op, transformExpr(lhs))

        case JSBinaryOp(op, lhs, rhs) =>
          JSBinaryOp(op, transformExpr(lhs), transformExpr(rhs))

        case JSArrayConstr(items) =>
          JSArrayConstr(items.map(transformExprOrJSSpread))

        case JSObjectConstr(fields) =>
          JSObjectConstr(fields.map { field =>
            (transformExpr(field._1), transformExpr(field._2))
          })

        case JSTypeOfGlobalRef(globalRef) =>
          JSTypeOfGlobalRef(transformExpr(globalRef).asInstanceOf[JSGlobalRef])

        // Atomic expressions

        case Closure(arrow, captureParams, params, restParam, body, captureValues) =>
          Closure(arrow, captureParams, params, restParam, transformExpr(body),
              captureValues.map(transformExpr))

        case CreateJSClass(className, captureValues) =>
          CreateJSClass(className, captureValues.map(transformExpr))

        // Transients
        case Transient(value) =>
          value.transform(this, isStat)

        // Trees that need not be transformed

        case _:Skip | _:Debugger | _:LoadModule | _:StoreModule |
            _:SelectStatic | _:SelectJSNativeMember | _:LoadJSConstructor |
            _:LoadJSModule | _:JSNewTarget | _:JSImportMeta | _:JSLinkingInfo |
            _:Literal | _:VarRef | _:This | _:JSGlobalRef  =>
          tree
      }
    }
  }

  abstract class ClassTransformer extends Transformer {
    def transformClassDef(tree: ClassDef): ClassDef = {
      import tree._
      ClassDef(name, originalName, kind, jsClassCaptures, superClass,
          interfaces, jsSuperClass.map(transformExpr), jsNativeLoadSpec,
          fields.map(transformAnyFieldDef(_)),
          methods.map(transformMethodDef), jsConstructor.map(transformJSConstructorDef),
          jsMethodProps.map(transformJSMethodPropDef), jsNativeMembers,
          topLevelExportDefs.map(transformTopLevelExportDef))(
          tree.optimizerHints)(tree.pos)
    }

    def transformAnyFieldDef(fieldDef: AnyFieldDef): AnyFieldDef =
      fieldDef

    def transformMethodDef(methodDef: MethodDef): MethodDef = {
      val MethodDef(flags, name, originalName, args, resultType, body) = methodDef
      val newBody = body.map(transform(_, isStat = resultType == NoType))
      MethodDef(flags, name, originalName, args, resultType, newBody)(
          methodDef.optimizerHints, Unversioned)(methodDef.pos)
    }

    def transformJSConstructorDef(jsConstructor: JSConstructorDef): JSConstructorDef = {
      val JSConstructorDef(flags, args, restParam, body) = jsConstructor
      JSConstructorDef(flags, args, restParam, transformJSConstructorBody(body))(
          jsConstructor.optimizerHints, Unversioned)(jsConstructor.pos)
    }

    def transformJSMethodPropDef(jsMethodPropDef: JSMethodPropDef): JSMethodPropDef = {
      jsMethodPropDef match {
        case jsMethodDef: JSMethodDef =>
          transformJSMethodDef(jsMethodDef)

        case JSPropertyDef(flags, name, getterBody, setterArgAndBody) =>
          JSPropertyDef(
              flags,
              transformExpr(name),
              getterBody.map(transformStat),
              setterArgAndBody map { case (arg, body) =>
                (arg, transformStat(body))
              })(Unversioned)(jsMethodPropDef.pos)
      }
    }

    def transformJSMethodDef(jsMethodDef: JSMethodDef): JSMethodDef = {
      val JSMethodDef(flags, name, args, restParam, body) = jsMethodDef
      JSMethodDef(flags, transformExpr(name), args, restParam, transformExpr(body))(
          jsMethodDef.optimizerHints, Unversioned)(jsMethodDef.pos)
    }

    def transformJSConstructorBody(body: JSConstructorBody): JSConstructorBody = {
      implicit val pos = body.pos

      val newBeforeSuper = body.beforeSuper.map(transformStat(_))
      val newSuperCall = transformStat(body.superCall).asInstanceOf[JSSuperConstructorCall]
      val newAfterSuper = body.afterSuper match {
        case stats :+ expr => stats.map(transformStat(_)) :+ transformExpr(expr)
        case empty         => empty // cannot use Nil here because the compiler does not know that it is exhaustive
      }

      JSConstructorBody(newBeforeSuper, newSuperCall, newAfterSuper)
    }

    def transformTopLevelExportDef(
        exportDef: TopLevelExportDef): TopLevelExportDef = {

      implicit val pos = exportDef.pos

      exportDef match {
        case _:TopLevelJSClassExportDef | _:TopLevelModuleExportDef |
            _:TopLevelFieldExportDef =>
          exportDef

        case TopLevelMethodExportDef(moduleID, methodDef) =>
          TopLevelMethodExportDef(moduleID, transformJSMethodDef(methodDef))
      }
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy