Skip to content

Instantly share code, notes, and snippets.

@gzoller
Created December 2, 2022 21:33
Show Gist options
  • Save gzoller/6cba7462895baa76fd1dc766aa65d68c to your computer and use it in GitHub Desktop.
Save gzoller/6cba7462895baa76fd1dc766aa65d68c to your computer and use it in GitHub Desktop.
Attempting to override a method in plugin
class MyPhase extends PluginPhase {
import tpd._
val phaseName = "overrider"
override val runsAfter = 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.of[StringBuilder],
TypeRepr.of[SJConfig],
),
_ => TypeRepr.typeConstructorOf(classOf[Unit]) // return type
),
Flags.Override, // NOTE: Setting override flag
Symbol.noSymbol
)
// 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)) =>
given Quotes = toJsonSymbol.asQuotes
Some({
quoted.Expr.ofList(List(
'{ println("Hello") },
'{ println("World") }
))
}.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
)
cd.asInstanceOf[dotty.tools.dotc.ast.tpd.Tree]
else
tree
else
tree
}
/*
Produces this error when plugin used to compile a class:
Exception in thread "sbt-bg-threads-1" java.lang.ClassFormatError: Duplicate method name "toJson" with signature "(Lscala.collection.mutable.StringBuilder;Lco.blocke.scala_reflection.SJConfig;)V" in class file com/foo/Person
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1013)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:524)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:427)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:421)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:712)
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:420)
at sbt.internal.ManagedClassLoader.findClass(ManagedClassLoader.java:102)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at com.foo.Person$.apply(Main.scala:13)
at com.foo.Main$package$.hello(Main.scala:35)
at com.foo.hello.main(Main.scala:27)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
...
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment