diff options
-rw-r--r-- | src/compiler/scala/reflect/internal/StdNames.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/Symbols.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/TreeInfo.scala | 15 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 129 | ||||
-rw-r--r-- | src/library/scala/reflect/DynamicProxy.scala | 74 | ||||
-rwxr-xr-x | src/library/scala/reflect/api/Names.scala | 4 | ||||
-rw-r--r-- | test/files/neg/applydynamic_sip.check | 10 | ||||
-rw-r--r-- | test/files/neg/applydynamic_sip.scala | 10 | ||||
-rw-r--r-- | test/files/run/applydynamic_sip.check | 22 | ||||
-rw-r--r-- | test/files/run/applydynamic_sip.scala | 58 | ||||
-rw-r--r-- | test/files/run/dynamic-proxy.check | 20 | ||||
-rw-r--r-- | test/files/run/dynamic-proxy.flags | 1 | ||||
-rw-r--r-- | test/files/run/dynamic-proxy.scala | 72 |
14 files changed, 407 insertions, 16 deletions
diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala index 6f6fc8e95c..ac2cf178bf 100644 --- a/src/compiler/scala/reflect/internal/StdNames.scala +++ b/src/compiler/scala/reflect/internal/StdNames.scala @@ -325,6 +325,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val append: NameType = "append" val apply: NameType = "apply" val applyDynamic: NameType = "applyDynamic" + val applyDynamicNamed: NameType = "applyDynamicNamed" val applyOrElse: NameType = "applyOrElse" val args : NameType = "args" val argv : NameType = "argv" @@ -426,6 +427,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val runtime: NameType = "runtime" val sameElements: NameType = "sameElements" val scala_ : NameType = "scala" + val selectDynamic: NameType = "selectDynamic" val selectOverloadedMethod: NameType = "selectOverloadedMethod" val selectTerm: NameType = "selectTerm" val selectType: NameType = "selectType" @@ -455,6 +457,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val unapplySeq: NameType = "unapplySeq" val unbox: NameType = "unbox" val update: NameType = "update" + val updateDynamic: NameType = "updateDynamic" val value: NameType = "value" val valueOf : NameType = "valueOf" val values : NameType = "values" diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 78d0ebbc67..3bb57cea04 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1885,7 +1885,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => site.nonPrivateMemberAdmitting(name, admit).filter(sym => !sym.isTerm || (site.memberType(this) matches site.memberType(sym))) - /** The symbol overridden by this symbol in given class `ofclazz`. + /** The symbol, in class `ofclazz`, that is overridden by this symbol. * * @param ofclazz is a base class of this symbol's owner. */ diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala index ed22cad730..a8cca1625f 100644 --- a/src/compiler/scala/reflect/internal/TreeInfo.scala +++ b/src/compiler/scala/reflect/internal/TreeInfo.scala @@ -532,6 +532,21 @@ abstract class TreeInfo { } } + def isApplyDynamicName(name: Name) = (name == nme.updateDynamic) || (name == nme.selectDynamic) || (name == nme.applyDynamic) || (name == nme.applyDynamicNamed) + + class DynamicApplicationExtractor(nameTest: Name => Boolean) { + def unapply(tree: Tree) = tree match { + case Apply(TypeApply(Select(qual, oper), _), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name)) + case Apply(Select(qual, oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((qual, name)) + case Apply(Ident(oper), List(Literal(Constant(name)))) if nameTest(oper) => Some((EmptyTree, name)) + case _ => None + } + } + object DynamicUpdate extends DynamicApplicationExtractor(_ == nme.updateDynamic) + object DynamicApplication extends DynamicApplicationExtractor(isApplyDynamicName) + object DynamicApplicationNamed extends DynamicApplicationExtractor(_ == nme.applyDynamicNamed) + + // domain-specific extractors for reification import definitions._ diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 96cd7dde59..e9c3bef737 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -504,6 +504,9 @@ trait ContextErrors { def ApplyWithoutArgsError(tree: Tree, fun: Tree) = NormalTypeError(tree, fun.tpe+" does not take parameters") + def DynamicVarArgUnsupported(tree: Tree, name: String) = + issueNormalTypeError(tree, name+ " does not support passing a vararg parameter") + //checkClassType def TypeNotAStablePrefixError(tpt: Tree, pre: Type) = { issueNormalTypeError(tpt, "type "+pre+" is not a stable prefix") diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index fcdcc7b748..a6893ff4b2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -674,7 +674,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (tree.tpe.isInstanceOf[MethodType] && pre.isStable && sym.tpe.params.isEmpty && (isStableContext(tree, mode, pt) || sym.isModule)) - tree.setType(MethodType(List(), singleType(pre, sym))) + tree.setType(MethodType(List(), singleType(pre, sym))) // TODO: should this be a NullaryMethodType? else tree } @@ -2859,7 +2859,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { (args exists isNamed) || // uses a named argument isNamedApplyBlock(fun)) { // fun was transformed to a named apply block => // integrate this application into the block - tryNamesDefaults + if (dyna.isApplyDynamicNamed(fun)) dyna.typedNamedApply(tree, fun, args, mode, pt) + else tryNamesDefaults } else { val tparams = context.extractUndetparams() if (tparams.isEmpty) { // all type params are defined @@ -3480,7 +3481,108 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case ErrorType => setError(treeCopy.TypeApply(tree, fun, args)) case _ => - TypedApplyDoesNotTakeTpeParametersError(tree, fun) + fun match { + // drop the application for an applyDynamic or selectDynamic call since it has been pushed down + case treeInfo.DynamicApplication(_, _) => fun + case _ => TypedApplyDoesNotTakeTpeParametersError(tree, fun) + } + } + + object dyna { + import treeInfo.{isApplyDynamicName, DynamicUpdate, DynamicApplicationNamed} + + def acceptsApplyDynamic(tp: Type) = tp.typeSymbol isNonBottomSubClass DynamicClass + + /** Returns `Some(t)` if `name` can be selected dynamically on `qual`, `None` if not. + * `t` specifies the type to be passed to the applyDynamic/selectDynamic call (unless it is NoType) + * NOTE: currently either returns None or Some(NoType) (scala-virtualized extends this to Some(t) for selections on staged Structs) + */ + def acceptsApplyDynamicWithType(qual: Tree, name: Name): Option[Type] = + // don't selectDynamic selectDynamic, do select dynamic at unknown type, + // in scala-virtualized, we may return a Some(tp) where tp ne NoType + if (!isApplyDynamicName(name) && acceptsApplyDynamic(qual.tpe.widen)) Some(NoType) + else None + + def isDynamicallyUpdatable(tree: Tree) = tree match { + case DynamicUpdate(qual, name) => + // if the qualifier is a Dynamic, that's all we need to know + acceptsApplyDynamic(qual.tpe) + case _ => false + } + + def isApplyDynamicNamed(fun: Tree): Boolean = fun match { + case DynamicApplicationNamed(qual, _) if acceptsApplyDynamic(qual.tpe.widen) => true + case _ => false + // look deeper? + // val methPart = treeInfo.methPart(fun) + // println("methPart of "+ fun +" is "+ methPart) + // if (methPart ne fun) isApplyDynamicNamed(methPart) + // else false + } + + def typedNamedApply(orig: Tree, fun: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { + def argToBinding(arg: Tree): Tree = arg match { + case AssignOrNamedArg(Ident(name), rhs) => gen.mkTuple(List(CODE.LIT(name.toString), rhs)) + case _ => gen.mkTuple(List(CODE.LIT(""), arg)) + } + typed(treeCopy.Apply(orig, fun, args map argToBinding), mode, pt) + } + + /** Translate selection that does not typecheck according to the normal rules into a selectDynamic/applyDynamic. + * + * foo.method("blah") ~~> foo.applyDynamic("method")("blah") + * foo.method(x = "blah") ~~> foo.applyDynamicNamed("method")(("x", "blah")) + * foo.varia = 10 ~~> foo.updateDynamic("varia")(10) + * foo.field ~~> foo.selectDynamic("field") + * foo.arr(10) = 13 ~~> foo.selectDynamic("arr").update(10, 13) + * + * what if we want foo.field == foo.selectDynamic("field") == 1, but `foo.field = 10` == `foo.selectDynamic("field").update(10)` == () + * what would the signature for selectDynamic be? (hint: it needs to depend on whether an update call is coming or not) + * + * need to distinguish selectDynamic and applyDynamic somehow: the former must return the selected value, the latter must accept an apply or an update + * - could have only selectDynamic and pass it a boolean whether more is to come, + * so that it can either return the bare value or something that can handle the apply/update + * HOWEVER that makes it hard to return unrelated values for the two cases + * --> selectDynamic's return type is now dependent on the boolean flag whether more is to come + * - simplest solution: have two method calls + * + */ + def mkInvoke(cxTree: Tree, tree: Tree, qual: Tree, name: Name): Option[Tree] = + acceptsApplyDynamicWithType(qual, name) map { tp => + // tp eq NoType => can call xxxDynamic, but not passing any type args (unless specified explicitly by the user) + // in scala-virtualized, when not NoType, tp is passed as type argument (for selection on a staged Struct) + + // strip off type application -- we're not doing much with outer, so don't bother preserving cxTree's attributes etc + val (outer, explicitTargs) = cxTree match { + case TypeApply(fun, targs) => (fun, targs) + case Apply(TypeApply(fun, targs), args) => (Apply(fun, args), targs) + case t => (t, Nil) + } + + @inline def hasNamedArg(as: List[Tree]) = as collectFirst {case AssignOrNamedArg(lhs, rhs) =>} nonEmpty + + // note: context.tree includes at most one Apply node + // thus, we can't use it to detect we're going to receive named args in expressions such as: + // qual.sel(a)(a2, arg2 = "a2") + val oper = outer match { + case Apply(`tree`, as) => + val oper = + if (hasNamedArg(as)) nme.applyDynamicNamed + else nme.applyDynamic + // not supported: foo.bar(a1,..., an: _*) + if (treeInfo.isWildcardStarArgList(as)) { + DynamicVarArgUnsupported(tree, oper) + return Some(setError(tree)) + } else oper + case Assign(`tree`, _) => nme.updateDynamic + case _ => nme.selectDynamic + } + + val dynSel = Select(qual, oper) + val tappSel = if (explicitTargs nonEmpty) TypeApply(dynSel, explicitTargs) else dynSel + + atPos(qual.pos)(Apply(tappSel, List(Literal(Constant(name.decode))))) + } } @inline final def deindentTyping() = context.typingIndentLevel -= 2 @@ -3653,10 +3755,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { case _ => } } +// if (varsym.isVariable || +// // setter-rewrite has been done above, so rule out methods here, but, wait a minute, why are we assigning to non-variables after erasure?! +// (phase.erasedTypes && varsym.isValue && !varsym.isMethod)) { if (varsym.isVariable || varsym.isValue && phase.erasedTypes) { val rhs1 = typed(rhs, EXPRmode | BYVALmode, lhs1.tpe) treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitClass.tpe } + else if(dyna.isDynamicallyUpdatable(lhs1)) { + val rhs1 = typed(rhs, EXPRmode | BYVALmode, WildcardType) + typed1(Apply(lhs1, List(rhs1)), mode, pt) + } else fail() } @@ -4119,16 +4228,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } // try to expand according to Dynamic rules. - - if (settings.Xexperimental.value && (qual.tpe.widen.typeSymbol isNonBottomSubClass DynamicClass)) { - var dynInvoke = Apply(Select(qual, nme.applyDynamic), List(Literal(Constant(name.decode)))) - context.tree match { - case Apply(tree1, args) if tree1 eq tree => - ; - case _ => - dynInvoke = Apply(dynInvoke, List()) - } - return typed1(util.trace("dynatype: ")(dynInvoke), mode, pt) + dyna.mkInvoke(context.tree, tree, qual, name) match { + case Some(invocation) => + return typed1(invocation, mode, pt) + case _ => } if (settings.debug.value) { diff --git a/src/library/scala/reflect/DynamicProxy.scala b/src/library/scala/reflect/DynamicProxy.scala new file mode 100644 index 0000000000..ffd1e7a39f --- /dev/null +++ b/src/library/scala/reflect/DynamicProxy.scala @@ -0,0 +1,74 @@ +package scala.reflect +/** + * A dynamic proxy which redirects method calls and attribute access to a given + * target object at runtime using reflection. + * + * Usage example: + * + * object x{ def hello = "hello world" } + * val d = new DynamicProxy{ val dynamicProxyTarget = x } + * assert( d.hello == "hello world" ) + * + * Not supported (yet): + * - implicit conversions and parameters + * - multiple arguments lists + * - explicit type arguments + */ +trait DynamicProxy extends Dynamic{ + /** Method calls on DynamicProxy are redirected to this object. Needs to be defined in a subclass. */ + 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/neg/applydynamic_sip.check b/test/files/neg/applydynamic_sip.check new file mode 100644 index 0000000000..8845f68a52 --- /dev/null +++ b/test/files/neg/applydynamic_sip.check @@ -0,0 +1,10 @@ +applydynamic_sip.scala:7: error: applyDynamic does not support passing a vararg parameter + qual.sel(a, a2: _*) + ^ +applydynamic_sip.scala:8: error: applyDynamicNamed does not support passing a vararg parameter + qual.sel(arg = a, a2: _*) + ^ +applydynamic_sip.scala:9: error: applyDynamicNamed does not support passing a vararg parameter + qual.sel(arg, arg2 = "a2", a2: _*) + ^ +three errors found diff --git a/test/files/neg/applydynamic_sip.scala b/test/files/neg/applydynamic_sip.scala new file mode 100644 index 0000000000..362461577b --- /dev/null +++ b/test/files/neg/applydynamic_sip.scala @@ -0,0 +1,10 @@ +object Test extends App { + val qual: Dynamic = ??? + val expr = "expr" + val a = "a" + val a2 = "a2" + + qual.sel(a, a2: _*) + qual.sel(arg = a, a2: _*) + qual.sel(arg, arg2 = "a2", a2: _*) +}
\ No newline at end of file diff --git a/test/files/run/applydynamic_sip.check b/test/files/run/applydynamic_sip.check new file mode 100644 index 0000000000..d94db4417e --- /dev/null +++ b/test/files/run/applydynamic_sip.check @@ -0,0 +1,22 @@ +qual.applyDynamic(sel)() +qual.applyDynamic(sel)(a) +qual.applyDynamic(sel)(a) +.apply(a2) +qual.applyDynamic(sel)(a) +qual.applyDynamic(sel)(a) +.apply(a2) +qual.applyDynamicNamed(sel)((arg,a)) +qual.applyDynamicNamed(sel)((arg,a)) +qual.applyDynamicNamed(sel)((,a), (arg2,a2)) +qual.updateDynamic(sel)(expr) +qual.selectDynamic(sel) +qual.selectDynamic(sel) +qual.selectDynamic(sel) +.update(1, expr) +qual.selectDynamic(sel) +.update(expr) +qual.selectDynamic(sel) +.apply(1) +qual.selectDynamic(sel) +.apply +.update(1, 1) diff --git a/test/files/run/applydynamic_sip.scala b/test/files/run/applydynamic_sip.scala new file mode 100644 index 0000000000..57cb4349f7 --- /dev/null +++ b/test/files/run/applydynamic_sip.scala @@ -0,0 +1,58 @@ +object Test extends App { + object stubUpdate { + def update(as: Any*) = println(".update"+as.toList.mkString("(",", ", ")")) + } + + object stub { + def apply = {println(".apply"); stubUpdate} + def apply(as: Any*) = println(".apply"+as.toList.mkString("(",", ", ")")) + def update(as: Any*) = println(".update"+as.toList.mkString("(",", ", ")")) + } + class MyDynamic extends Dynamic { + def applyDynamic[T](n: String)(as: Any*) = {println("qual.applyDynamic("+ n +")"+ as.toList.mkString("(",", ", ")")); stub} + def applyDynamicNamed[T](n: String)(as: (String, Any)*) = {println("qual.applyDynamicNamed("+ n +")"+ as.toList.mkString("(",", ", ")")); stub} + def selectDynamic[T](n: String) = {println("qual.selectDynamic("+ n +")"); stub} + def updateDynamic(n: String)(x: Any): Unit = {println("qual.updateDynamic("+ n +")("+ x +")")} + } + val qual = new MyDynamic + val expr = "expr" + val a = "a" + val a2 = "a2" + type T = String + + // If qual.sel is followed by a potential type argument list [Ts] and an argument list (arg1, …, argn) where none of the arguments argi are named: + // qual.applyDynamic(“sel”)(arg1, …, argn) + qual.sel() + qual.sel(a) + // qual.sel(a, a2: _*) -- should not accept varargs? + qual.sel(a)(a2) + qual.sel[T](a) + qual.sel[T](a)(a2) + + // If qual.sel is followed by a potential type argument list [Ts] + // and a non-empty named argument list (x1 = arg1, …, xn = argn) where some name prefixes xi = might be missing: + // qual.applyDynamicNamed(“sel”)(xs1 -> arg1, …, xsn -> argn) + qual.sel(arg = a) + qual.sel[T](arg = a) + qual.sel(a, arg2 = "a2") + // qual.sel(a)(a2, arg2 = "a2") + // qual.sel[T](a)(a2, arg2 = "a2") + // qual.sel(arg = a, a2: _*) + // qual.sel(arg, arg2 = "a2", a2: _*) + + // If qual.sel appears immediately on the left-hand side of an assigment + // qual.updateDynamic(“sel”)(expr) + qual.sel = expr + + // If qual.sel, possibly applied to type arguments, but is + // not applied to explicit value arguments, + // nor immediately followed by an assignment operator: + // qual.selectDynamic[Ts](“sel”) + qual.sel + qual.sel[T] + + qual.sel(1) = expr // parser turns this into qual.sel.update(1, expr) + qual.sel() = expr // parser turns this into qual.sel.update(expr) + qual.sel.apply(1) + qual.sel.apply(1) = 1 +}
\ No newline at end of file diff --git a/test/files/run/dynamic-proxy.check b/test/files/run/dynamic-proxy.check new file mode 100644 index 0000000000..d1b85daff4 --- /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..48fd867160 --- /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..ab5a8b1d66 --- /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 + */ +} |