Skip to content

Instantly share code, notes, and snippets.

@gkossakowski
Created August 11, 2011 11:19
Show Gist options
  • Save gkossakowski/1139415 to your computer and use it in GitHub Desktop.
Save gkossakowski/1139415 to your computer and use it in GitHub Desktop.
Scala Manifests without reflection.
package scala.tools.nsc
package backend.jribble
import scala.collection.mutable
import scala.tools.nsc.transform.{Transform, TypingTransformers}
/**
* Implements 'factorymanifests' compiler phase that provides alternative implementation of
* Manifests that use static factories for Array creation.
*
* Canonical Manifest implementation in Scala uses reflection for generic Array creation.
* In environment where reflection is not supported (like GWT) this approach for generic
* array handling is not feasible. This phase rewrites calls to ClassManifest.classType
* methods into FactoryManifest.classType ones. Also, we generate anonymous classes that
* implement FactoryManifest.Factory[T] trait for every concrete T <: AnyRef.
*
* This way we have implementation of Manifests that does not depend on reflection. The price
* we pay is increased number of classes needed to support generic arrays.
*
* It's worth noting that this phase should be run just after 'refchecks' phase (and for sure
* before flatten) so anonymous classes we generate are lifted to top-level ones. We want to
* run after 'refcheck' because it means we are running after pickler. It's desirable because
* we don't want pickling information for our anonymous classes to be generated.
*/
trait FactoryManifests extends Transform with TypingTransformers {
val global: Global
import global._
override val phaseName = "factorymanifests"
override protected def newTransformer(unit: CompilationUnit): Transformer =
new Trans(unit)
/**
* The main transformer of this phase.
*/
private class Trans(cunit: CompilationUnit) extends TypingTransformer(cunit) {
lazy val ClassManifestModule = definitions.getModule("scala.reflect.ClassManifest")
lazy val classTypeMethod = definitions.getMember(ClassManifestModule, "classType") suchThat {
//we want to match this one: def classType[T <: AnyRef](clazz: JClass[_]): ClassManifest[T]
_.tpe.params.size == 1
}
lazy val FactoryManifestModule = definitions.getModule("scala.reflect.FactoryManifest")
lazy val classTypeFactoryMethod = definitions.getMember(FactoryManifestModule, "classType") suchThat {
//we want to match this one: def classType[T <: AnyRef](clazz: JClass[_], factory: Factory[T]): ClassManifest[T]
_.tpe.params.size == 2
}
lazy val factoryTrait = definitions.getClass("scala.reflect.FactoryManifest.Factory")
override def transform(tree: Tree): Tree = tree match {
case tree@Apply(fun, List(c: Literal)) if fun.symbol == classTypeMethod =>
assert(c.value.tag == ClassTag)
val forTpe = {
val TypeApply(_, List(tp: TypeTree)) = fun
tp.tpe
}
val factoryExpr = mkFactoryClass(forTpe, currentOwner, 1)
localTyper.typedPos(tree.pos) {
gen.mkMethodCall(classTypeFactoryMethod, List(forTpe), List(c, factoryExpr))
}
case x => super.transform(x)
}
def mkFactoryClass(param: Type, owner: Symbol, nest: Int): Tree = {
import scala.reflect.internal.Flags._
val arrayTpe: Type = {
val at = definitions.ArrayClass.tpe
at.instantiateTypeParams(at.typeArgs.map(_.typeSymbol), List(param))
}
def mkFactoryTpe(t: Type): Type = appliedType(factoryTrait.tpe, List(t))
val factoryTpe = mkFactoryTpe(param)
val arrayFactoryTpe = mkFactoryTpe(arrayTpe)
def mkNewInstance(owner: Symbol): DefDef = {
val name = newTermName("newInstance")
val s = owner.newMethod(name)
val len = {
val ss = s.newValueParameter(NoPosition, "len") setInfo definitions.IntClass.tpe
ValDef(ss) setType ss.tpe
}
s setInfo MethodType(s newSyntheticValueParams List(len.tpe), arrayTpe)
val newArr = New(TypeTree(arrayTpe), List(List(Ident(len.symbol))))
DefDef(Modifiers(FINAL), name, Nil, List(List(len)), TypeTree(arrayTpe), newArr) setSymbol s
}
def mkforArrayOf(owner: Symbol): DefDef = {
val name = newTermName("forArrayOf")
val s = owner.newMethod(name)
s setInfo MethodType(Nil, arrayFactoryTpe)
val body = if (nest == 0)
gen.mkSysErrorCall("FactoryManifests support limited depth of generic Array nesting.") else
mkFactoryClass(arrayTpe, s, nest-1)
DefDef(Modifiers(FINAL), name, Nil, Nil, TypeTree(arrayFactoryTpe), body) setSymbol s
}
val anonClass = owner newAnonymousClass owner.pos setFlag (FINAL | SYNTHETIC)
val parents = List(definitions.ObjectClass.tpe, factoryTpe)
anonClass setInfo ClassInfoType(parents, new Scope, anonClass)
val members = List(mkNewInstance(anonClass), mkforArrayOf(anonClass))
members foreach { x => anonClass.info.decls enter x.symbol }
Block(ClassDef(anonClass, NoMods, List(Nil), List(Nil), members, owner.pos),
Typed(New(TypeTree(anonClass.tpe), List(List())),
TypeTree(factoryTpe)))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment