For example as the following code:
object Test extends App
{
trait Class
{
val f1: Int
}
val c = new Class {
val f1: Int = 1
val f2: String = "Class"
}
println(c.f1)
println(c.f2)
}
I look into the bytecode with a decompiler, and notice that the compile generate a java interface 'Test.Class' as pseudo code:
trait Class
{
val f1: Int
}
and a class 'Test$$anon$1' implemeting 'Test.Class', pseudo code as:
class Test$$anon$1 extends Class
{
val f1: Int = 1
val f2: String = "Class"
}
and then the compiler initiaize the variable 'c' as:
c = new Test$$anon$1()
then calls the member 'f1' as normal invocation:
println(c.f1)
but it calls 'f2' using reflection:
println(reflMethod(c, f2))
Here, since the definition of the anonymous class 'Test$$anon$1' is visible in the same scope, is it possible to use macro to change the generated code to invoke 'f2' as normal field avoiding reflection?
I just want to change the invocation code in the same scope, not want to change the reflection code across scopes e.g. structual-typing instance as argument in function call. So I think it is possible in theory. But I am not familiar with scala macro, suggestions and code examples are appreciated. Thanks!
Macros (more precisely, macro annotations because def macros are irrelevant to this task) are not enough. You want to rewrite not class (trait, object) or its parameter or member but local expressions. You can do this either with compiler plugin (see also) at compile time or with Scalameta code generation before compile time.
If you choose Scalameta then actually you want to rewrite your expressions semantically rather than syntactically because you want to go from local expression
new Class...
to the definitiontrait Class...
and check whether there are proper members there. So you need Scalameta + SemanticDB. More convenient is to use Scalameta + SemanticDB with Scalafix (see also section for users).You can create your own rewriting rule. Then you can use it either for rewriting your code in-place or for code generation (see below).
rules/src/main/scala/MyRule.scala
in/src/main/scala/Test.scala
out/target/scala-2.13/src_managed/main/scala/Test.scala (after
sbt out/compile
)build.sbt
project/plugins.sbt
Other examples:
https://github.com/olafurpg/scalafix-codegen
https://github.com/DmytroMitin/scalafix-codegen
https://github.com/DmytroMitin/scalameta-demo
Scala conditional compilation
Macro annotation to override toString of Scala function
How to merge multiple imports in scala?