I have several objects with different methods (without any traits/interfaces) and I'd like to intercept those methods in order to add them some kind of a debug feature. I think I can achieve that with macros in Scala 2.11.12. Macros seem to be quite powerful but not trivial at all.
The methods that I want to get intercepted can call among them, that's why I can't use a simple Proxy approach. I've also tried using AspectJ, but don't want to add any dependencies to my project if I can help it.
This is what I'm trying, but I don't know how to call the methods I'm trying to intercept.
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object MacroProxy {
def createProxy[T](original: T): T = macro createProxyImpl[T]
def createProxyImpl[T: c.WeakTypeTag](c: whitebox.Context)(original: c.Expr[T]): c.Expr[T] = {
import c.universe._
val className = original.getClass.getSimpleName
val m = weakTypeOf[T].members.iterator.toList
.filter(m => m.isMethod).map(_.asMethod).map { m =>
q"""override def ${m.name} = $m"""
}
c.Expr {
q"""new $className { ..$m } """
}
}
}
I'm getting this error
missing argument list for method mymethod in class MyClass Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing
mymethod _
ormymethod(_)
instead ofmymethod
.
Several things:
class Foo {
def a(): Int = ...
def b(a: String): Double = ...
}
new Foo {
override def a = original.a
override def b = original.b
}
You basically skipped on argument lists and compiler thinks you are trying to turn methods into functions through eta expansion (which wouldn't let you override). You'd have to do something like:
val tpe = weakTypeOf[T]
tpe.members.iterator.toList
.filter(m => m.isMethod).map(_.asMethod).map { m =>
val name = m.name
val params = m.typeSignatureIn(tpe).paramLists
val arguments = params.map(_.map(_.name.toTermName))
q"""override def $name($...params) = $original.$name(...$arguments)"""
}
Though it would be just the beginning:
new $className { ... }
you have to check if class doesn't require some constructor with parameters..
(to expand sequence of definitions/parameters/trees) and ...
(to expand sequence of sequences, which appear with every parameter list)Basically, what you are trying to do is doable, but unless you want to support only most basic cases, this would be much more work than a single afternoon.