From 53a0e803f03fe715c214c01c03f82baa19d7f2c2 Mon Sep 17 00:00:00 2001 From: Christopher Vogt Date: Sat, 14 Apr 2012 00:57:09 +0200 Subject: implemented DynamicProxy as portrayed by the Scala reflection team --- src/library/scala/reflect/DynamicProxy.scala | 67 ++++++++++++++++++++++++++ src/library/scala/reflect/api/Names.scala | 4 +- test/files/run/dynamic-proxy.check | 20 ++++++++ test/files/run/dynamic-proxy.flags | 1 + test/files/run/dynamic-proxy.scala | 72 ++++++++++++++++++++++++++++ 5 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 src/library/scala/reflect/DynamicProxy.scala create mode 100644 test/files/run/dynamic-proxy.check create mode 100644 test/files/run/dynamic-proxy.flags create mode 100644 test/files/run/dynamic-proxy.scala diff --git a/src/library/scala/reflect/DynamicProxy.scala b/src/library/scala/reflect/DynamicProxy.scala new file mode 100644 index 0000000000..8449cb4063 --- /dev/null +++ b/src/library/scala/reflect/DynamicProxy.scala @@ -0,0 +1,67 @@ +package scala.reflect +/** + * A dynamic proxy which redirect method calls and attribute access to a given + * target object at runtime using reflection. + * A usage example can be found in test/files/run/dynamic-reflect.scala + * Not supported (yet): + * - implicit conversions and parameters + * - multiple arguments lists + * - explicit type arguments + */ +trait DynamicProxy extends Dynamic{ + val dynamicProxyTarget : AnyRef + + import scala.reflect.mirror._ + /** + * boxing to preserve information on primitive types for overloading resolution + */ + case class DynamicReflectBoxed( class_ : Class[_], value: Any ) + object DynamicReflectBoxed{ + implicit def box[@specialized T]( v:T ) = DynamicReflectBoxed( v.getClass, v ) + } + + def selectDynamic( method:String ) = { + val symbol = classToType( dynamicProxyTarget.getClass ).member( newTermName(method).encodedName ) + invoke( dynamicProxyTarget, symbol )() + } + + def updateDynamic( method:String )( value : Any ) = { + val symbol = classToType( dynamicProxyTarget.getClass ).member( newTermName(method+"_=").encodedName ) + invoke( dynamicProxyTarget, symbol )( value ) + } + + def applyDynamic( method:String )( args:DynamicReflectBoxed* ) : Any + = applyDynamicNamed( method )( args.map( value => ("",value) ) :_* ) + + def applyDynamicNamed( method:String )( args:(String,DynamicReflectBoxed)* ) : Any = { + val class_ = dynamicProxyTarget.getClass + var i = 0 + val toolbox = mkToolBox(mkConsoleReporter(),"") + val symbol = classToType( dynamicProxyTarget.getClass ).member( newTermName(method).encodedName ) + if(args.size == 0){ + invoke( dynamicProxyTarget, symbol )() + } else { + val call = + Apply( + Select( + TypeApply( + Select( + Select( + Ident(newFreeTerm("__this", symbolForName("scala.reflect.DynamicProxy").asType, this, null)) + , newTermName("dynamicProxyTarget") + ), + newTermName("asInstanceOf") ) + , List(TypeTree().setType(classToType(class_))) + ) + ,newTermName(method).encodedName + ) + ,args.map{ case(name,box) => + val value = Ident(newFreeTerm("__arg"+({i+=1;i}.toString), classToType(box.class_), box.value, null)) + if( name == "" ) value + else AssignOrNamedArg( Ident(name), value ) + }.toList + ) + toolbox.runExpr( call ) + } + } +} diff --git a/src/library/scala/reflect/api/Names.scala b/src/library/scala/reflect/api/Names.scala index c72774dfc7..d92d056751 100755 --- a/src/library/scala/reflect/api/Names.scala +++ b/src/library/scala/reflect/api/Names.scala @@ -34,12 +34,12 @@ trait Names { def toTypeName: TypeName /** Replaces all occurrences of $op_names in this name by corresponding operator symbols. - * Example: `foo_+=` becomes `foo_$plus$eq`. + * Example: `foo_$plus$eq` becomes `foo_+=` */ def decoded: String /** Replaces all occurrences of operator symbols in this name by corresponding $op_names. - * Example: `foo_$plus$eq` becomes `foo_+=` + * Example: `foo_+=` becomes `foo_$plus$eq`. */ def encoded: String diff --git a/test/files/run/dynamic-proxy.check b/test/files/run/dynamic-proxy.check new file mode 100644 index 0000000000..83c386812d --- /dev/null +++ b/test/files/run/dynamic-proxy.check @@ -0,0 +1,20 @@ +noargs +noargs +nullary +value +symbolic +symbolic with args +non-existent method +before mutation +mutation 1 +after mutation 1 +mutation 2 +after mutation 2 +overloaded with object +overloaded with primitive +overloaded with object in var +overloaded with object in var 2 +typeArgs: I am a car +default: 4 +default: 3 +named: 6 diff --git a/test/files/run/dynamic-proxy.flags b/test/files/run/dynamic-proxy.flags new file mode 100644 index 0000000000..68ecfa03f8 --- /dev/null +++ b/test/files/run/dynamic-proxy.flags @@ -0,0 +1 @@ +-Xexperimental diff --git a/test/files/run/dynamic-proxy.scala b/test/files/run/dynamic-proxy.scala new file mode 100644 index 0000000000..194da2d092 --- /dev/null +++ b/test/files/run/dynamic-proxy.scala @@ -0,0 +1,72 @@ +import scala.reflect._ + +class Car{ override def toString = "I am a car" } +object x{ + def nullary = "nullary" + def noargs() = "noargs" + def - = "symbolic" + def $(s:String) = "symbolic with args" + val value = "value" + def overloaded(i:Int) = "overloaded with primitive" + def overloaded(s:String) = s + def default( a:Int, b:Int = 2 ) = "default: "+(a+b) + def named( a:Int, b:Int, c:Int ) = "named: "+(a+b+c) + def typeArgs[T]( v:T ) = "typeArgs: "+v + var mutable = "before mutation" + def multiArgLists( a:String )( b:String ) = "multiArgList " + a + b + def bar( s:String )(implicit car:Car) = s + car.toString +} + +object Test extends App{ + val d = new DynamicProxy{ val dynamicProxyTarget = x } + + println( d.noargs ) + println( d.noargs() ) + println( d.nullary ) + println( d.value ) + println( d.- ) + println( d.$("x") ) + + try{ + println( d.test ) + } catch { + case _ => println("non-existent method") + } + + println( d.mutable ) + + println("mutation 1") + d.mutable_=("after mutation 1") + println( d.mutable ) + + println("mutation 2") + d.mutable = "after mutation 2" + println( d.mutable ) + + println( d.overloaded("overloaded with object") ) + println( d.overloaded(1) ) + + // test some non-constant arguments + def s = "overloaded with object in var" + println( d.overloaded(s) ) + println( d.overloaded(s + " 2") ) + + val car = new Car + println( d.typeArgs(car) ) // inferred + // println( d.typeArgs[Car](car) ) // explicit not working (yet) + + println( d.default( 1,3 ) ) + println( d.default( 1 ) ) + + println( d.named(1,c=3,b=2) ) // applyDynamicNamed seems to be broken + + // println( d.multiArgLists("a")("b") ) // not working yet + + /* + // may never work + // testing implicit parameters (first test works when moving x into TestDynamicReflect) + implicit val car2 = new Car + println( d.bar( "Yeah, ") ); // FAILS: could not find implicit value for parameter car + {println( d.bar( "Yeah, ") )} // FAILS: could not find implicit value for parameter car + */ +} -- cgit v1.2.3