0

Is there Scala way for runtime generation of proxy classes? not DynamicProxy but runtime types that extend provided class/interface and pass all calls through provided callback.

Java world uses cglib/javassist for that, but what is the best way to proxy in Scala?

def makeProxy[T](interceptor: Interceptor, implicit baseClass: Manifest[T]): T

Google says Scala macros can be used for this but I am unsure how.

Ben Reich
  • 16,222
  • 2
  • 38
  • 59
Sergey Alaev
  • 3,851
  • 2
  • 20
  • 35
  • macroses are generating code in compile-time. Macros will generate your handler in compile-time; they act like compiler's plugin, so manipulating with AST instead of bytecode. When you're talking about cglib/javassist - it's not about JavaWorld - that's about JVM-World. You steel can manipulate Scala clases with them (especially prepared for Java). So, your question is too broad. You may really need runtime-reflection or not. And it's duplicating - http://stackoverflow.com/questions/17535971/is-there-scala-aware-high-level-byte-code-manipulation-tool-like-javassist – dk14 May 05 '15 at 20:23
  • Btw, runtime/compiletime is pretty relative here. You can modify compiled classes on your disk with tools, and you can [interpret](http://speaking-my-language.blogspot.com/2009/11/embedded-scala-interpreter.html) scala-macroses on-the-fly. Plenty of abilities and the best one depends on what you really want. Maybe you want just `DynamicProxy` - `ClassTag.runtimeClass` (Manifests are deprecated) will give you `Class[_]`, so you can simply create new instance and register proxy for it (and check the instance inside handler to choose proxify it or not). – dk14 May 05 '15 at 20:35

1 Answers1

2

Here is an example how (more-less) to do something like that with macro:

def testImpl[T : c.WeakTypeTag](c: Context): c.Expr[Any] = {
  import c.universe._
  val className = newTypeName(weakTypeTag[T].tpe.typeSymbol.name.toString) //not best way

  val m = weakTypeOf[T].declarations.iterator.toList.map(_.asMethod) //`declaration` takes only current; `members` also takes inherited
               .filter(m => !m.isConstructor && !m.isFinal).map { m => //all reflection info about method 
                     q"""override def  ${m.name} = 9""" //generating new method
  }

  c.Expr { q"""new $className {  ..$m } """}
} 
def t[T] = macro testImpl[T]

class Aaa{ def a = 7; def b = 8}

scala> t[Aaa].a
res39: Int = 9

scala> t[Aaa].b
res40: Int = 9

All such macro works only if overriden methods are not final as they can't change types (as it works in compile-time) - only create new and inherit. This example doesn't process classes with non-empty constructors and many other things. m here is instance of MethodSymbol and gives you full scala-style reflection about input class' method. You need only generate correct AST in response.

To read more about that:

Another solution would be:

scala> def getClasss[T: ClassTag] = classTag[T].runtimeClass
getClasss: [T](implicit evidence$1: scala.reflect.ClassTag[T])Class[_]

Using this instance you can apply any asm/cglib/javassist or even DynamicProxy to it.

dk14
  • 22,206
  • 4
  • 51
  • 88