Last active
December 30, 2015 15:19
-
-
Save squito/7847796 to your computer and use it in GitHub Desktop.
experiments with reflection. goes with a blog post
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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))) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //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)) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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]) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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, ... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @FillDefsWithReflection[MyTrait] class Blah |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment