Skip to content

Instantly share code, notes, and snippets.

@squito
Last active December 30, 2015 15:19
Show Gist options
  • Select an option

  • Save squito/7847796 to your computer and use it in GitHub Desktop.

Select an option

Save squito/7847796 to your computer and use it in GitHub Desktop.
experiments with reflection. goes with a blog post
//the annotation class has type parameter T
class FillDefsWithReflection[T] extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro FillDefsWithReflectionImpl.impl
}
object FillDefsWithReflectionImpl {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
//we need to do some pattern matching to pull out just the type of the trait
val targetTrait = c.prefix.tree match {
case Apply(Select(New(AppliedTypeTree(Ident(_), List(typ))), nme.CONSTRUCTOR), List()) => typ
}
//of course, 7 is not really an instance of our target trait -- but we
// don't care; we just want it to *type check* as our target trait,
// so we can pull out the type
val tpe = c.typeCheck(q"(7.asInstanceOf[$targetTrait])").tpe
...
}
}
/**
* this methods finds all the zero-arg methods of the argument, and calls
* them using reflection. It returns a map of the results
*/
def callAllZeroArgMethods[A:reflect.runtime.universe.TypeTag: reflect.ClassTag](a: A):
Map[reflect.runtime.universe.MethodSymbol, util.Try[Any]] = {
val zms = zeroArgMethodsOf[A](a)
//first get the runtime mirror ...
val cm = ru.runtimeMirror(ru.getClass.getClassLoader)
//then the instance mirror ...
val aMirror = cm.reflect(a)
// and now, for each of the methods ...
zms.map {zm =>
//call the method, wrapped in a Try
zm -> util.Try{aMirror.reflectMethod(zm)()}
}.toMap
}
import ru._ //not really sure why this is necessary ... but otherwise can't find ClassTags for ru.MethodSymbol
/**
* this method calls all zero-arg methods of two different methods, and compares
* the results. Mostly its normal scala-code to nicely format the results
*/
def diffZeroArgMethods(methodA: MethodSymbol, methodB: MethodSymbol) = {
def c(m: Map[MethodSymbol,util.Try[Any]]): Map[String,String] = {
m.map{case (k,v) => k.toString -> {v match {
case util.Success(s) => Option(s).toString
case util.Failure(f) => "<xxx>"
}}}
}
val as = c(callAllZeroArgMethods(methodA))
val bs = c(callAllZeroArgMethods(methodB))
val fmt = "%20s|%40s%40s"
println(fmt.format("", methodA, methodB))
println("-" * 100)
as.filter{case(k,v) => bs(k) != v}.foreach{ case(method,aVal) =>
println(fmt.format(method, aVal, bs(method)))
}
}
scala> diffZeroArgMethods(mX,mY)
| method x method y
----------------------------------------------------------------------------------------------------
method returnType| Some(scala.Int) Some(scala.Float)
method fullName| Some($line15.$read.$iw.$iw.$iw.$iw.A.x) Some($line15.$read.$iw.$iw.$iw.$iw.A.y)
method asMethod| Some(method x) Some(method y)
method asTerm| Some(method x) Some(method y)
method name| Some(x) Some(y)
method typeSignature| Some(=> scala.Int) Some(=> scala.Float)
method alternatives| Some(List(method x)) Some(List(method y))
//first we define our trait
trait PersonalInfo {
def height: Float
def weight: Float
def phoneNumber: Int
def birthYear: Int
}
//now we can create a concrete class that knows how to read all
// the fields in a ByteBuffer
@ByteBufferBacked[PersonalInfo]
class PersonalInfoImpl(val bb: ByteBuffer)
//and we can also make a mutable version, which includes setters
@MutableByteBufferBacked[PersonalInfo]
class MutablePersonalInfoImpl(val bb: ByteBuffer)
//the classes now just behave like normal implementation of the trait:
def doStuff() {
val bb = new ByteBuffer(16)
val p = new MutablePersonalInfoImpl(bb)
//these setters will store data in the bytebuffer ...
p.height = 5.8f
p.weight = 178
...
val p2 = new PersonalInfoImpl(bb)
//we can read data from the bytebuffer with normal calls to the getters
val bmi = p2.weight * 703 / (math.pow(p2.height * 12,2))
}
def isDefined(method: u.MethodSymbol): Boolean = {
//from http://stackoverflow.com/questions/16792824/test-whether-a-method-is-defined
!isDeferred(method)
}
def isDeferred(sym: u.MethodSymbol) = {
sym.asInstanceOf[scala.reflect.internal.Symbols#Symbol].
hasFlag(scala.reflect.internal.Flags.DEFERRED)
}
scala> val mX = targetMethods(typ).find{_.name.toString == "x"}.head
mX: reflect.runtime.universe.MethodSymbol = method x
scala> val mY = targetMethods(typ).find{_.name.toString == "y"}.head
mY: reflect.runtime.universe.MethodSymbol = method y
scala> mX.
NameType accessed allOverriddenSymbols alternatives annotations asClass
asFreeTerm asFreeType asInstanceOf asMethod asModule asTerm
asType associatedFile companionSymbol filter fullName getter
isAbstractOverride isAccessor isByNameParam isCaseAccessor isClass isConstructor
isErroneous isFinal isFreeTerm isFreeType isGetter isImplementationArtifact
isImplicit isInstanceOf isJava isLazy isLocal isMacro
isMethod isModule isModuleClass isOverloaded isOverride isPackage
isPackageClass isParamAccessor isParamWithDefault isParameter isPrimaryConstructor isPrivate
isProtected isPublic isSetter isSpecialized isStable isStatic
isSynthetic isTerm isType isVal isVar isVarargs
map name newClassSymbol newMethodSymbol newModuleAndClassSymbol newTermSymbol
newTypeSymbol orElse owner paramss privateWithin returnType
setter suchThat toString typeParams typeSignature typeSignatureIn
def zeroArgMethods(typ: ru.Type) = {
typ.members.collect{
case m if m.isMethod => m.asMethod
}.filter{_.paramss.isEmpty}
}
def targetMethods(typ: ru.Type) = {
zeroArgMethods(typ).filter{m =>
val rt = m.returnType
(rt =:= ru.typeOf[Int]) || (rt =:= ru.typeOf[Float])
}
}
scala> zeroArgMethods(typ)
res8: Iterable[reflect.runtime.universe.MethodSymbol] =
List(method y, method asInstanceOf, method isInstanceOf, method x)
scala> targetMethods(typ)
res9: Iterable[reflect.runtime.universe.MethodSymbol] =
List(method y, method x)
scala> trait A {
| def x: Int
| def y = 7.0f
| }
defined trait A
scala> val typ = ru.typeOf[A]
typ: reflect.runtime.universe.Type = A
scala> typ.members
res0: reflect.runtime.universe.MemberScope =
Scopes(method y, method $init$, method $asInstanceOf, method $isInstanceOf, ...
scala> typ.members.filter{_.isMethod}.map{_.asMethod}
res1: Iterable[reflect.runtime.universe.MethodSymbol] =
List(method y, method $init$, method $asInstanceOf, method $isInstanceOf, ...
@FillDefsWithReflection[MyTrait] class Blah
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment