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

edu.stanford.dawn.virtualized.LanguageVirtualization.scala Maven / Gradle / Ivy

The newest version!
package virtualized

import scala.reflect.macros.blackbox.Context
import language.experimental.macros
import scala.collection.mutable

/**
 * Converts Scala features that can not be overridden to method calls that can be given
 * arbitrary semantics.
 *
 * ==Features covered are==
 * {{{
 *   var x = e              =>       var x = __newVar(e)
 *   if(c) t else e         =>       __ifThenElse(c, t, e)
 *   return t               =>       __return(t)
 *   x = t                  =>       __assign(x, t)
 *   while(c) b             =>       __whileDo(c, b)
 *   do b while c           =>       __doWhile(c, b)
 * }}}
 *
 * ===Poor man's infix methods for `Any` methods===
 * {{{
 *   t == t1                =>       infix_==(t, t1)
 *   t != t1                =>       infix_!=(t, t1)
 *   t.##                   =>       infix_##(t, t1)
 *   t.equals t1            =>       infix_equals(t, t1)
 *   t.hashCode             =>       infix_hashCode(t)
 *   t.asInstanceOf[T]      =>       infix_asInstanceOf[T](t)
 *   t.isInstanceOf[T]      =>       infix_isInstanceOf[T](t)
 *   t.toString             =>       infix_toString(t)
 *   t.getClass             =>       infix_getClass(t)
 * }}}
 *
 * ===Poor man's infix methods for `AnyRef` methods===
 * {{{
 *   t eq t1                =>       infix_eq(t, t1)
 *   t ne t1                =>       infix_ne(t, t1)
 *   t.clone                =>       infix_clone(t)
 *   t.notify               =>       infix_notify(t)
 *   t.notifyAll            =>       infix_notifyAll(t)
 *   t.synchronized[T](t1)  =>       infix_synchronized(t, t1)
 *   t.wait                 =>       infix_wait(t)
 *   t.wait(l)              =>       infix_wait(t, l)
 *   t.wait(t1, l)          =>       infix_wait(t, t1, l)
 *   t.finalize()           =>       infix_finalize(t)
 * }}}
 *
 * @todo
 * {{{
 *   try b catch c          =>       __tryCatch(b, c, f)
 *   throw e                =>       __throw(e)
 *   case class C { ... }   =>       ???
 *   Nothing                =>       ???
 *   Null                   =>       ???
 * }}}
 */
trait LanguageVirtualization extends MacroModule with TransformationUtils with DataDefs {
  import c.universe._

  def virtualize(t: Tree): (List[Tree], Seq[DSLFeature]) = VirtualizationTransformer(t)

  object VirtualizationTransformer {
    def apply(tree: Tree) = {
      val t = new VirtualizationTransformer().apply(tree)
      log("(edu.stanford.cs.dawn, Seq[Features]): " + t, 2)
      t
    }
  }

  private class VirtualizationTransformer extends Transformer {
    val lifted = mutable.ArrayBuffer[DSLFeature]()

    def liftFeature(receiver: Option[Tree], nme: String, args: List[Tree], targs: List[Tree] = Nil, trans: Tree => Tree = transform): Tree = {
      lifted += DSLFeature(receiver.map(_.tpe), nme, targs, List(args.map(_.tpe)))
      log(show(method(receiver.map(trans), nme, List(args.map(trans)), targs)), 3)
      method(receiver.map(trans), nme, List(args.map(trans)), targs)
    }

    // this is used for scopes:
    // def OptiQL[R](b: => R) = new Scope[OptiQLLower, OptiQLLowerRunner[R], R](b)
    // syntax has to correspond exactly!
    // this map collects dsls at definition site so it can access them at call site
    // TODO: this is not a safe feature which should rather be removed as it is not longer used in Delite
    val dslScopes:scala.collection.mutable.HashMap[String, (Tree, Tree, Tree)] = new scala.collection.mutable.HashMap()

    // Call for transforming Blocks. Allows expanding a single statement to multiple statements within a given scope
    private def transformStm(tree: Tree): List[Tree] = tree match {
      // TODO: Name mangling is nice and elegant, but becomes an issue when we assume we've mangled
      // names which actually haven't been changed. Would need to come up with a solution to check to see
      // if a name's been mangled that also respects scoping (and potentially incremental compilation?)
      /*
      case ValDef(mods, sym, tpt, rhs) if mods.hasFlag(Flag.MUTABLE) =>
        // Mangle Var name to make readVar calls happen explicitly
        val s = TermName(sym+"$v")
        // leaving it a var makes it easier to revert when custom __newVar isn't supplied
        val v = ValDef(mods, s, tpt, liftFeature(None, "__newVar", List(rhs)))
        val d = DefDef(mods, sym, Nil, Nil, tpt, liftFeature(None, "__readVar", List(Ident(s))))

        List(v, d)
      */
      case v@ValDef(mods, term@TermName(name), _, _) if !mods.hasFlag(Flag.PARAMACCESSOR) =>
        val vdef = transform(v)
        val n = Literal(Constant(name.toString))
        val regv = Apply(Ident(TermName("__valDef")), List(Ident(term), Literal(Constant(name))))

        List(vdef, regv)

      case _ => List(transform(tree))
    }


    override def transform(tree: Tree): Tree = atPos(tree.pos) {
      tree match {
        /* Attempt to virtualize vars in both class bodies and blocks **/
        case Template(parents, selfType, bodyList) =>
          val body = bodyList.flatMap(transformStm)

          Template(parents, selfType, body)

        case Block(stms, ret) =>
          val stms2 = stms.flatMap(transformStm) ++ transformStm(ret)

          Block(stms2.dropRight(1), stms2.last)


        /* Variables */
        case ValDef(mods, sym, tpt, rhs) if mods.hasFlag(Flag.MUTABLE) =>
          // TODO: What about case like:
          // var x: Option[Int] = None
          // x = Some(3)
          // __newVar: Var[Option[Int]]
          ValDef(mods, sym, tpt, liftFeature(None, "__newVar", List(rhs)))

        case Assign(lhs, rhs) =>
          // liftFeature(None, "__assign", List(Ident(lhs+"$v"), rhs))   // Name mangling version

          liftFeature(None, "__assign", List(lhs, rhs))

        // Don't rewrite +=, -=, *=, and /=. This restricts the return value to Unit
        // in the case where the compiler/DSL author chooses to use implicit classes rather
        // than infix_ methods
        /*
        case Apply(Select(qualifier, TermName("$plus$eq")), List(arg)) =>     // x += y
          liftFeature(None, "infix_$plus$eq", List(qualifier, arg))

        case Apply(Select(qualifier, TermName("$minus$eq")), List(arg)) =>    // x -= y
          liftFeature(None, "infix_$minus$eq", List(qualifier, arg))

        case Apply(Select(qualifier, TermName("$times$eq")), List(arg)) =>    // x *= y
          liftFeature(None, "infix_$times$eq", List(qualifier, arg))

        case Apply(Select(qualifier, TermName("$div$eq")), List(arg)) =>      // x /= y
          liftFeature(None, "infix_$div$eq", List(qualifier, arg))
        */


        /* Control structures (keywords) */

        case t @ If(cond, thenBr, elseBr) =>
          liftFeature(None, "__ifThenElse", List(cond, thenBr, elseBr))

        case Return(e) =>
          liftFeature(None, "__return", List(e))

        case LabelDef(sym, List(), If(cond, Block(body :: Nil, Apply(Ident(label),
          List())), Literal(Constant(())))) if label == sym => // while(){}
          liftFeature(None, "__whileDo", List(cond, body))

        case LabelDef(sym, List(), Block(body :: Nil, If(cond, Apply(Ident(label),
          List()), Literal(Constant(()))))) if label == sym => // do while(){}
          liftFeature(None, "__doWhile", List(cond, body))

        case Try(block, catches, finalizer) => {
          c.warning(tree.pos, "virtualization of try/catch expressions is not supported.")
          super.transform(tree)
        }

        case Throw(expr) => {
          c.warning(tree.pos, "virtualization of throw expressions is not supported.")
          super.transform(tree)
        }

        /* Special case + for String literals */

        // only virtualize `+` to `infix_+` if lhs is a String *literal* (we can't look at types!)
        // NOFIX: this pattern does not work for: `string + unstaged + staged`
        case Apply(Select(qual @ Literal(Constant(s: String)), TermName("$plus")), List(arg)) =>
          liftFeature(None, "infix_$plus", List(qual, arg))

        /* Methods defined on Any/AnyRef with arguments */

        case Apply(Select(qualifier, TermName("$eq$eq")), List(arg)) =>
          liftFeature(None, "infix_$eq$eq", List(qualifier, arg))

        case Apply(Select(qualifier, TermName("$bang$eq")), List(arg)) =>
          liftFeature(None, "infix_$bang$eq", List(qualifier, arg))

        case Apply(Select(qualifier, TermName("equals")), List(arg)) =>
          liftFeature(None, "infix_equals", List(qualifier, arg))

        case Apply(Select(qualifier, TermName("eq")), List(arg)) =>
          liftFeature(None, "infix_eq", List(qualifier, arg))

        case Apply(Select(qualifier, TermName("ne")), List(arg)) =>
          liftFeature(None, "infix_ne", List(qualifier, arg))

        case Apply(Select(qualifier, TermName("wait")), List(arg)) =>
          liftFeature(None, "infix_wait", List(qualifier, arg))

        case Apply(Select(qualifier, TermName("wait")), List(arg0, arg1)) =>
          liftFeature(None, "infix_wait", List(qualifier, arg0, arg1))

        case Apply(Select(qualifier, TermName("synchronized")), List(arg)) =>
          liftFeature(None, "infix_synchronized", List(qualifier, arg))

        case Apply(TypeApply(Select(qualifier, TermName("synchronized")), targs), List(arg)) =>
          liftFeature(None, "infix_synchronized", List(qualifier, arg), targs)

        case TypeApply(Select(qualifier, TermName("asInstanceOf")), targs) =>
          liftFeature(None, "infix_asInstanceOf", List(qualifier), targs)

        case TypeApply(Select(qualifier, TermName("isInstanceOf")), targs) =>
          liftFeature(None, "infix_isInstanceOf", List(qualifier), targs)

        /* Methods defined on Any/AnyRef without arguments */

        // For 0-arg methods we get a different tree depending on if the user writes empty parens 'x.clone()' or no parens 'x.clone'
        // We always match on the empty parens version first

        case Apply(Select(qualifier, TermName("toString")), List()) =>
          liftFeature(None, "infix_toString", List(qualifier))

        case Select(qualifier, TermName("toString")) =>
          liftFeature(None, "infix_toString", List(qualifier))

        case Apply(Select(qualifier, TermName("$hash$hash")), List()) =>
          liftFeature(None, "infix_$hash$hash", List(qualifier))

        case Select(qualifier, TermName("$hash$hash")) =>
          liftFeature(None, "infix_$hash$hash", List(qualifier))

        case Apply(Select(qualifier, TermName("hashCode")), List()) =>
          liftFeature(None, "infix_hashCode", List(qualifier))

        case Select(qualifier, TermName("hashCode")) =>
          liftFeature(None, "infix_hashCode", List(qualifier))

        case Apply(Select(qualifier, TermName("clone")), List()) =>
          liftFeature(None, "infix_clone", List(qualifier))

        case Select(qualifier, TermName("clone")) =>
          liftFeature(None, "infix_clone", List(qualifier))

        case Apply(Select(qualifier, TermName("notify")), List()) =>
          liftFeature(None, "infix_notify", List(qualifier))

        case Select(qualifier, TermName("notify")) =>
          liftFeature(None, "infix_notify", List(qualifier))

        case Apply(Select(qualifier, TermName("notifyAll")), List()) =>
          liftFeature(None, "infix_notifyAll", List(qualifier))

        case Select(qualifier, TermName("notifyAll")) =>
          liftFeature(None, "infix_notifyAll", List(qualifier))

        case Apply(Select(qualifier, TermName("wait")), List()) =>
          liftFeature(None, "infix_wait", List(qualifier))

        case Select(qualifier, TermName("wait")) =>
          liftFeature(None, "infix_wait", List(qualifier))

        case Apply(Select(qualifier, TermName("finalize")), List()) =>
          liftFeature(None, "infix_finalize", List(qualifier))

        case Select(qualifier, TermName("finalize")) =>
          liftFeature(None, "infix_finalize", List(qualifier))

        case Apply(Select(qualifier, TermName("getClass")), List()) =>
          liftFeature(None, "infix_getClass", List(qualifier))

        case Select(qualifier, TermName("getClass")) =>
          liftFeature(None, "infix_getClass", List(qualifier))

        // HACK: Transform into if-then-else for now. Better way?
        /*case Match(selector, cases) => tree

          def transformCase(cases: Seq[Tree]): Tree = {
            def transformSingleCase(cas: Tree, els: => Tree): Tree = cas match {
              // case name: Type if guard => body
              case CaseDef(Bind(name, Typed(typeTree)), EmptyTree, body) => If(q"$selector.isInstanceOf[$typeTree]", body, els)
              case CaseDef(Bind(name, Typed(typeTree)), guard, body) => If(q"$selector.isInstanceOf[$typeTree] && $guard", body, els)

              // case Unapply(args...) if guard => body
              case CaseDef(Apply(unapply, List(args)), _, _) => c.abort(c.enclosingPosition, "Virtualization of unapply methods is currently unsupported")
              case CaseDef(Bind(name, Apply(unapply, List(args))), _, _) => c.abort(c.enclosingPosition, "Virtualization of unapply methods is currently unsupported")

              // case name @ pattern if guard => body
              case CaseDef(Bind(name, pattern), EmptyTree, body) => If(q"$selector == $pattern", body, els)
              case CaseDef(Bind(name, pattern), guard, body) => If(q"$selector == $pattern && $guard", body, els)

              // case pattern if guard => body
              case CaseDef(pattern, EmptyTree, body) => If(q"$selector == $pattern", body, els)
              case CaseDef(pattern, guard, body) => If(q"$selector == $pattern && $guard", body, els)
            }

            if (cases.length == 2) (cases(0),cases(1)) match {
              case (c0, CaseDef(Ident(termNames.WILDCARD), EmptyTree, body)) => transformSingleCase(c0, body)
              case _ => transformSingleCase(cases.head, transformCase(cases.tail))
            }
            else if (cases.length > 2) {
              transformSingleCase(cases.head, transformCase(cases.tail))
            }
            else {
              transformSingleCase(cases.head, q"()")
            }
          }*/

        /* Unsupported */
        case ClassDef(mods, name, tpt, body) if mods.hasFlag(Flag.CASE) =>
          // sstucki: there are issues with the ordering of
          // virtualization and expansion of case classes (i.e. some
          // of the expanded code might be virtualized even though it
          // should not be and vice-versa).  So until we have decided
          // how proper virtualization of case classes should be done,
          // any attempt to do so should fail.
          // TR: not 100% sure what the issue is (although i vaguely
          // remember that we had issues in Scala-virtualized with
          // auto-generated case class equality methods using virtualized
          // equality where it shouldn't). For the moment it seems like
          // just treating case classes as regular classes works fine.
          c.warning(tree.pos, "virtualization of case classes is not fully supported.")
          super.transform(tree) //don't virtualize the case class definition but virtualize its body

        /* Scopes */

        // this is a helper `method` for DSL generation in Forge
        // It avoid some boilerplate code but it not that principled:
        // USAGE:
        // magic() //have to make an explicit call to 'execute' side effects
        // @virtualize //values could not be annotated...
        // def magic[R]() = withTpee(Community){ //rhs pattern is matched by virtualized

        case Apply(Apply(Ident(TermName("withTpee")), List(termName)), body) =>
          val objName = TermName(termName.toString()+"Object")
          //TODO (macrotrans) val bodyTransform = transform(body)
          val x = q"""
            _tpeScopeBox = $termName
            abstract class DSLprog extends TpeScope {
              def apply = $body //Transform
            }
            class DSLrun extends DSLprog with TpeScopeRunner
            ((new DSLrun): TpeScope with TpeScopeRunner).result
          """
          c.warning(tree.pos, s"WITHTPE SCOPE GENERATED for term: "+termName.toString)
          x

        /**
        * little Hack for Delite Scope object:
        * inject specific Code for DSL to make it easier to use a DSL
        *
        * given:
        * `def OptiML[R](b: => R) = new Scope[OptiML, OptiMLExp, R](b)`
        *
        * generate:
        * `OptiML { body }` is expanded to:
        *
        * trait DSLprog$ extends OptiML {def apply = body}
        * (new DSLprog$ with OptiMLExp): OptiML with OptiMLExp
        *
        * other use case: (with type parameters)
        * new Scope[TpeScope, TpeScopeRunner[R], R](block)
        * Apply(Select(New(AppliedTypeTree(Ident(TypeName("Scope")), List(id1, AppliedTypeTree(Ident(TypeName("TpeScopeRunner")), List(Ident(TypeName("R")))), id3))), termNames.CONSTRUCTOR), List(Ident(TermName("block"))))
        *
        */
        //def apply[R](b: => R) = new Scope[OptiWranglerLower, OptiWranglerLowerRunner[R], R](b)")
        //DefDef(Modifiers(), TermName("apply"), List(TypeDef(Modifiers(PARAM), TypeName("R"), List(), TypeBoundsTree(EmptyTree, EmptyTree))), List(List(ValDef(Modifiers(PARAM | BYNAMEPARAM/CAPTURED/COVARIANT), TermName("b"), AppliedTypeTree(Select(Select(Ident(termNames.ROOTPKG), TermName("scala")), TypeName("")), List(Ident(TypeName("R")))), EmptyTree))), TypeTree(), Apply(Select(New(AppliedTypeTree(Ident(TypeName("Scope")), List(Ident(TypeName("OptiWranglerLower")), AppliedTypeTree(Ident(TypeName("OptiWranglerLowerRunner")), List(Ident(TypeName("R")))), Ident(TypeName("R"))))), termNames.CONSTRUCTOR), List(Ident(TermName("b")))))
        case DefDef(_, dslName, _, _, _, Apply(Select(New(AppliedTypeTree(Ident(TypeName("Scope")), List(identDSL, AppliedTypeTree(identDSLRunner, _), typeParam))), _), _)) =>
          dslScopes += Tuple2(dslName.toString, (identDSL, identDSLRunner, typeParam))
          q""

        case Apply(identTermName, List(body)) if dslScopes.contains(identTermName.toString) =>
          val (dsl, runner, typ) = dslScopes(identTermName.toString())
          val ret = q"""{
            trait DSLprog extends $dsl {def apply:$typ = $body }
            val cl = (new DSLprog with $runner[$typ]): $dsl with $runner[$typ]
            cl.apply
          }"""
          ret

        // this only works for: `new Scope[A, B, C]()` not for: `new Scope[A, B, C]{}` => creates anonymous class and stuff
        case Apply(Select(New(AppliedTypeTree(Ident(TypeName("Scope")), List(tn1, tn2, tnR))), termnames), List(body)) =>
          //TODO(trans): val bodyTranform = transform(body)
          val ret = q"""{
            trait DSLprog extends $tn1 {def apply = $body }
            val cl = (new DSLprog with $tn2): $tn1 with $tn2
            cl.apply
          }"""
          c.warning(tree.pos, s"SCOPE GENERATED: \n RAW: "+showRaw(ret)+"\n CODE: "+showCode(ret))
          ret


        // Argon-specific hack for changing T:Type to T<:MetaAny[T]:Type
        case DefDef(mods,name,tparams,paramss,retTpe,body) =>
          // HACK: Change return type from IR.Void to scala.Unit
          // This is to allow both
          //   def method() { } and
          //   def method(): Unit = { } syntax
          // Since lifting from scala.Unit to IR.Void is supported in argon,
          // but not IR.Unit to scala.Unit, this is the only sane thing to do
          val modifiedRetTpe = retTpe match {
            case Ident(TypeName("Unit")) =>
              Select(Ident(TermName("scala")),TypeName("Unit"))

            case tp => tp
          }

          if (paramss.nonEmpty) {
            val metaTypes = paramss.last.collect{
              case ValDef(ms,_,AppliedTypeTree(Ident(TypeName("Meta")),List(typeTree)),rhs) if ms.hasFlag(Flag.IMPLICIT) => typeTree
              case ValDef(ms,_,AppliedTypeTree(Ident(TypeName("Type")),List(typeTree)),rhs) if ms.hasFlag(Flag.IMPLICIT) => typeTree
            }

            object TypedTree {
              def unapply(x: Tree): Option[String] = x match {
                case Ident(TypeName(typeName)) => Some(typeName)
                case AppliedTypeTree(TypedTree(typeName),args) => Some(typeName)
                case ExistentialType(TypedTree(typeName),args) => Some(typeName)
              }
            }
            val metaTypeNames = metaTypes.map{case TypedTree(typeName) => typeName }

            val newTParams = tparams.map{
              case TypeDef(ms,TypeName(typeName),targs,TypeBoundsTree(child,EmptyTree)) if metaTypeNames.contains(typeName) =>
                val i = metaTypeNames.indexOf(typeName)
                val superBound = AppliedTypeTree(Ident(TypeName("MetaAny")), List(metaTypes(i)))
                TypeDef(ms,TypeName(typeName),targs,TypeBoundsTree(child,superBound))

              case tp => tp
            }

            super.transform( DefDef(mods,name,newTParams,paramss,modifiedRetTpe,body) )

          }
          else super.transform(DefDef(mods,name,tparams,paramss,modifiedRetTpe,body))

        case _ =>
          super.transform(tree)
      }
    }
    def apply(tree: c.universe.Tree): (List[Tree], Seq[DSLFeature]) =
      (transformStm(tree), lifted.toSeq)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy