Created
November 29, 2022 20:16
-
-
Save gzoller/2ec240a4ad60d6c11e6e78d8325b6131 to your computer and use it in GitHub Desktop.
Scala Compiler Plugin ClassDef Modificaiton
This file contains 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
class ReflectionWorker extends StandardPlugin { | |
val name: String = "reflectionWorker" | |
override val description: String = "heavy-lift reflection worker" | |
def init(options: List[String]): List[PluginPhase] = | |
new ReflectionWorkerPhase :: Nil | |
} | |
class ReflectionWorkerPhase extends PluginPhase { | |
import tpd._ | |
val phaseName = "reflectionWorker" | |
override val runsBefore = Set(Pickler.name) | |
override def transformTypeDef(tree: TypeDef)(implicit ctx: Context): Tree = | |
if tree.isClassDef && !tree.rhs.symbol.isStatic then // only look at classes & traits, not objects | |
// 0. Get a FreshContext so we can set the tree to this tree. (for '{} later) | |
implicit val fresh = ctx.fresh | |
fresh.setTree(tree) | |
QuotesCache.init(fresh) | |
implicit val quotes:Quotes = QuotesImpl.apply() // picks up fresh | |
import quotes.reflect.* | |
val s3ReflectionClassSymbol = getClassIfDefined("co.blocke.scala_reflection.Skip_Reflection").asInstanceOf[ClassSymbol] | |
if tree.symbol.getAnnotation(s3ReflectionClassSymbol).isDefined then | |
// 1. Set up method symbol, define parameters and return type | |
val toJsonSymbol = Symbol.newMethod( | |
Symbol.spliceOwner, | |
"toJson", | |
MethodType( | |
List("sb","config"))( // parameter list | |
_ => List( // types of the parameters | |
TypeRepr.typeConstructorOf(classOf[StringBuilder]), | |
TypeRepr.typeConstructorOf(classOf[SJConfig]) | |
), | |
_ => TypeRepr.typeConstructorOf(classOf[Unit]) // return type | |
) | |
) | |
// 2. Get our class' Symbol (for ownerhsip reassignment) | |
val classSymbol = tree.tpe.asInstanceOf[quotes.reflect.TypeRef].classSymbol.get | |
// 3. Define our method definition (DefDef) using our method symbol defined above | |
val toJsonMethodDef = DefDef( | |
toJsonSymbol, | |
{ | |
case List(List(sb: Term, config: Term)) => | |
Some('{ println("Greg Zoller") }.asTerm.changeOwner(toJsonSymbol)) | |
} | |
).changeOwner(classSymbol) | |
// 4. Add toJsonMethodDef to tree and return | |
val classDef = tree.asInstanceOf[ClassDef] | |
val cd = ClassDef.copy(classDef)( | |
name = classDef.name, | |
constr = classDef.constructor, | |
parents = classDef.parents, | |
selfOpt = classDef.self, | |
body = classDef.body :+ toJsonMethodDef | |
) | |
println(cd.body.map(s => println(">> "+s))) // show body to prove toJson was added | |
cd.asInstanceOf[dotty.tools.dotc.ast.tpd.Tree] | |
else | |
tree | |
else | |
tree | |
} | |
/* | |
Shows 2 functions: toJsonOG (defined in the original Person class) and toJson (generated). | |
Note different treatment of parameter types for some reason... | |
>> DefDef(toJsonOG,List(List(ValDef(sb,Ident(StringBuilder),EmptyTree), ValDef(c,Ident(SJConfig),EmptyTree))),Ident(Unit),Block(List(Apply(Select(Ident(sb),append),List(Literal(Constant({))))),Block(List(Apply(Select(Ident(sb),append),List(Literal(Constant(}))))),Literal(Constant(()))))) | |
>> DefDef(toJson,List(List(ValDef(sb,TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class mutable)),class StringBuilder)],EmptyTree), ValDef(config,TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala_reflection)),class SJConfig)],EmptyTree))),TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class Unit)],Inlined(Ident(ReflectionWorkerPhase),List(),Apply(Ident(println),List(Literal(Constant(Greg Zoller)))))) | |
Consuming class (using the plugin): | |
@Skip_Reflection | |
case class Person(name:String, age:Int) { | |
val greg: String = "hey" | |
def toJsonOG(sb: StringBuilder, c:SJConfig): Unit = | |
sb.append("{") | |
sb.append("}") | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment