package scala.tools.nsc package transform import scala.tools.nsc.symtab.SymbolTable import scala.reflect import collection.mutable.HashMap /** Functions to reify (and un-reify) symbols, types, and trees. * These can be used with only a symbol table; they do not * need a full compiler. * * @author Gilles Dubochet, Lex Spoon */ trait Reifiers { val symbols: SymbolTable import symbols._ private def mkGlobalSymbol(fullname: String, sym: Symbol): reflect.Symbol = if (sym.isClass) reflect.Class(fullname) else if (sym.isType) reflect.TypeField(fullname, reify(sym.info)) else if (sym.isMethod) reflect.Method(fullname, reify(sym.info)) else reflect.Field(fullname, reify(sym.info)); def reify(sym: Symbol): reflect.Symbol = { if (sym.isRoot || sym.isRootPackage || sym.isEmptyPackageClass || sym.isEmptyPackage) reflect.RootSymbol else if (sym.owner.isTerm) reflect.NoSymbol else reify(sym.owner) match { case reflect.NoSymbol => reflect.NoSymbol; case reflect.RootSymbol => mkGlobalSymbol(sym.name.toString(), sym) case reflect.Class(ownername) => mkGlobalSymbol(ownername + "." + sym.name, sym) case _ => reflect.NoSymbol } } var _log_reify_type_ = false def reify(tp: Type): reflect.Type = tp match { case ErrorType => reflect.NoType case WildcardType => if (_log_reify_type_) println("cannot handle WildcardType") reflect.NoType case NoType => reflect.NoType case NoPrefix => reflect.NoType case ThisType(sym) => val rsym = reify(sym) reflect.ThisType(rsym) case SingleType(pre, sym) => reflect.SingleType(reify(pre), reify(sym)) case ConstantType(value) => reify(value.tpe) case TypeRef(pre, sym, args) => val rpre = reify(pre) val rsym = reify(sym) val rargs = args map reify val beforeArgs = reflect.PrefixedType(rpre, rsym) if (rargs.isEmpty) beforeArgs else if (rpre == reflect.NoType || rsym == reflect.NoSymbol) beforeArgs else reflect.AppliedType(beforeArgs, rargs) case TypeBounds(lo, hi) => reflect.TypeBounds(reify(lo), reify(hi)) case RefinedType(parents, defs) => if (_log_reify_type_) println("cannot handle RefinedType "+tp); reflect.NoType case ClassInfoType(parents, defs, clazz) => if (_log_reify_type_) println("cannot handle ClassInfoType "+tp); reflect.NoType case MethodType(params, result) => reflect.MethodType(params.map(reify), reify(result)) case PolyType(tparams, result) => val boundss = for { param <- tparams TypeBounds(lo,hi) = param.info.bounds } yield (reify(lo), reify(hi)) reflect.PolyType( tparams.map(reify), boundss, reify(result)) //todo: treat ExistentialType case AnnotatedType(annots, tp, _) => reify(tp) case _ => println("could not reify: " + tp) reflect.NoType } /** This is woefully incomplete. It is barely enough * to process the types of Constant's . */ def unreify(tpe: reflect.Type): Type = tpe match { case reflect.NoPrefix => NoPrefix case reflect.NoType => NoType case reflect.NamedType(fullname) => //NamedType(fullname) println("NamedType: " + fullname) NoType case reflect.PrefixedType(_, reflect.Class("scala.Array")) => definitions.ArrayClass.tpe case reflect.PrefixedType(_, reflect.Class("java.lang.String")) => definitions.StringClass.tpe case reflect.PrefixedType(_, reflect.Class("scala.Unit")) => definitions.UnitClass.tpe case reflect.PrefixedType(_, reflect.Class("scala.Boolean")) => definitions.BooleanClass.tpe case reflect.PrefixedType(_, reflect.Class("scala.Byte")) => definitions.ByteClass.tpe case reflect.PrefixedType(_, reflect.Class("scala.Short")) => definitions.ShortClass.tpe case reflect.PrefixedType(_, reflect.Class("scala.Int")) => definitions.IntClass.tpe case reflect.PrefixedType(_, reflect.Class("scala.Long")) => definitions.LongClass.tpe case reflect.PrefixedType(_, reflect.Class("scala.Float")) => definitions.FloatClass.tpe case reflect.PrefixedType(_, reflect.Class("scala.Double")) => definitions.DoubleClass.tpe case reflect.PrefixedType(pre, sym) => NoType case reflect.SingleType(pre, sym) => SingleType(unreify(pre), unreify(sym)) case reflect.ThisType(clazz) => ThisType(unreify(clazz)) case reflect.AppliedType(tpe, args) => val untpe = unreify(tpe) if (untpe == NoType) NoType else appliedType(untpe, args.map(unreify)) case reflect.TypeBounds(lo, hi) => TypeBounds(unreify(lo), unreify(hi)) case reflect.MethodType(params, restpe) => MethodType(params.map(unreify), unreify(restpe)) case reflect.PolyType(typeParams, typeBounds, resultType) => PolyType(typeParams.map(unreify), unreify(resultType)) //todo: treat ExistentialType case _ => NoType } /** This is woefully incomplete. It is barely enough * to process the types of Constant's . */ def unreify(symbol: reflect.Symbol): Symbol = symbol match { case reflect.Class(fullname) => fullname match { case "scala.Unit" => definitions.UnitClass case "scala.Boolean" => definitions.BooleanClass case "scala.Byte" => definitions.ByteClass case "scala.Short" => definitions.ShortClass case "scala.Int" => definitions.IntClass case "scala.Long" => definitions.LongClass case "scala.Float" => definitions.FloatClass case "scala.Double" => definitions.DoubleClass case "scala.Array" => definitions.ArrayClass case _ => NoSymbol } case _ => NoSymbol } case class FreeValue(tree: Tree) extends reflect.Tree class ReifyEnvironment extends HashMap[Symbol, reflect.Symbol] { var targets = new HashMap[String, Option[reflect.LabelSymbol]]() def addTarget(name: String, target: reflect.LabelSymbol): Unit = targets.update(name, Some(target)) def getTarget(name: String): Option[reflect.LabelSymbol] = targets.get(name) match { case None => targets.update(name, None) None //case Some(None) => None //bq:redundant case Some(tgt) => tgt } def hasAllTargets: Boolean = targets.iterator.map(_._2).forall { case Some(_) => true case None => false } override def update(sym: Symbol, rsym: reflect.Symbol) = super.update(sym,rsym) } class Reifier(env: ReifyEnvironment, currentOwner: reflect.Symbol) { def reify(tree: Tree): reflect.Tree = tree match { case Ident(_) => val rsym = reify(tree.symbol); //Console.println("LiftCode: seen ident") if (rsym == reflect.NoSymbol) { //Console.println(" free = "+tree) FreeValue(tree) } else { //Console.println(" rsym = "+rsym) reflect.Ident(rsym) } case Select(qual, _) => val rsym = reify(tree.symbol); if (rsym == reflect.NoSymbol) throw new TypeError("cannot reify symbol: " + tree.symbol) else reflect.Select(reify(qual), reify(tree.symbol)) case _ : StubTree => reflect.Literal(0) case Literal(constant) => reflect.Literal(constant.value) case Apply(name, args) if name.toString().startsWith("label$") => env.getTarget(name.toString()) match { case None => throw new TypeError("cannot reify tree (no forward jumps allowed): " + tree) case Some(label) => reflect.Goto(label) } case Apply(fun, args) => reflect.Apply(reify(fun), args map reify) case TypeApply(fun, args) => reflect.TypeApply(reify(fun), args map (_.tpe) map reify) case Function(vparams, body) => var env1 = env for (vparam <- vparams) { val local = reflect.LocalValue( currentOwner, vparam.symbol.name.toString(), reify(vparam.symbol.tpe)); env1.update(vparam.symbol, local); } reflect.Function(vparams map (_.symbol) map env1, new Reifier(env1, currentOwner).reify(body)) case tree@This(_) if tree.symbol.isModule => // there is no reflect node for a module's this, so // represent it as a selection of the module reify( Select(This(tree.symbol.owner), tree.symbol.name)) case This(_) => reflect.This(reify(tree.symbol)) case Block(stats, expr) => reflect.Block(stats.map(reify), reify(expr)) case New(clazz) if (clazz.isType) => val reifiedSymbol = reify(clazz.symbol) reflect.New(reflect.Ident(reifiedSymbol)) case New(clazz) => val reifiedClass = reify(clazz) reflect.New(reifiedClass) case Typed(t, _) => reify(t) case If(cond, thenp, elsep) => reflect.If(reify(cond), reify(thenp), reify(elsep)) case Assign(lhs, rhs) => reflect.Assign(reify(lhs), reify(rhs)) case LabelDef(name, Nil, body) => val sym = new reflect.LabelSymbol(name.toString()) env.addTarget(name.toString(), sym) val res = reflect.Target(sym, reify(body)) res case vd @ ValDef(mods, name, tpt, rhs) => val rtpe = reify(vd.tpe) // will return null, currently?! val sym = reflect.LocalValue(currentOwner, name.toString(), rtpe) env(vd.symbol) = sym // bq: despite Scala's scoping rules, this should work because references to vd.symbol were type checked. val rhs_ = reify(rhs) reflect.ValDef(sym, rhs_) case cd @ ClassDef(mods, name, tparams, impl) => if(!tparams.isEmpty) throw new TypeError("cannot handle polymorphic ClassDef ("+name+"): " + tparams) val rsym = reify(cd.symbol) val rimp = reify(impl) val rtpe = reify(impl.self.tpt.tpe) //todo: update reflect.ClassDef(rsym, rtpe, rimp.asInstanceOf[reflect.Template]) case tmpl @ Template(parents, self, body) => val rparents = for (p <- parents) yield { reify(p.tpe) } //todo: add self to reified templates reflect.Template(rparents, body.map(reify)) case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => if(!tparams.isEmpty) throw new TypeError("cannot handle polymorphic DefDef ("+name+"): " + tparams) val rsym = reify(dd.symbol) val rparss = vparamss map { x => x map (reify) } val rret = reify(tpt.tpe) val rrhs = reify(rhs) reflect.DefDef(rsym, rparss, rret, rrhs) case sp @ Super(qual: Name, mix: Name) => val rsym = reify(sp.symbol) reflect.Super(rsym) case _ => throw new TypeError("cannot reify tree ("+tree.getClass()+"): " + tree) } def reify(sym: Symbol): reflect.Symbol = env.get(sym) match { case Some(rsym) => rsym case None => Reifiers.this.reify(sym) } def reify(tpe: Type): reflect.Type = Reifiers.this.reify(tpe) } def reify(tree: Tree): reflect.Tree = new Reifier(new ReifyEnvironment(), reflect.NoSymbol).reify(tree) }