From 8b87327d4a3e8145c5716ec4883c497d86739281 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Thu, 31 Jan 2013 21:09:24 +0100 Subject: SI-6411 reflection is now aware of posterasure The `transformedType` method, which is used to bring Scala types to Java world, was written in pre-valueclass times. Therefore, this method only called transforms from erasure, uncurry and refChecks. Now runtime reflection becomes aware of posterasure and as a consequence methods, which have value classes in their signatures, can be called without having to wrap them in catch-a-crash clause. Another facet to this fix was the realization that value classes need to be unwrapped, e.g. C(2) needs to be transformed to just 2, when they are used naked in method signatures (i.e. `c` in `def foo(c: C)` needs to be unwrapped, whereas `cs: List[C]`, `cs: C*` and even `cs: Array[C]` do not). --- test/files/run/t6411.check | 80 ++++++++++++++++++++++ test/files/run/t6411.scala | 73 ++++++++++++++++++++ .../nsc/symtab/SymbolTableForUnitTesting.scala | 1 + 3 files changed, 154 insertions(+) create mode 100644 test/files/run/t6411.check create mode 100644 test/files/run/t6411.scala (limited to 'test') diff --git a/test/files/run/t6411.check b/test/files/run/t6411.check new file mode 100644 index 0000000000..03055b5a8a --- /dev/null +++ b/test/files/run/t6411.check @@ -0,0 +1,80 @@ +meth = method yg_1 +as seen by Scala reflection: def yg_1[T](y: Y[T]): T +as seen by Java reflection: public java.lang.Object a$.yg_1(java.lang.Object) +result = 1 +meth = method yg_1 +as seen by Scala reflection: def yg_1[T](y: Y[T]): T +as seen by Java reflection: public java.lang.Object a$.yg_1(java.lang.Object) +result = 1 +meth = method yi_2 +as seen by Scala reflection: def yi_2(y: Y[Int]): Int +as seen by Java reflection: public int a$.yi_2(java.lang.Integer) +result = 2 +meth = method yi_2 +as seen by Scala reflection: def yi_2(y: Y[Int]): Int +as seen by Java reflection: public int a$.yi_2(java.lang.Integer) +result = class java.lang.IllegalArgumentException: argument type mismatch +meth = method ys_3 +as seen by Scala reflection: def ys_3(y: Y[String]): String +as seen by Java reflection: public java.lang.String a$.ys_3(java.lang.String) +result = class java.lang.IllegalArgumentException: argument type mismatch +meth = method ys_3 +as seen by Scala reflection: def ys_3(y: Y[String]): String +as seen by Java reflection: public java.lang.String a$.ys_3(java.lang.String) +result = 3 +meth = method ya_4 +as seen by Scala reflection: def ya_4(ys: Array[Y[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.ya_4(Y[]) +result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String +meth = method ya_4 +as seen by Scala reflection: def ya_4(ys: Array[Y[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.ya_4(Y[]) +result = List(4) +meth = method yl_5 +as seen by Scala reflection: def yl_5(ys: List[Y[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.yl_5(scala.collection.immutable.List) +result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String +meth = method yl_5 +as seen by Scala reflection: def yl_5(ys: List[Y[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.yl_5(scala.collection.immutable.List) +result = List(5) +meth = method zg_1 +as seen by Scala reflection: def zg_1[T](z: Z[T]): T +as seen by Java reflection: public java.lang.Object a$.zg_1(Z) +result = 1 +meth = method zg_1 +as seen by Scala reflection: def zg_1[T](z: Z[T]): T +as seen by Java reflection: public java.lang.Object a$.zg_1(Z) +result = 1 +meth = method zi_2 +as seen by Scala reflection: def zi_2(z: Z[Int]): Int +as seen by Java reflection: public int a$.zi_2(Z) +result = 2 +meth = method zi_2 +as seen by Scala reflection: def zi_2(z: Z[Int]): Int +as seen by Java reflection: public int a$.zi_2(Z) +result = class java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer +meth = method zs_3 +as seen by Scala reflection: def zs_3(z: Z[String]): String +as seen by Java reflection: public java.lang.String a$.zs_3(Z) +result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String +meth = method zs_3 +as seen by Scala reflection: def zs_3(z: Z[String]): String +as seen by Java reflection: public java.lang.String a$.zs_3(Z) +result = 3 +meth = method za_4 +as seen by Scala reflection: def za_4(zs: Array[Z[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.za_4(Z[]) +result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String +meth = method za_4 +as seen by Scala reflection: def za_4(zs: Array[Z[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.za_4(Z[]) +result = List(4) +meth = method zl_5 +as seen by Scala reflection: def zl_5(zs: List[Z[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.zl_5(scala.collection.immutable.List) +result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String +meth = method zl_5 +as seen by Scala reflection: def zl_5(zs: List[Z[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.zl_5(scala.collection.immutable.List) +result = List(5) diff --git a/test/files/run/t6411.scala b/test/files/run/t6411.scala new file mode 100644 index 0000000000..d80f7f6a1e --- /dev/null +++ b/test/files/run/t6411.scala @@ -0,0 +1,73 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.language.reflectiveCalls + +class Y[T](val i: T) extends AnyVal { + override def toString = s"Y($i)" +} +class Z[T](val i: T) extends AnyRef { + override def toString = s"Z($i)" +} + +object a { + def yg_1[T](y: Y[T]) = y.i + def yi_2(y: Y[Int]) = y.i + def ys_3(y: Y[String]) = y.i + def ya_4(ys: Array[Y[String]]) = ys.toList.map(_.i) + def yl_5(ys: List[Y[String]]) = ys.map(_.i) + def yv_6(ys: Y[String]*) = ys.toList.map(_.i) + + def zg_1[T](z: Z[T]) = z.i + def zi_2(z: Z[Int]) = z.i + def zs_3(z: Z[String]) = z.i + def za_4(zs: Array[Z[String]]) = zs.toList.map(_.i) + def zl_5(zs: List[Z[String]]) = zs.map(_.i) + def zv_6(zs: Z[String]*) = zs.toList.map(_.i) +} + +object Test extends App { + def test(methName: String, arg: Any) = { + val moduleA = cm.reflect(a) + val msym = moduleA.symbol.typeSignature.declaration(TermName(methName)).asMethod + println(s"meth = $msym") + val mmirror = moduleA.reflectMethod(msym) + val mresult = + try { mmirror(arg) } + catch { + case ex: Exception => + val ex1 = scala.reflect.runtime.ReflectionUtils.unwrapThrowable(ex) + s"${ex1.getClass}: ${ex1.getMessage}" + } + println(s"as seen by Scala reflection: ${msym.asInstanceOf[scala.reflect.internal.Symbols#Symbol].defString}") + println(s"as seen by Java reflection: ${mmirror.asInstanceOf[{val jmeth: java.lang.reflect.Method}].jmeth}") + println(s"result = $mresult") + } + + test("yg_1", new Y(1)) + test("yg_1", new Y("1")) + test("yi_2", new Y(2)) + test("yi_2", new Y("2")) + test("ys_3", new Y(3)) + test("ys_3", new Y("3")) + test("ya_4", Array(new Y(4))) + test("ya_4", Array(new Y("4"))) + test("yl_5", List(new Y(5))) + test("yl_5", List(new Y("5"))) + // FIXME: disabled because of SI-7056 + // test("yv_6", new Y(6)) + // test("yv_6", new Y("6")) + + test("zg_1", new Z(1)) + test("zg_1", new Z("1")) + test("zi_2", new Z(2)) + test("zi_2", new Z("2")) + test("zs_3", new Z(3)) + test("zs_3", new Z("3")) + test("za_4", Array(new Z(4))) + test("za_4", Array(new Z("4"))) + test("zl_5", List(new Z(5))) + test("zl_5", List(new Z("5"))) + // FIXME: disabled because of SI-7056 + // test("zv_6", new Z(6)) + // test("zv_6", new Z("6")) +} \ No newline at end of file diff --git a/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala b/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala index a3699a4eeb..b42e9a07cb 100644 --- a/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala +++ b/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala @@ -69,6 +69,7 @@ class SymbolTableForUnitTesting extends SymbolTable { // Members declared in scala.reflect.internal.Required def picklerPhase: scala.reflect.internal.Phase = SomePhase + def erasurePhase: scala.reflect.internal.Phase = SomePhase // Members declared in scala.reflect.internal.SymbolTable def currentRunId: Int = 1 -- cgit v1.2.3 From 32a02d6f7c697870c071b86e6552740bcced7697 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 24 Jan 2014 21:02:57 +0300 Subject: updates the test for by-name value class parameters --- test/files/run/t6411.check | 16 ++++++++++++++++ test/files/run/t6411.scala | 8 ++++++++ 2 files changed, 24 insertions(+) (limited to 'test') diff --git a/test/files/run/t6411.check b/test/files/run/t6411.check index 03055b5a8a..9226146195 100644 --- a/test/files/run/t6411.check +++ b/test/files/run/t6411.check @@ -38,6 +38,14 @@ meth = method yl_5 as seen by Scala reflection: def yl_5(ys: List[Y[String]]): List[String] as seen by Java reflection: public scala.collection.immutable.List a$.yl_5(scala.collection.immutable.List) result = List(5) +meth = method yni_7 +as seen by Scala reflection: def yni_7(y: => Y[Int]): Int +as seen by Java reflection: public int a$.yni_7(scala.Function0) +result = 7 +meth = method yns_8 +as seen by Scala reflection: def yns_8(y: => Y[String]): String +as seen by Java reflection: public java.lang.String a$.yns_8(scala.Function0) +result = 8 meth = method zg_1 as seen by Scala reflection: def zg_1[T](z: Z[T]): T as seen by Java reflection: public java.lang.Object a$.zg_1(Z) @@ -78,3 +86,11 @@ meth = method zl_5 as seen by Scala reflection: def zl_5(zs: List[Z[String]]): List[String] as seen by Java reflection: public scala.collection.immutable.List a$.zl_5(scala.collection.immutable.List) result = List(5) +meth = method zni_7 +as seen by Scala reflection: def zni_7(z: => Z[Int]): Int +as seen by Java reflection: public int a$.zni_7(scala.Function0) +result = 7 +meth = method zns_8 +as seen by Scala reflection: def zns_8(z: => Z[String]): String +as seen by Java reflection: public java.lang.String a$.zns_8(scala.Function0) +result = 8 diff --git a/test/files/run/t6411.scala b/test/files/run/t6411.scala index d80f7f6a1e..3bfeac2890 100644 --- a/test/files/run/t6411.scala +++ b/test/files/run/t6411.scala @@ -16,6 +16,8 @@ object a { def ya_4(ys: Array[Y[String]]) = ys.toList.map(_.i) def yl_5(ys: List[Y[String]]) = ys.map(_.i) def yv_6(ys: Y[String]*) = ys.toList.map(_.i) + def yni_7(y: => Y[Int]) = y.i + def yns_8(y: => Y[String]) = y.i def zg_1[T](z: Z[T]) = z.i def zi_2(z: Z[Int]) = z.i @@ -23,6 +25,8 @@ object a { def za_4(zs: Array[Z[String]]) = zs.toList.map(_.i) def zl_5(zs: List[Z[String]]) = zs.map(_.i) def zv_6(zs: Z[String]*) = zs.toList.map(_.i) + def zni_7(z: => Z[Int]) = z.i + def zns_8(z: => Z[String]) = z.i } object Test extends App { @@ -56,6 +60,8 @@ object Test extends App { // FIXME: disabled because of SI-7056 // test("yv_6", new Y(6)) // test("yv_6", new Y("6")) + test("yni_7", new Y(7)) + test("yns_8", new Y("8")) test("zg_1", new Z(1)) test("zg_1", new Z("1")) @@ -70,4 +76,6 @@ object Test extends App { // FIXME: disabled because of SI-7056 // test("zv_6", new Z(6)) // test("zv_6", new Z("6")) + test("zni_7", new Z(7)) + test("zns_8", new Z("8")) } \ No newline at end of file -- cgit v1.2.3 From 6c1129bdc274eeb822a1924cb22d852ea9cd62de Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 24 Jan 2014 22:46:34 +0300 Subject: unifies method and constructor handling in JavaMirrors This automatically brings performance fixes and correct handling of values class / by-name params into the constructor land. --- .../scala/reflect/runtime/JavaMirrors.scala | 91 +++++++++++--------- .../files/run/reflection-magicsymbols-invoke.check | 2 +- test/files/run/t6411.check | 96 ---------------------- test/files/run/t6411.scala | 81 ------------------ test/files/run/t6411a.check | 96 ++++++++++++++++++++++ test/files/run/t6411a.scala | 81 ++++++++++++++++++ test/files/run/t6411b.check | 1 + test/files/run/t6411b.scala | 12 +++ 8 files changed, 242 insertions(+), 218 deletions(-) delete mode 100644 test/files/run/t6411.check delete mode 100644 test/files/run/t6411.scala create mode 100644 test/files/run/t6411a.check create mode 100644 test/files/run/t6411a.scala create mode 100644 test/files/run/t6411b.check create mode 100644 test/files/run/t6411b.scala (limited to 'test') diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 4a95791284..84f8b5991b 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -118,15 +118,16 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def abort(msg: String) = throw new ScalaReflectionException(msg) - private def ErrorInnerClass(sym: Symbol) = abort(s"$sym is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") - private def ErrorInnerModule(sym: Symbol) = abort(s"$sym is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") - private def ErrorStaticClass(sym: Symbol) = abort(s"$sym is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") - private def ErrorStaticModule(sym: Symbol) = abort(s"$sym is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") - private def ErrorNotMember(sym: Symbol, owner: Symbol) = abort(s"expected a member of $owner, you provided ${sym.kindString} ${sym.fullName}") - private def ErrorNotField(sym: Symbol) = abort(s"expected a field or an accessor method symbol, you provided $sym") - private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = abort(s"expected a constructor of $owner, you provided $sym") - private def ErrorFree(member: Symbol, freeType: Symbol) = abort(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}") - private def ErrorNonExistentField(sym: Symbol) = abort( + private def ErrorInnerClass(sym: Symbol) = abort(s"$sym is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") + private def ErrorInnerModule(sym: Symbol) = abort(s"$sym is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") + private def ErrorStaticClass(sym: Symbol) = abort(s"$sym is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") + private def ErrorStaticModule(sym: Symbol) = abort(s"$sym is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") + private def ErrorNotMember(sym: Symbol, owner: Symbol) = abort(s"expected a member of $owner, you provided ${sym.kindString} ${sym.fullName}") + private def ErrorNotField(sym: Symbol) = abort(s"expected a field or an accessor method symbol, you provided $sym") + private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = abort(s"expected a constructor of $owner, you provided $sym") + private def ErrorArrayConstructor(sym: Symbol, owner: Symbol) = abort(s"Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag().newArray()` instead") + private def ErrorFree(member: Symbol, freeType: Symbol) = abort(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}") + private def ErrorNonExistentField(sym: Symbol) = abort( sm"""Scala field ${sym.name} isn't represented as a Java field, neither it has a Java accessor method |note that private parameters of class constructors don't get mapped onto fields and/or accessors, |unless they are used outside of their declaring constructors.""") @@ -221,6 +222,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def checkConstructorOf(sym: Symbol, owner: ClassSymbol) { if (!sym.isClassConstructor) ErrorNotConstructor(sym, owner) + if (owner == ArrayClass) ErrorArrayConstructor(sym, owner) ensuringNotFree(sym) { if (!owner.info.decls.toList.contains(sym)) ErrorNotConstructor(sym, owner) } @@ -334,53 +336,76 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } } } + private abstract class JavaMethodMirror(val symbol: MethodSymbol) extends MethodMirror { lazy val jmeth = ensureAccessible(methodToJava(symbol)) - def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) + lazy val jconstr = ensureAccessible(constructorToJava(symbol)) - def jinvoke(jmeth: jMethod, receiver: Any, args: Seq[Any]): Any = { - val result = jinvokeraw(jmeth, receiver, args) - if (jmeth.getReturnType == java.lang.Void.TYPE) () + def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) + else if (receiver == null) jconstr.newInstance(args.asInstanceOf[Seq[AnyRef]]: _*) + else jconstr.newInstance((receiver +: args).asInstanceOf[Seq[AnyRef]]: _*) + def jinvoke(args: Seq[Any]): Any = { + val result = jinvokeraw(args) + if (!symbol.isConstructor && jmeth.getReturnType == java.lang.Void.TYPE) () else result } - override def toString = s"method mirror for ${showMethodSig(symbol)} (bound to $receiver)" + override def toString = { + val what = if (symbol.isConstructor) "constructor mirror" else "method mirror" + s"$what for ${showMethodSig(symbol)} (bound to $receiver)" + } } private class JavaVanillaMethodMirror(val receiver: Any, symbol: MethodSymbol) extends JavaMethodMirror(symbol) { def bind(newReceiver: Any) = new JavaVanillaMethodMirror(newReceiver, symbol) - def apply(args: Any*): Any = jinvoke(jmeth, receiver, args) + def apply(args: Any*): Any = jinvoke(args) } private class JavaVanillaMethodMirror0(receiver: Any, symbol: MethodSymbol) extends JavaVanillaMethodMirror(receiver, symbol) { override def bind(newReceiver: Any) = new JavaVanillaMethodMirror0(newReceiver, symbol) - override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver) + override def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver) + else if (receiver == null) jconstr.newInstance() + else jconstr.newInstance(receiver.asInstanceOf[AnyRef]) } private class JavaVanillaMethodMirror1(receiver: Any, symbol: MethodSymbol) extends JavaVanillaMethodMirror(receiver, symbol) { override def bind(newReceiver: Any) = new JavaVanillaMethodMirror1(newReceiver, symbol) - override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef]) + override def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef]) + else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef]) + else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef]) } private class JavaVanillaMethodMirror2(receiver: Any, symbol: MethodSymbol) extends JavaVanillaMethodMirror(receiver, symbol) { override def bind(newReceiver: Any) = new JavaVanillaMethodMirror2(newReceiver, symbol) - override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) + override def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) + else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) + else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) } private class JavaVanillaMethodMirror3(receiver: Any, symbol: MethodSymbol) extends JavaVanillaMethodMirror(receiver, symbol) { override def bind(newReceiver: Any) = new JavaVanillaMethodMirror3(newReceiver, symbol) - override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) + override def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) + else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) + else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) } private class JavaVanillaMethodMirror4(receiver: Any, symbol: MethodSymbol) extends JavaVanillaMethodMirror(receiver, symbol) { override def bind(newReceiver: Any) = new JavaVanillaMethodMirror4(newReceiver, symbol) - override def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) + override def jinvokeraw(args: Seq[Any]) = + if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) + else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) + else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) } // caches MethodSymbol metadata, so that we minimize the work that needs to be done during Mirror.apply @@ -414,7 +439,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni else if (isValueClass(i)) args1(i) = paramUnboxers(i).invoke(arg) i += 1 } - jinvoke(jmeth, receiver, args1) + jinvoke(args1) } } @@ -444,7 +469,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def invokePrimitiveMethod = { val jmeths = classOf[BoxesRunTime].getDeclaredMethods.filter(_.getName == nme.primitiveMethodName(symbol.name).toString) assert(jmeths.length == 1, jmeths.toList) - jinvoke(jmeths.head, null, objReceiver +: objArgs) + val jmeth = jmeths.head + val result = jmeth.invoke(null, (objReceiver +: objArgs).asInstanceOf[Seq[AnyRef]]: _*) + if (jmeth.getReturnType == java.lang.Void.TYPE) () + else result } symbol match { @@ -475,23 +503,6 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } } - private class JavaConstructorMirror(val outer: AnyRef, val symbol: MethodSymbol) - extends MethodMirror { - def bind(newReceiver: Any) = new JavaConstructorMirror(newReceiver.asInstanceOf[AnyRef], symbol) - override val receiver = outer - lazy val jconstr = ensureAccessible(constructorToJava(symbol)) - def apply(args: Any*): Any = { - if (symbol.owner == ArrayClass) - abort("Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag().newArray()` instead") - - val effectiveArgs = - if (outer == null) args.asInstanceOf[Seq[AnyRef]] - else outer +: args.asInstanceOf[Seq[AnyRef]] - jconstr.newInstance(effectiveArgs: _*) - } - override def toString = s"constructor mirror for ${showMethodSig(symbol)} (bound to $outer)" - } - private abstract class JavaTemplateMirror extends TemplateMirror { def outer: AnyRef @@ -504,7 +515,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def isStatic = false def reflectConstructor(constructor: MethodSymbol) = { checkConstructorOf(constructor, symbol) - new JavaConstructorMirror(outer, constructor) + mkJavaMethodMirror(outer, constructor) } override def toString = s"class mirror for ${symbol.fullName} (bound to $outer)" } diff --git a/test/files/run/reflection-magicsymbols-invoke.check b/test/files/run/reflection-magicsymbols-invoke.check index 352aefaf25..7f9767f095 100644 --- a/test/files/run/reflection-magicsymbols-invoke.check +++ b/test/files/run/reflection-magicsymbols-invoke.check @@ -28,7 +28,7 @@ it's important to print the list of AnyVal's members if some of them change (possibly, adding and/or removing magic symbols), we must update this test constructor AnyVal: ()AnyVal method getClass: ()Class[_ <: AnyVal] -testing AnyVal.: class java.lang.InstantiationException: null +testing AnyVal.: class scala.ScalaReflectionException: unsupported symbol constructor AnyVal when invoking constructor mirror for scala.AnyVal.(): AnyVal (bound to null) testing AnyVal.getClass: class scala.ScalaReflectionException: expected a member of class Integer, you provided method scala.AnyVal.getClass ============ AnyRef diff --git a/test/files/run/t6411.check b/test/files/run/t6411.check deleted file mode 100644 index 9226146195..0000000000 --- a/test/files/run/t6411.check +++ /dev/null @@ -1,96 +0,0 @@ -meth = method yg_1 -as seen by Scala reflection: def yg_1[T](y: Y[T]): T -as seen by Java reflection: public java.lang.Object a$.yg_1(java.lang.Object) -result = 1 -meth = method yg_1 -as seen by Scala reflection: def yg_1[T](y: Y[T]): T -as seen by Java reflection: public java.lang.Object a$.yg_1(java.lang.Object) -result = 1 -meth = method yi_2 -as seen by Scala reflection: def yi_2(y: Y[Int]): Int -as seen by Java reflection: public int a$.yi_2(java.lang.Integer) -result = 2 -meth = method yi_2 -as seen by Scala reflection: def yi_2(y: Y[Int]): Int -as seen by Java reflection: public int a$.yi_2(java.lang.Integer) -result = class java.lang.IllegalArgumentException: argument type mismatch -meth = method ys_3 -as seen by Scala reflection: def ys_3(y: Y[String]): String -as seen by Java reflection: public java.lang.String a$.ys_3(java.lang.String) -result = class java.lang.IllegalArgumentException: argument type mismatch -meth = method ys_3 -as seen by Scala reflection: def ys_3(y: Y[String]): String -as seen by Java reflection: public java.lang.String a$.ys_3(java.lang.String) -result = 3 -meth = method ya_4 -as seen by Scala reflection: def ya_4(ys: Array[Y[String]]): List[String] -as seen by Java reflection: public scala.collection.immutable.List a$.ya_4(Y[]) -result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String -meth = method ya_4 -as seen by Scala reflection: def ya_4(ys: Array[Y[String]]): List[String] -as seen by Java reflection: public scala.collection.immutable.List a$.ya_4(Y[]) -result = List(4) -meth = method yl_5 -as seen by Scala reflection: def yl_5(ys: List[Y[String]]): List[String] -as seen by Java reflection: public scala.collection.immutable.List a$.yl_5(scala.collection.immutable.List) -result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String -meth = method yl_5 -as seen by Scala reflection: def yl_5(ys: List[Y[String]]): List[String] -as seen by Java reflection: public scala.collection.immutable.List a$.yl_5(scala.collection.immutable.List) -result = List(5) -meth = method yni_7 -as seen by Scala reflection: def yni_7(y: => Y[Int]): Int -as seen by Java reflection: public int a$.yni_7(scala.Function0) -result = 7 -meth = method yns_8 -as seen by Scala reflection: def yns_8(y: => Y[String]): String -as seen by Java reflection: public java.lang.String a$.yns_8(scala.Function0) -result = 8 -meth = method zg_1 -as seen by Scala reflection: def zg_1[T](z: Z[T]): T -as seen by Java reflection: public java.lang.Object a$.zg_1(Z) -result = 1 -meth = method zg_1 -as seen by Scala reflection: def zg_1[T](z: Z[T]): T -as seen by Java reflection: public java.lang.Object a$.zg_1(Z) -result = 1 -meth = method zi_2 -as seen by Scala reflection: def zi_2(z: Z[Int]): Int -as seen by Java reflection: public int a$.zi_2(Z) -result = 2 -meth = method zi_2 -as seen by Scala reflection: def zi_2(z: Z[Int]): Int -as seen by Java reflection: public int a$.zi_2(Z) -result = class java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer -meth = method zs_3 -as seen by Scala reflection: def zs_3(z: Z[String]): String -as seen by Java reflection: public java.lang.String a$.zs_3(Z) -result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String -meth = method zs_3 -as seen by Scala reflection: def zs_3(z: Z[String]): String -as seen by Java reflection: public java.lang.String a$.zs_3(Z) -result = 3 -meth = method za_4 -as seen by Scala reflection: def za_4(zs: Array[Z[String]]): List[String] -as seen by Java reflection: public scala.collection.immutable.List a$.za_4(Z[]) -result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String -meth = method za_4 -as seen by Scala reflection: def za_4(zs: Array[Z[String]]): List[String] -as seen by Java reflection: public scala.collection.immutable.List a$.za_4(Z[]) -result = List(4) -meth = method zl_5 -as seen by Scala reflection: def zl_5(zs: List[Z[String]]): List[String] -as seen by Java reflection: public scala.collection.immutable.List a$.zl_5(scala.collection.immutable.List) -result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String -meth = method zl_5 -as seen by Scala reflection: def zl_5(zs: List[Z[String]]): List[String] -as seen by Java reflection: public scala.collection.immutable.List a$.zl_5(scala.collection.immutable.List) -result = List(5) -meth = method zni_7 -as seen by Scala reflection: def zni_7(z: => Z[Int]): Int -as seen by Java reflection: public int a$.zni_7(scala.Function0) -result = 7 -meth = method zns_8 -as seen by Scala reflection: def zns_8(z: => Z[String]): String -as seen by Java reflection: public java.lang.String a$.zns_8(scala.Function0) -result = 8 diff --git a/test/files/run/t6411.scala b/test/files/run/t6411.scala deleted file mode 100644 index 3bfeac2890..0000000000 --- a/test/files/run/t6411.scala +++ /dev/null @@ -1,81 +0,0 @@ -import scala.reflect.runtime.universe._ -import scala.reflect.runtime.{currentMirror => cm} -import scala.language.reflectiveCalls - -class Y[T](val i: T) extends AnyVal { - override def toString = s"Y($i)" -} -class Z[T](val i: T) extends AnyRef { - override def toString = s"Z($i)" -} - -object a { - def yg_1[T](y: Y[T]) = y.i - def yi_2(y: Y[Int]) = y.i - def ys_3(y: Y[String]) = y.i - def ya_4(ys: Array[Y[String]]) = ys.toList.map(_.i) - def yl_5(ys: List[Y[String]]) = ys.map(_.i) - def yv_6(ys: Y[String]*) = ys.toList.map(_.i) - def yni_7(y: => Y[Int]) = y.i - def yns_8(y: => Y[String]) = y.i - - def zg_1[T](z: Z[T]) = z.i - def zi_2(z: Z[Int]) = z.i - def zs_3(z: Z[String]) = z.i - def za_4(zs: Array[Z[String]]) = zs.toList.map(_.i) - def zl_5(zs: List[Z[String]]) = zs.map(_.i) - def zv_6(zs: Z[String]*) = zs.toList.map(_.i) - def zni_7(z: => Z[Int]) = z.i - def zns_8(z: => Z[String]) = z.i -} - -object Test extends App { - def test(methName: String, arg: Any) = { - val moduleA = cm.reflect(a) - val msym = moduleA.symbol.typeSignature.declaration(TermName(methName)).asMethod - println(s"meth = $msym") - val mmirror = moduleA.reflectMethod(msym) - val mresult = - try { mmirror(arg) } - catch { - case ex: Exception => - val ex1 = scala.reflect.runtime.ReflectionUtils.unwrapThrowable(ex) - s"${ex1.getClass}: ${ex1.getMessage}" - } - println(s"as seen by Scala reflection: ${msym.asInstanceOf[scala.reflect.internal.Symbols#Symbol].defString}") - println(s"as seen by Java reflection: ${mmirror.asInstanceOf[{val jmeth: java.lang.reflect.Method}].jmeth}") - println(s"result = $mresult") - } - - test("yg_1", new Y(1)) - test("yg_1", new Y("1")) - test("yi_2", new Y(2)) - test("yi_2", new Y("2")) - test("ys_3", new Y(3)) - test("ys_3", new Y("3")) - test("ya_4", Array(new Y(4))) - test("ya_4", Array(new Y("4"))) - test("yl_5", List(new Y(5))) - test("yl_5", List(new Y("5"))) - // FIXME: disabled because of SI-7056 - // test("yv_6", new Y(6)) - // test("yv_6", new Y("6")) - test("yni_7", new Y(7)) - test("yns_8", new Y("8")) - - test("zg_1", new Z(1)) - test("zg_1", new Z("1")) - test("zi_2", new Z(2)) - test("zi_2", new Z("2")) - test("zs_3", new Z(3)) - test("zs_3", new Z("3")) - test("za_4", Array(new Z(4))) - test("za_4", Array(new Z("4"))) - test("zl_5", List(new Z(5))) - test("zl_5", List(new Z("5"))) - // FIXME: disabled because of SI-7056 - // test("zv_6", new Z(6)) - // test("zv_6", new Z("6")) - test("zni_7", new Z(7)) - test("zns_8", new Z("8")) -} \ No newline at end of file diff --git a/test/files/run/t6411a.check b/test/files/run/t6411a.check new file mode 100644 index 0000000000..9226146195 --- /dev/null +++ b/test/files/run/t6411a.check @@ -0,0 +1,96 @@ +meth = method yg_1 +as seen by Scala reflection: def yg_1[T](y: Y[T]): T +as seen by Java reflection: public java.lang.Object a$.yg_1(java.lang.Object) +result = 1 +meth = method yg_1 +as seen by Scala reflection: def yg_1[T](y: Y[T]): T +as seen by Java reflection: public java.lang.Object a$.yg_1(java.lang.Object) +result = 1 +meth = method yi_2 +as seen by Scala reflection: def yi_2(y: Y[Int]): Int +as seen by Java reflection: public int a$.yi_2(java.lang.Integer) +result = 2 +meth = method yi_2 +as seen by Scala reflection: def yi_2(y: Y[Int]): Int +as seen by Java reflection: public int a$.yi_2(java.lang.Integer) +result = class java.lang.IllegalArgumentException: argument type mismatch +meth = method ys_3 +as seen by Scala reflection: def ys_3(y: Y[String]): String +as seen by Java reflection: public java.lang.String a$.ys_3(java.lang.String) +result = class java.lang.IllegalArgumentException: argument type mismatch +meth = method ys_3 +as seen by Scala reflection: def ys_3(y: Y[String]): String +as seen by Java reflection: public java.lang.String a$.ys_3(java.lang.String) +result = 3 +meth = method ya_4 +as seen by Scala reflection: def ya_4(ys: Array[Y[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.ya_4(Y[]) +result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String +meth = method ya_4 +as seen by Scala reflection: def ya_4(ys: Array[Y[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.ya_4(Y[]) +result = List(4) +meth = method yl_5 +as seen by Scala reflection: def yl_5(ys: List[Y[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.yl_5(scala.collection.immutable.List) +result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String +meth = method yl_5 +as seen by Scala reflection: def yl_5(ys: List[Y[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.yl_5(scala.collection.immutable.List) +result = List(5) +meth = method yni_7 +as seen by Scala reflection: def yni_7(y: => Y[Int]): Int +as seen by Java reflection: public int a$.yni_7(scala.Function0) +result = 7 +meth = method yns_8 +as seen by Scala reflection: def yns_8(y: => Y[String]): String +as seen by Java reflection: public java.lang.String a$.yns_8(scala.Function0) +result = 8 +meth = method zg_1 +as seen by Scala reflection: def zg_1[T](z: Z[T]): T +as seen by Java reflection: public java.lang.Object a$.zg_1(Z) +result = 1 +meth = method zg_1 +as seen by Scala reflection: def zg_1[T](z: Z[T]): T +as seen by Java reflection: public java.lang.Object a$.zg_1(Z) +result = 1 +meth = method zi_2 +as seen by Scala reflection: def zi_2(z: Z[Int]): Int +as seen by Java reflection: public int a$.zi_2(Z) +result = 2 +meth = method zi_2 +as seen by Scala reflection: def zi_2(z: Z[Int]): Int +as seen by Java reflection: public int a$.zi_2(Z) +result = class java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer +meth = method zs_3 +as seen by Scala reflection: def zs_3(z: Z[String]): String +as seen by Java reflection: public java.lang.String a$.zs_3(Z) +result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String +meth = method zs_3 +as seen by Scala reflection: def zs_3(z: Z[String]): String +as seen by Java reflection: public java.lang.String a$.zs_3(Z) +result = 3 +meth = method za_4 +as seen by Scala reflection: def za_4(zs: Array[Z[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.za_4(Z[]) +result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String +meth = method za_4 +as seen by Scala reflection: def za_4(zs: Array[Z[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.za_4(Z[]) +result = List(4) +meth = method zl_5 +as seen by Scala reflection: def zl_5(zs: List[Z[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.zl_5(scala.collection.immutable.List) +result = class java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String +meth = method zl_5 +as seen by Scala reflection: def zl_5(zs: List[Z[String]]): List[String] +as seen by Java reflection: public scala.collection.immutable.List a$.zl_5(scala.collection.immutable.List) +result = List(5) +meth = method zni_7 +as seen by Scala reflection: def zni_7(z: => Z[Int]): Int +as seen by Java reflection: public int a$.zni_7(scala.Function0) +result = 7 +meth = method zns_8 +as seen by Scala reflection: def zns_8(z: => Z[String]): String +as seen by Java reflection: public java.lang.String a$.zns_8(scala.Function0) +result = 8 diff --git a/test/files/run/t6411a.scala b/test/files/run/t6411a.scala new file mode 100644 index 0000000000..3bfeac2890 --- /dev/null +++ b/test/files/run/t6411a.scala @@ -0,0 +1,81 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.language.reflectiveCalls + +class Y[T](val i: T) extends AnyVal { + override def toString = s"Y($i)" +} +class Z[T](val i: T) extends AnyRef { + override def toString = s"Z($i)" +} + +object a { + def yg_1[T](y: Y[T]) = y.i + def yi_2(y: Y[Int]) = y.i + def ys_3(y: Y[String]) = y.i + def ya_4(ys: Array[Y[String]]) = ys.toList.map(_.i) + def yl_5(ys: List[Y[String]]) = ys.map(_.i) + def yv_6(ys: Y[String]*) = ys.toList.map(_.i) + def yni_7(y: => Y[Int]) = y.i + def yns_8(y: => Y[String]) = y.i + + def zg_1[T](z: Z[T]) = z.i + def zi_2(z: Z[Int]) = z.i + def zs_3(z: Z[String]) = z.i + def za_4(zs: Array[Z[String]]) = zs.toList.map(_.i) + def zl_5(zs: List[Z[String]]) = zs.map(_.i) + def zv_6(zs: Z[String]*) = zs.toList.map(_.i) + def zni_7(z: => Z[Int]) = z.i + def zns_8(z: => Z[String]) = z.i +} + +object Test extends App { + def test(methName: String, arg: Any) = { + val moduleA = cm.reflect(a) + val msym = moduleA.symbol.typeSignature.declaration(TermName(methName)).asMethod + println(s"meth = $msym") + val mmirror = moduleA.reflectMethod(msym) + val mresult = + try { mmirror(arg) } + catch { + case ex: Exception => + val ex1 = scala.reflect.runtime.ReflectionUtils.unwrapThrowable(ex) + s"${ex1.getClass}: ${ex1.getMessage}" + } + println(s"as seen by Scala reflection: ${msym.asInstanceOf[scala.reflect.internal.Symbols#Symbol].defString}") + println(s"as seen by Java reflection: ${mmirror.asInstanceOf[{val jmeth: java.lang.reflect.Method}].jmeth}") + println(s"result = $mresult") + } + + test("yg_1", new Y(1)) + test("yg_1", new Y("1")) + test("yi_2", new Y(2)) + test("yi_2", new Y("2")) + test("ys_3", new Y(3)) + test("ys_3", new Y("3")) + test("ya_4", Array(new Y(4))) + test("ya_4", Array(new Y("4"))) + test("yl_5", List(new Y(5))) + test("yl_5", List(new Y("5"))) + // FIXME: disabled because of SI-7056 + // test("yv_6", new Y(6)) + // test("yv_6", new Y("6")) + test("yni_7", new Y(7)) + test("yns_8", new Y("8")) + + test("zg_1", new Z(1)) + test("zg_1", new Z("1")) + test("zi_2", new Z(2)) + test("zi_2", new Z("2")) + test("zs_3", new Z(3)) + test("zs_3", new Z("3")) + test("za_4", Array(new Z(4))) + test("za_4", Array(new Z("4"))) + test("zl_5", List(new Z(5))) + test("zl_5", List(new Z("5"))) + // FIXME: disabled because of SI-7056 + // test("zv_6", new Z(6)) + // test("zv_6", new Z("6")) + test("zni_7", new Z(7)) + test("zns_8", new Z("8")) +} \ No newline at end of file diff --git a/test/files/run/t6411b.check b/test/files/run/t6411b.check new file mode 100644 index 0000000000..e20bed6d8d --- /dev/null +++ b/test/files/run/t6411b.check @@ -0,0 +1 @@ +Bar(Foo(3)) diff --git a/test/files/run/t6411b.scala b/test/files/run/t6411b.scala new file mode 100644 index 0000000000..af30108826 --- /dev/null +++ b/test/files/run/t6411b.scala @@ -0,0 +1,12 @@ +import scala.reflect.runtime.universe._ + +case class Foo(n: Int) extends AnyVal +case class Bar(foo: Foo) + +object Test extends App { + val mirror = runtimeMirror(getClass.getClassLoader) + val cm = mirror.reflectClass(typeOf[Bar].typeSymbol.asClass) + val ctor = typeOf[Bar].declaration(nme.CONSTRUCTOR).asMethod + val ctorm = cm.reflectConstructor(ctor) + println(ctorm(Foo(3))) +} \ No newline at end of file -- cgit v1.2.3 From 6c7ceb611f574a578a67c2cd653b211e555b7898 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sat, 25 Jan 2014 09:45:51 +0300 Subject: SI-7328 FieldMirrors now support value classes --- .../scala/reflect/runtime/JavaMirrors.scala | 107 +++++++++++++-------- .../files/run/reflection-magicsymbols-invoke.check | 2 +- test/files/run/t7328.check | 4 + test/files/run/t7328.scala | 18 ++++ 4 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 test/files/run/t7328.check create mode 100644 test/files/run/t7328.scala (limited to 'test') diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 84f8b5991b..854c9ddc10 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -249,7 +249,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } def reflectMethod(method: MethodSymbol): MethodMirror = { checkMemberOf(method, symbol) - mkJavaMethodMirror(instance, method) + mkMethodMirror(instance, method) } def reflectClass(cls: ClassSymbol): ClassMirror = { if (cls.isStatic) ErrorStaticClass(cls) @@ -264,16 +264,35 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni override def toString = s"instance mirror for $instance" } - private class JavaFieldMirror(val receiver: Any, val symbol: TermSymbol) + // caches value class metadata, so that we minimize the work that needs to be done during Mirror.apply + private class DerivedValueClassMetadata(info: Type) { + val symbol = info.typeSymbol + val isDerivedValueClass = symbol.isDerivedValueClass + lazy val boxer = runtimeClass(symbol.toType).getDeclaredConstructors().head + lazy val unboxer = { + val fields @ (field :: _) = symbol.toType.declarations.collect{ case ts: TermSymbol if ts.isParamAccessor && ts.isMethod => ts }.toList + assert(fields.length == 1, s"$symbol: $fields") + runtimeClass(symbol.asClass).getDeclaredMethod(field.name.toString) + } + } + + private class JavaFieldMirror(val receiver: Any, val symbol: TermSymbol, metadata: DerivedValueClassMetadata) extends FieldMirror { + def this(receiver: Any, symbol: TermSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.info)) + def bind(newReceiver: Any) = new JavaFieldMirror(newReceiver, symbol, metadata) + import metadata._ + lazy val jfield = ensureAccessible(fieldToJava(symbol)) - def get = jfield get receiver + def get = { + val value = jfield get receiver + if (isDerivedValueClass) boxer.newInstance(value) else value + } def set(value: Any) = { // it appears useful to be able to set values of vals, therefore I'm disabling this check // if (!symbol.isMutable) ErrorSetImmutableField(symbol) - jfield.set(receiver, value) + jfield.set(receiver, if (isDerivedValueClass) unboxer.invoke(value) else value) } - def bind(newReceiver: Any) = new JavaFieldMirror(newReceiver, symbol) + override def toString = s"field mirror for ${symbol.fullName} (bound to $receiver)" } @@ -321,9 +340,9 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni // that's because we want to have decent performance // therefore we move special cases into separate subclasses // rather than have them on a hot path them in a unified implementation of the `apply` method - private def mkJavaMethodMirror[T: ClassTag](receiver: T, symbol: MethodSymbol): JavaMethodMirror = { + private def mkMethodMirror[T: ClassTag](receiver: T, symbol: MethodSymbol): MethodMirror = { def existsParam(pred: Type => Boolean) = symbol.paramss.flatten.map(_.info).exists(pred) - if (isBytecodelessMethod(symbol)) new JavaBytecodelessMethodMirror(receiver, symbol) + if (isBytecodelessMethod(symbol)) new BytecodelessMethodMirror(receiver, symbol) else if (existsParam(isByNameParam) || existsParam(isValueClassParam)) new JavaTransformingMethodMirror(receiver, symbol) else { symbol.paramss.flatten.length match { @@ -337,7 +356,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } } - private abstract class JavaMethodMirror(val symbol: MethodSymbol) extends MethodMirror { + private abstract class JavaMethodMirror(val symbol: MethodSymbol, protected val ret: DerivedValueClassMetadata) extends MethodMirror { lazy val jmeth = ensureAccessible(methodToJava(symbol)) lazy val jconstr = ensureAccessible(constructorToJava(symbol)) @@ -348,6 +367,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def jinvoke(args: Seq[Any]): Any = { val result = jinvokeraw(args) if (!symbol.isConstructor && jmeth.getReturnType == java.lang.Void.TYPE) () + else if (!symbol.isConstructor && ret.isDerivedValueClass) ret.boxer.newInstance(result.asInstanceOf[AnyRef]) else result } @@ -357,51 +377,57 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } } - private class JavaVanillaMethodMirror(val receiver: Any, symbol: MethodSymbol) - extends JavaMethodMirror(symbol) { - def bind(newReceiver: Any) = new JavaVanillaMethodMirror(newReceiver, symbol) + private class JavaVanillaMethodMirror(val receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaMethodMirror(symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + def bind(newReceiver: Any) = new JavaVanillaMethodMirror(newReceiver, symbol, ret) def apply(args: Any*): Any = jinvoke(args) } - private class JavaVanillaMethodMirror0(receiver: Any, symbol: MethodSymbol) - extends JavaVanillaMethodMirror(receiver, symbol) { - override def bind(newReceiver: Any) = new JavaVanillaMethodMirror0(newReceiver, symbol) + private class JavaVanillaMethodMirror0(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaVanillaMethodMirror(receiver, symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror0(newReceiver, symbol, ret) override def jinvokeraw(args: Seq[Any]) = if (!symbol.isConstructor) jmeth.invoke(receiver) else if (receiver == null) jconstr.newInstance() else jconstr.newInstance(receiver.asInstanceOf[AnyRef]) } - private class JavaVanillaMethodMirror1(receiver: Any, symbol: MethodSymbol) - extends JavaVanillaMethodMirror(receiver, symbol) { - override def bind(newReceiver: Any) = new JavaVanillaMethodMirror1(newReceiver, symbol) + private class JavaVanillaMethodMirror1(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaVanillaMethodMirror(receiver, symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror1(newReceiver, symbol, ret) override def jinvokeraw(args: Seq[Any]) = if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef]) else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef]) else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef]) } - private class JavaVanillaMethodMirror2(receiver: Any, symbol: MethodSymbol) - extends JavaVanillaMethodMirror(receiver, symbol) { - override def bind(newReceiver: Any) = new JavaVanillaMethodMirror2(newReceiver, symbol) + private class JavaVanillaMethodMirror2(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaVanillaMethodMirror(receiver, symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror2(newReceiver, symbol, ret) override def jinvokeraw(args: Seq[Any]) = if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef]) } - private class JavaVanillaMethodMirror3(receiver: Any, symbol: MethodSymbol) - extends JavaVanillaMethodMirror(receiver, symbol) { - override def bind(newReceiver: Any) = new JavaVanillaMethodMirror3(newReceiver, symbol) + private class JavaVanillaMethodMirror3(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaVanillaMethodMirror(receiver, symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror3(newReceiver, symbol, ret) override def jinvokeraw(args: Seq[Any]) = if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) else jconstr.newInstance(receiver.asInstanceOf[AnyRef], args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef]) } - private class JavaVanillaMethodMirror4(receiver: Any, symbol: MethodSymbol) - extends JavaVanillaMethodMirror(receiver, symbol) { - override def bind(newReceiver: Any) = new JavaVanillaMethodMirror4(newReceiver, symbol) + private class JavaVanillaMethodMirror4(receiver: Any, symbol: MethodSymbol, ret: DerivedValueClassMetadata) + extends JavaVanillaMethodMirror(receiver, symbol, ret) { + def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new DerivedValueClassMetadata(symbol.returnType)) + override def bind(newReceiver: Any) = new JavaVanillaMethodMirror4(newReceiver, symbol, ret) override def jinvokeraw(args: Seq[Any]) = if (!symbol.isConstructor) jmeth.invoke(receiver, args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) else if (receiver == null) jconstr.newInstance(args(0).asInstanceOf[AnyRef], args(1).asInstanceOf[AnyRef], args(2).asInstanceOf[AnyRef], args(3).asInstanceOf[AnyRef]) @@ -412,41 +438,40 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni // TODO: vararg is only supported in the last parameter list (SI-6182), so we don't need to worry about the rest for now private class MethodMetadata(symbol: MethodSymbol) { private val params = symbol.paramss.flatten.toArray + private val vcMetadata = params.map(p => new DerivedValueClassMetadata(p.info)) val isByName = params.map(p => isByNameParam(p.info)) - val isValueClass = params.map(p => isValueClassParam(p.info)) - private def valueClassUnboxer(p: Symbol) = { - val fields @ (field :: _) = p.info.declarations.collect{ case ts: TermSymbol if ts.isParamAccessor && ts.isMethod => ts }.toList - assert(fields.length == 1, s"$p: $fields") - runtimeClass(p.info).getDeclaredMethod(field.name.toString) - } - val paramUnboxers = params.map(p => if (isValueClassParam(p.info)) valueClassUnboxer(p) else null) + def isDerivedValueClass(i: Int) = vcMetadata(i).isDerivedValueClass + def paramUnboxers(i: Int) = vcMetadata(i).unboxer val paramCount = params.length + val ret = new DerivedValueClassMetadata(symbol.returnType) } private class JavaTransformingMethodMirror(val receiver: Any, symbol: MethodSymbol, metadata: MethodMetadata) - extends JavaMethodMirror(symbol) { + extends JavaMethodMirror(symbol, metadata.ret) { def this(receiver: Any, symbol: MethodSymbol) = this(receiver, symbol, new MethodMetadata(symbol)) override def bind(newReceiver: Any) = new JavaTransformingMethodMirror(newReceiver, symbol, metadata) + import metadata._ def apply(args: Any*): Any = { - import metadata._ val args1 = new Array[Any](args.length) var i = 0 while (i < args1.length) { val arg = args(i) if (i >= paramCount) args1(i) = arg // don't transform varargs else if (isByName(i)) args1(i) = () => arg // don't transform by-name value class params - else if (isValueClass(i)) args1(i) = paramUnboxers(i).invoke(arg) + else if (isDerivedValueClass(i)) args1(i) = paramUnboxers(i).invoke(arg) i += 1 } jinvoke(args1) } } - private class JavaBytecodelessMethodMirror[T: ClassTag](val receiver: T, symbol: MethodSymbol) - extends JavaMethodMirror(symbol) { - def bind(newReceiver: Any) = new JavaBytecodelessMethodMirror(newReceiver.asInstanceOf[T], symbol) - def apply(args: Any*): Any = { + private class BytecodelessMethodMirror[T: ClassTag](val receiver: T, val symbol: MethodSymbol) + extends MethodMirror { + def bind(newReceiver: Any) = new BytecodelessMethodMirror(newReceiver.asInstanceOf[T], symbol) + override def toString = s"bytecodeless method mirror for ${showMethodSig(symbol)} (bound to $receiver)" + + def apply(args: Any*): Any = { // checking type conformance is too much of a hassle, so we don't do it here // actually it's not even necessary, because we manually dispatch arguments below val params = symbol.paramss.flatten @@ -515,7 +540,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def isStatic = false def reflectConstructor(constructor: MethodSymbol) = { checkConstructorOf(constructor, symbol) - mkJavaMethodMirror(outer, constructor) + mkMethodMirror(outer, constructor) } override def toString = s"class mirror for ${symbol.fullName} (bound to $outer)" } diff --git a/test/files/run/reflection-magicsymbols-invoke.check b/test/files/run/reflection-magicsymbols-invoke.check index 7f9767f095..43116858de 100644 --- a/test/files/run/reflection-magicsymbols-invoke.check +++ b/test/files/run/reflection-magicsymbols-invoke.check @@ -28,7 +28,7 @@ it's important to print the list of AnyVal's members if some of them change (possibly, adding and/or removing magic symbols), we must update this test constructor AnyVal: ()AnyVal method getClass: ()Class[_ <: AnyVal] -testing AnyVal.: class scala.ScalaReflectionException: unsupported symbol constructor AnyVal when invoking constructor mirror for scala.AnyVal.(): AnyVal (bound to null) +testing AnyVal.: class scala.ScalaReflectionException: unsupported symbol constructor AnyVal when invoking bytecodeless method mirror for scala.AnyVal.(): AnyVal (bound to null) testing AnyVal.getClass: class scala.ScalaReflectionException: expected a member of class Integer, you provided method scala.AnyVal.getClass ============ AnyRef diff --git a/test/files/run/t7328.check b/test/files/run/t7328.check new file mode 100644 index 0000000000..e386fe70d9 --- /dev/null +++ b/test/files/run/t7328.check @@ -0,0 +1,4 @@ +Foo +Foo(3) +Foo(3) +Foo(5) diff --git a/test/files/run/t7328.scala b/test/files/run/t7328.scala new file mode 100644 index 0000000000..8816fa2347 --- /dev/null +++ b/test/files/run/t7328.scala @@ -0,0 +1,18 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} + +case class Foo(x: Int) extends AnyVal +case class Bar(foo: Foo) + +object Test extends App { + val foo = typeOf[Bar].declaration(TermName("foo")).asMethod + println(foo.returnType) // Foo + + val bar = Bar(Foo(3)) + println(bar.foo) // Foo(3) + + val im = cm.reflect(bar) + println(im.reflectField(foo).get) // incorrectly gives java.lang.Integer(3) not Foo(3) + im.reflectField(foo).set(Foo(5)) // java.lang.IllegalArgumentException: Can not set int field Bar.foo to Foo + println(im.reflectMethod(foo)()) // incorrectly gives java.lang.Integer(3) not Foo(3) +} \ No newline at end of file -- cgit v1.2.3