summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2012-08-04 11:08:10 +0200
committerEugene Burmako <xeno.by@gmail.com>2012-08-06 23:09:30 +0200
commit432d7b86cb7c46d0415b8c06bf8045e309c63f03 (patch)
treec8d768c86d849a5cddf59f2ef6fce8de889b1310
parent114367c0b2ce5f48186d4270c1724090fd77877b (diff)
downloadscala-432d7b86cb7c46d0415b8c06bf8045e309c63f03.tar.gz
scala-432d7b86cb7c46d0415b8c06bf8045e309c63f03.tar.bz2
scala-432d7b86cb7c46d0415b8c06bf8045e309c63f03.zip
SI-6178 reflective invocation of magic symbols
In Scala there are some methods that only exist in symbol tables, but don't have corresponding method entries in Java class files. To the best of my knowledge, these methods can be subdivided into five groups: 1) stuff weaved onto Any, AnyVal and AnyRef (aka Object), 2) magic methods that Scala exposes to fix Java arrays, 3) magic methods declared on Scala primitive value classes, 4) compile-time methods (such as classOf and all kinds of macros), 5) miscellaneous stuff (currently only String_+). To support these magic symbols, I've modified the `checkMemberOf` validator to special case Any/AnyVal/AnyRef methods and adjusted MethodMirror and ConstructorMirror classes to use special invokers for those instead of relying on Java reflection. Support for value classes will arrive in the subsequent commit, because it requires some unrelated changes to the mirror API (currently mirrors only support AnyRefs as their targets).
-rw-r--r--src/reflect/scala/reflect/runtime/JavaMirrors.scala110
-rw-r--r--test/files/run/reflection-magicsymbols-invoke.check124
-rw-r--r--test/files/run/reflection-magicsymbols-invoke.scala94
-rw-r--r--test/files/run/t6178.check1
-rw-r--r--test/files/run/t6178.scala7
5 files changed, 320 insertions, 16 deletions
diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
index 5eb7770de6..83fbee97cc 100644
--- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala
+++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
@@ -20,6 +20,7 @@ import internal.Flags._
//import scala.tools.nsc.util.ScalaClassLoader._
import ReflectionUtils.{singletonInstance}
import language.existentials
+import scala.runtime.ScalaRunTime
trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: SymbolTable =>
@@ -152,8 +153,15 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
def moduleSymbol(rtcls: RuntimeClass): ModuleSymbol = classToScala(rtcls).companionModule.asModule
- private def checkMemberOf(wannabe: Symbol, owner: Symbol) =
- if (!owner.info.member(wannabe.name).alternatives.contains(wannabe)) ErrorNotMember(wannabe, owner)
+ private def checkMemberOf(wannabe: Symbol, owner: ClassSymbol) {
+ if (wannabe.owner == AnyClass || wannabe.owner == AnyRefClass || wannabe.owner == ObjectClass) {
+ // do nothing
+ } else if (wannabe.owner == AnyValClass) {
+ if (!owner.isPrimitiveValueClass && !owner.isDerivedValueClass) ErrorNotMember(wannabe, owner)
+ } else {
+ if (!owner.info.member(wannabe.name).alternatives.contains(wannabe)) ErrorNotMember(wannabe, owner)
+ }
+ }
private class JavaInstanceMirror(obj: AnyRef)
extends InstanceMirror {
@@ -175,7 +183,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
}
def reflectMethod(method: MethodSymbol): MethodMirror = {
checkMemberOf(method, symbol)
- new JavaMethodMirror(obj, method)
+ mkJavaMethodMirror(obj, method)
}
def reflectClass(cls: ClassSymbol): ClassMirror = {
if (cls.isStatic) ErrorStaticClass(cls)
@@ -230,26 +238,93 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
sig
}
- private class JavaMethodMirror(val receiver: AnyRef, val symbol: MethodSymbol)
+ // the "symbol == Any_getClass || symbol == Object_getClass" test doesn't cut it
+ // because both AnyVal and its primitive descendants define their own getClass methods
+ private def isGetClass(meth: MethodSymbol) = meth.name.toString == "getClass" && meth.params.flatten.isEmpty
+ private def isMagicPrimitiveMethod(meth: MethodSymbol) = meth.owner.isPrimitiveValueClass
+ private def isStringConcat(meth: MethodSymbol) = meth == String_+ || (isMagicPrimitiveMethod(meth) && meth.returnType =:= StringClass.toType)
+ lazy val magicMethodOwners = Set[Symbol](AnyClass, AnyValClass, AnyRefClass, ObjectClass, ArrayClass) ++ ScalaPrimitiveValueClasses
+ lazy val nonMagicObjectMethods = Set[Symbol](Object_clone, Object_equals, Object_finalize, Object_hashCode, Object_toString,
+ Object_notify, Object_notifyAll) ++ ObjectClass.info.member(nme.wait_).asTerm.alternatives.map(_.asMethod)
+ private def isMagicMethod(meth: MethodSymbol): Boolean = {
+ if (isGetClass(meth) || isStringConcat(meth) || isMagicPrimitiveMethod(meth) || meth == Predef_classOf || meth.isTermMacro) return true
+ magicMethodOwners(meth.owner) && !nonMagicObjectMethods(meth)
+ }
+
+ // unlike other mirrors, method mirrors are created by a factory
+ // 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(receiver: AnyRef, symbol: MethodSymbol): JavaMethodMirror = {
+ if (isMagicMethod(symbol)) new JavaMagicMethodMirror(receiver, symbol)
+ else new JavaVanillaMethodMirror(receiver, symbol)
+ }
+
+ private abstract class JavaMethodMirror(val receiver: AnyRef, val symbol: MethodSymbol)
extends MethodMirror {
lazy val jmeth = {
val jmeth = methodToJava(symbol)
if (!jmeth.isAccessible) jmeth.setAccessible(true)
jmeth
}
- def apply(args: Any*): Any =
- if (symbol.owner == ArrayClass)
- symbol.name match {
- case nme.length => jArray.getLength(receiver)
- case nme.apply => jArray.get(receiver, args(0).asInstanceOf[Int])
- case nme.update => jArray.set(receiver, args(0).asInstanceOf[Int], args(1))
- case _ => assert(false, s"unexpected array method: $symbol")
- }
- else
- jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*)
+
override def toString = s"method mirror for ${showMethodSig(symbol)} (bound to $receiver)"
}
+ private class JavaVanillaMethodMirror(receiver: AnyRef, symbol: MethodSymbol)
+ extends JavaMethodMirror(receiver, symbol) {
+ def apply(args: Any*): Any = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*)
+ }
+
+ private class JavaMagicMethodMirror(receiver: AnyRef, symbol: MethodSymbol)
+ extends JavaMethodMirror(receiver, symbol) {
+ 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 to magic methods below
+ val params = symbol.paramss.flatten
+ val perfectMatch = args.length == params.length
+ // todo. this doesn't account for multiple vararg parameter lists
+ // however those aren't supported by the mirror API: https://issues.scala-lang.org/browse/SI-6182
+ // hence I leave this code as is, to be fixed when the corresponding bug is fixed
+ val varargMatch = args.length >= params.length - 1 && isVarArgsList(params)
+ if (!perfectMatch && !varargMatch) {
+ val n_arguments = if (isVarArgsList(params)) s"${params.length - 1} or more" else s"${params.length}"
+ var s_arguments = if (params.length == 1 && !isVarArgsList(params)) "argument" else "arguments"
+ throw new ScalaReflectionException(s"${showMethodSig(symbol)} takes $n_arguments $s_arguments")
+ }
+
+ def objArg0 = args(0).asInstanceOf[AnyRef]
+ def objArgs = args.asInstanceOf[Seq[AnyRef]]
+ def fail(msg: String) = throw new ScalaReflectionException(msg + ", it cannot be invoked with mirrors")
+
+ symbol match {
+ case Any_== | Object_== => ScalaRunTime.inlinedEquals(receiver, objArg0)
+ case Any_!= | Object_!= => !ScalaRunTime.inlinedEquals(receiver, objArg0)
+ case Any_## | Object_## => ScalaRunTime.hash(receiver)
+ case Any_equals => receiver.equals(objArg0)
+ case Any_hashCode => receiver.hashCode
+ case Any_toString => receiver.toString
+ case Object_eq => receiver eq objArg0
+ case Object_ne => receiver ne objArg0
+ case Object_synchronized => receiver.synchronized(objArg0)
+ case sym if isGetClass(sym) => receiver.getClass
+ case Any_asInstanceOf => fail("Any.asInstanceOf requires a type argument")
+ case Any_isInstanceOf => fail("Any.isInstanceOf requires a type argument")
+ case Object_asInstanceOf => fail("AnyRef.$asInstanceOf is an internal method")
+ case Object_isInstanceOf => fail("AnyRef.$isInstanceOf is an internal method")
+ case Array_length => ScalaRunTime.array_length(receiver)
+ case Array_apply => ScalaRunTime.array_apply(receiver, args(0).asInstanceOf[Int])
+ case Array_update => ScalaRunTime.array_update(receiver, args(0).asInstanceOf[Int], args(1))
+ case Array_clone => ScalaRunTime.array_clone(receiver)
+ case sym if isStringConcat(sym) => receiver.toString + objArg0
+ case sym if isMagicPrimitiveMethod(sym) => fail("implementation restriction: ${symbol.fullName} is a magic primitive method")
+ case sym if sym == Predef_classOf => fail("Predef.classOf is a compile-time function")
+ case sym if sym.isTermMacro => fail(s"${symbol.fullName} is a macro, i.e. a compile-time function")
+ case _ => assert(false, this)
+ }
+ }
+ }
+
private class JavaConstructorMirror(val outer: AnyRef, val symbol: MethodSymbol)
extends MethodMirror {
override val receiver = outer
@@ -259,6 +334,9 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
jconstr
}
def apply(args: Any*): Any = {
+ if (symbol.owner == ArrayClass)
+ throw new ScalaReflectionException("Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag(<class of element>).newArray(<length>)` instead")
+
val effectiveArgs =
if (outer == null) args.asInstanceOf[Seq[AnyRef]]
else outer +: args.asInstanceOf[Seq[AnyRef]]
@@ -1067,7 +1145,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
mirrors(rootToLoader getOrElseUpdate(root, findLoader)).get.get
}
- private lazy val magicSymbols: Map[(String, Name), Symbol] = {
+ private lazy val magicClasses: Map[(String, Name), Symbol] = {
def mapEntry(sym: Symbol): ((String, Name), Symbol) = (sym.owner.fullName, sym.name) -> sym
Map() ++ (definitions.magicSymbols filter (_.isType) map mapEntry)
}
@@ -1088,7 +1166,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym
if (name.isTermName && !owner.isEmptyPackageClass)
return mirror.makeScalaPackage(
if (owner.isRootSymbol) name.toString else owner.fullName+"."+name)
- magicSymbols get (owner.fullName, name) match {
+ magicClasses get (owner.fullName, name) match {
case Some(tsym) =>
owner.info.decls enter tsym
return tsym
diff --git a/test/files/run/reflection-magicsymbols-invoke.check b/test/files/run/reflection-magicsymbols-invoke.check
new file mode 100644
index 0000000000..a180ed806e
--- /dev/null
+++ b/test/files/run/reflection-magicsymbols-invoke.check
@@ -0,0 +1,124 @@
+============
+Any
+it's important to print the list of Any's members
+if some of them change (possibly, adding and/or removing magic symbols), we must update this test
+method !=: (x$1: Any)Boolean
+method ##: ()Int
+method ==: (x$1: Any)Boolean
+method asInstanceOf: [T0]=> T0
+method equals: (x$1: Any)Boolean
+method getClass: ()java.lang.Class[_]
+method hashCode: ()Int
+method isInstanceOf: [T0]=> Boolean
+method toString: ()java.lang.String
+testing Any.!=: false
+testing Any.##: 50
+testing Any.==: true
+testing Any.asInstanceOf: class scala.ScalaReflectionException: Any.asInstanceOf requires a type argument, it cannot be invoked with mirrors
+testing Any.asInstanceOf: class scala.ScalaReflectionException: scala.Any.asInstanceOf[T0]: T0 takes 0 arguments
+testing Any.equals: true
+testing Any.getClass: class java.lang.String
+testing Any.hashCode: 50
+testing Any.isInstanceOf: class scala.ScalaReflectionException: Any.isInstanceOf requires a type argument, it cannot be invoked with mirrors
+testing Any.isInstanceOf: class scala.ScalaReflectionException: scala.Any.isInstanceOf[T0]: Boolean takes 0 arguments
+testing Any.toString: 2
+============
+AnyVal
+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.<init>: class java.lang.InstantiationException: null
+testing AnyVal.getClass: class scala.ScalaReflectionException: expected a member of class Integer, you provided method scala.AnyVal.getClass
+============
+AnyRef
+it's important to print the list of AnyRef's members
+if some of them change (possibly, adding and/or removing magic symbols), we must update this test
+constructor Object: ()java.lang.Object
+method !=: (x$1: Any)Boolean
+method !=: (x$1: AnyRef)Boolean
+method ##: ()Int
+method $asInstanceOf: [T0]()T0
+method $isInstanceOf: [T0]()Boolean
+method ==: (x$1: Any)Boolean
+method ==: (x$1: AnyRef)Boolean
+method asInstanceOf: [T0]=> T0
+method clone: ()java.lang.Object
+method eq: (x$1: AnyRef)Boolean
+method equals: (x$1: Any)Boolean
+method finalize: ()Unit
+method getClass: ()java.lang.Class[_]
+method hashCode: ()Int
+method isInstanceOf: [T0]=> Boolean
+method ne: (x$1: AnyRef)Boolean
+method notify: ()Unit
+method notifyAll: ()Unit
+method synchronized: [T0](x$1: T0)T0
+method toString: ()java.lang.String
+method wait: ()Unit
+method wait: (x$1: Long)Unit
+method wait: (x$1: Long, x$2: Int)Unit
+testing Object.!=: false
+testing Object.##: 50
+testing Object.$asInstanceOf: class scala.ScalaReflectionException: AnyRef.$asInstanceOf is an internal method, it cannot be invoked with mirrors
+testing Object.$asInstanceOf: class scala.ScalaReflectionException: java.lang.Object.$asInstanceOf[T0](): T0 takes 0 arguments
+testing Object.$isInstanceOf: class scala.ScalaReflectionException: AnyRef.$isInstanceOf is an internal method, it cannot be invoked with mirrors
+testing Object.$isInstanceOf: class scala.ScalaReflectionException: java.lang.Object.$isInstanceOf[T0](): Boolean takes 0 arguments
+testing Object.==: true
+testing Object.clone: class java.lang.CloneNotSupportedException: java.lang.String
+testing Object.eq: true
+testing Object.equals: true
+testing Object.finalize: null
+testing Object.getClass: class java.lang.String
+testing Object.hashCode: 50
+testing Object.ne: false
+testing Object.notify: class java.lang.IllegalMonitorStateException: null
+testing Object.notifyAll: class java.lang.IllegalMonitorStateException: null
+testing Object.synchronized: 2
+testing Object.toString: 2
+TODO: also test AnyRef.wait overloads
+============
+Array
+it's important to print the list of Array's members
+if some of them change (possibly, adding and/or removing magic symbols), we must update this test
+constructor Array: (_length: Int)Array[T]
+constructor Object: ()java.lang.Object
+method !=: (x$1: Any)Boolean
+method !=: (x$1: AnyRef)Boolean
+method ##: ()Int
+method $asInstanceOf: [T0]()T0
+method $isInstanceOf: [T0]()Boolean
+method ==: (x$1: Any)Boolean
+method ==: (x$1: AnyRef)Boolean
+method apply: (i: <?>)T
+method asInstanceOf: [T0]=> T0
+method clone: ()Array[T]
+method eq: (x$1: AnyRef)Boolean
+method equals: (x$1: Any)Boolean
+method finalize: ()Unit
+method getClass: ()java.lang.Class[_]
+method hashCode: ()Int
+method isInstanceOf: [T0]=> Boolean
+method length: => Int
+method ne: (x$1: AnyRef)Boolean
+method notify: ()Unit
+method notifyAll: ()Unit
+method synchronized: [T0](x$1: T0)T0
+method toString: ()java.lang.String
+method update: (i: <?>, x: <?>)Unit
+method wait: ()Unit
+method wait: (x$1: Long)Unit
+method wait: (x$1: Long, x$2: Int)Unit
+value _length: Int
+testing Array.length: 2
+testing Array.apply: 1
+testing Array.update: ()
+testing Array.clone: List(1, 2)
+============
+Other
+testing String.+: 23
+============
+CTM
+testing Predef.classOf: class scala.ScalaReflectionException: Predef.classOf is a compile-time function, it cannot be invoked with mirrors
+testing Predef.classOf: class scala.ScalaReflectionException: scala.Predef.classOf[T]: Class[T] takes 0 arguments
+testing Universe.reify: class scala.ScalaReflectionException: scala.reflect.base.Universe.reify is a macro, i.e. a compile-time function, it cannot be invoked with mirrors
diff --git a/test/files/run/reflection-magicsymbols-invoke.scala b/test/files/run/reflection-magicsymbols-invoke.scala
new file mode 100644
index 0000000000..61ecc6458d
--- /dev/null
+++ b/test/files/run/reflection-magicsymbols-invoke.scala
@@ -0,0 +1,94 @@
+import scala.reflect.runtime.universe._
+import scala.reflect.runtime.universe.definitions._
+import scala.reflect.runtime.{currentMirror => cm}
+
+object Test extends App {
+ def key(sym: Symbol) = sym + ": " + sym.typeSignature
+ def test(tpe: Type, receiver: Any, method: String, args: Any*) {
+ def wrap[T](op: => T) =
+ try {
+ var result = op.asInstanceOf[AnyRef]
+ if (scala.runtime.ScalaRunTime.isArray(result))
+ result = scala.runtime.ScalaRunTime.toObjectArray(result).toList
+ println(result)
+ } catch {
+ case ex: Throwable =>
+ val realex = scala.reflect.runtime.ReflectionUtils.unwrapThrowable(ex)
+ println(realex.getClass + ": " + realex.getMessage)
+ }
+ print(s"testing ${tpe.typeSymbol.name}.$method: ")
+ wrap({
+ if (method == nme.CONSTRUCTOR.toString) {
+ val ctor = tpe.declaration(nme.CONSTRUCTOR).asMethod
+ cm.reflectClass(ctor.owner.asClass).reflectConstructor(ctor)(args: _*)
+ } else {
+ val meth = tpe.declaration(newTermName(method).encodedName.toTermName).asMethod
+ cm.reflect(receiver).reflectMethod(meth)(args: _*)
+ }
+ })
+ }
+
+ println("============\nAny")
+ println("it's important to print the list of Any's members")
+ println("if some of them change (possibly, adding and/or removing magic symbols), we must update this test")
+ typeOf[Any].members.toList.sortBy(key).foreach(sym => println(key(sym)))
+ test(typeOf[Any], "2", "!=", "2")
+ test(typeOf[Any], "2", "##")
+ test(typeOf[Any], "2", "==", "2")
+ test(typeOf[Any], "2", "asInstanceOf")
+ test(typeOf[Any], "2", "asInstanceOf", typeOf[String])
+ test(typeOf[Any], "2", "equals", "2")
+ test(typeOf[Any], "2", "getClass")
+ test(typeOf[Any], "2", "hashCode")
+ test(typeOf[Any], "2", "isInstanceOf")
+ test(typeOf[Any], "2", "isInstanceOf", typeOf[String])
+ test(typeOf[Any], "2", "toString")
+
+ println("============\nAnyVal")
+ println("it's important to print the list of AnyVal's members")
+ println("if some of them change (possibly, adding and/or removing magic symbols), we must update this test")
+ typeOf[AnyVal].declarations.toList.sortBy(key).foreach(sym => println(key(sym)))
+ test(typeOf[AnyVal], null, "<init>")
+ test(typeOf[AnyVal], 2, "getClass")
+
+ println("============\nAnyRef")
+ println("it's important to print the list of AnyRef's members")
+ println("if some of them change (possibly, adding and/or removing magic symbols), we must update this test")
+ typeOf[AnyRef].members.toList.sortBy(key).foreach(sym => println(key(sym)))
+ test(typeOf[AnyRef], "2", "!=", "2")
+ test(typeOf[AnyRef], "2", "##")
+ test(typeOf[AnyRef], "2", "$asInstanceOf")
+ test(typeOf[AnyRef], "2", "$asInstanceOf", typeOf[String])
+ test(typeOf[AnyRef], "2", "$isInstanceOf")
+ test(typeOf[AnyRef], "2", "$isInstanceOf", typeOf[String])
+ test(typeOf[AnyRef], "2", "==", "2")
+ test(typeOf[AnyRef], "2", "clone")
+ test(typeOf[AnyRef], "2", "eq", "2")
+ test(typeOf[AnyRef], "2", "equals", "2")
+ test(typeOf[AnyRef], "2", "finalize")
+ test(typeOf[AnyRef], "2", "getClass")
+ test(typeOf[AnyRef], "2", "hashCode")
+ test(typeOf[AnyRef], "2", "ne", "2")
+ test(typeOf[AnyRef], "2", "notify")
+ test(typeOf[AnyRef], "2", "notifyAll")
+ test(typeOf[AnyRef], "2", "synchronized", "2")
+ test(typeOf[AnyRef], "2", "toString")
+ println("TODO: also test AnyRef.wait overloads")
+
+ println("============\nArray")
+ println("it's important to print the list of Array's members")
+ println("if some of them change (possibly, adding and/or removing magic symbols), we must update this test")
+ ArrayClass.typeSignature.members.toList.sortBy(key).foreach(sym => println(key(sym)))
+ test(ArrayClass.typeSignature, Array(1, 2), "length")
+ test(ArrayClass.typeSignature, Array(1, 2), "apply", 0)
+ test(ArrayClass.typeSignature, Array(1, 2), "update", 0, 0)
+ test(ArrayClass.typeSignature, Array(1, 2), "clone")
+
+ println("============\nOther")
+ test(typeOf[String], "2", "+", 3)
+
+ println("============\nCTM")
+ test(PredefModule.moduleClass.typeSignature, Predef, "classOf")
+ test(PredefModule.moduleClass.typeSignature, Predef, "classOf", typeOf[String])
+ test(typeOf[scala.reflect.base.Universe], scala.reflect.runtime.universe, "reify", "2")
+} \ No newline at end of file
diff --git a/test/files/run/t6178.check b/test/files/run/t6178.check
new file mode 100644
index 0000000000..d8263ee986
--- /dev/null
+++ b/test/files/run/t6178.check
@@ -0,0 +1 @@
+2 \ No newline at end of file
diff --git a/test/files/run/t6178.scala b/test/files/run/t6178.scala
new file mode 100644
index 0000000000..0b4cf0bbf5
--- /dev/null
+++ b/test/files/run/t6178.scala
@@ -0,0 +1,7 @@
+import scala.reflect.runtime.universe._
+import scala.reflect.runtime.{currentMirror => cm}
+
+object Test extends App {
+ val plus = typeOf[java.lang.String].member(newTermName("$plus")).asMethod
+ println(cm.reflect("").reflectMethod(plus).apply("2"))
+} \ No newline at end of file