summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2009-06-19 14:48:47 +0000
committerPaul Phillips <paulp@improving.org>2009-06-19 14:48:47 +0000
commit2b3218c7886241792a8936d31e509c6edc1ff191 (patch)
treeb065f3c196f750946226f810681ab70823245e8d /src
parent4d5d6fbe94bc7008f7ea1ad645a3e1923da9687d (diff)
downloadscala-2b3218c7886241792a8936d31e509c6edc1ff191.tar.gz
scala-2b3218c7886241792a8936d31e509c6edc1ff191.tar.bz2
scala-2b3218c7886241792a8936d31e509c6edc1ff191.zip
Created scala.reflect.Invocation, which offers ...
Created scala.reflect.Invocation, which offers an easy syntax for reflective method calls, like: "abcdefgh" o 'endsWith("gh") // returns Any val res: Boolean = "abc" oo 'endsWith("z") // casts to expected type
Diffstat (limited to 'src')
-rw-r--r--src/dotnet-library/scala/reflect/Invocation.scala1
-rw-r--r--src/library/scala/reflect/Invocation.scala128
2 files changed, 129 insertions, 0 deletions
diff --git a/src/dotnet-library/scala/reflect/Invocation.scala b/src/dotnet-library/scala/reflect/Invocation.scala
new file mode 100644
index 0000000000..7276fb983b
--- /dev/null
+++ b/src/dotnet-library/scala/reflect/Invocation.scala
@@ -0,0 +1 @@
+/* Invocation.scala does not exist for the dotnet target */ \ No newline at end of file
diff --git a/src/library/scala/reflect/Invocation.scala b/src/library/scala/reflect/Invocation.scala
new file mode 100644
index 0000000000..3e757950d4
--- /dev/null
+++ b/src/library/scala/reflect/Invocation.scala
@@ -0,0 +1,128 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+// $Id$
+
+
+package scala.reflect
+
+import scala.annotation.experimental
+import scala.util.control.Exception.catching
+import java.lang.{ Class => JClass }
+import java.lang.reflect.{ Method => JMethod }
+import scala.{ Symbol => ScalaSymbol }
+
+/** A more convenient syntax for reflective invocation. Example usage:
+ *
+ * class Obj { private def foo(x: Int, y: String): Long = x + y.length }
+ *
+ * You can call it reflectively one of two ways:
+ *
+ * import scala.reflect.Invocation._
+ * (new Obj) o 'foo(5, "abc") // the 'o' method returns Any
+ * val x: Long = (new Obj) oo 'foo(5, "abc") // the 'oo' method casts to expected type.
+ *
+ * If you call the 'oo' method and do not give the type inferencer enough help,
+ * it will most likely infer Nothing, which will result in a ClassCastException.
+ *
+ * @author Paul Phillips
+ *
+ */
+@experimental
+object Invocation
+{
+ /** In order to encapsulate anything to do with reflection, we must
+ * overcome an issue with the boxing of primitives. If we declare a
+ * method which takes arguments of type Any, by the time the method
+ * parameters can be examined, the primitives have already been boxed.
+ * The reflective call will then fail because classOf[java.lang.Integer]
+ * is not the same thing as classOf[scala.Int].
+ *
+ * Any useful workaround will require examining the arguments before
+ * the method is called. The approach here is to define two implicits,
+ * one for AnyRefs and one for AnyVals, and box them in a container
+ * which preserves their original class identity.
+ */
+ trait PrimitivePreserver[T] {
+ val value: T
+ val clazz: JClass[_]
+ }
+ case class PreservedAnyVal[T <: AnyVal](value: T) extends PrimitivePreserver[T] {
+ val clazz = getAnyValClass(value)
+ }
+ case class PreservedAnyRef[T <: AnyRef](value: T) extends PrimitivePreserver[T] {
+ val clazz = value.getClass
+ }
+ implicit def makePreservedAnyRef[T <: AnyRef](x: T) = PreservedAnyRef(x)
+ implicit def makePreservedAnyVal[T <: AnyVal](x: T) = PreservedAnyVal(x)
+
+ /** We also require an implicit on scala.Symbol so they appear to contain
+ * an apply method, which packages the method arguments. The type parameter
+ * is the method's expected result type.
+ */
+ class SymbolWithArguments[R](val sym: ScalaSymbol, val args: PrimitivePreserver[_]*) {
+ def getArgs = args map (_.value.asInstanceOf[AnyRef])
+ def getArgTypes = args.toList map (_.clazz)
+ def argsMatch(m: JMethod) =
+ List.map2(m.getParameterTypes.toList, getArgTypes)(_ isAssignableFrom _) forall (_ == true)
+
+ // only called if getMethod() fails - searches private methods too.
+ def getDeclaredMethodsOn(x: AnyRef) =
+ (x.getClass.getDeclaredMethods filter (_.getName == sym.name) find argsMatch) match {
+ case Some(m) => m setAccessible true ; m
+ case None => throw new NoSuchMethodException(sym.name)
+ }
+
+ def getMethodOn(x: AnyRef) =
+ catching(classOf[NoSuchMethodException]) .
+ opt (x.getClass.getMethod(sym.name, getArgTypes: _*)) .
+ getOrElse (getDeclaredMethodsOn(x))
+
+ }
+ class RichSymbol(sym: ScalaSymbol) {
+ def apply[R](args: PrimitivePreserver[_]*): SymbolWithArguments[R] =
+ new SymbolWithArguments[R](sym, args: _*)
+ }
+ implicit def makeRichSymbol[R](sym: ScalaSymbol): RichSymbol = new RichSymbol(sym)
+
+ /** An implicit on AnyRef provides it with the 'o' method, which is supposed
+ * to look like a giant '.' and present the feel of method invocation.
+ */
+ class ReflectionOperators[T <: AnyRef](self: T) {
+ val clazz = self.getClass.asInstanceOf[JClass[T]]
+
+ /** Issue call without touching result - returns Any.
+ */
+ def o(sym: ScalaSymbol): Any = oo(new SymbolWithArguments[Any](sym))
+ def o(symApp: SymbolWithArguments[_]): Any = oo(symApp)
+
+ /** Issue call expecting return type R - casts result to R.
+ */
+ def oo[R](sym: ScalaSymbol): R = oo[R](new SymbolWithArguments[R](sym))
+ def oo[R](symApp: SymbolWithArguments[R]): R = {
+ def method = symApp getMethodOn self
+ method.invoke(self, symApp.getArgs: _*).asInstanceOf[R]
+ }
+ }
+ implicit def makeReflectionOperators[T <: AnyRef](x: T): ReflectionOperators[T] =
+ new ReflectionOperators(x)
+
+ /** Obtain the class object for an AnyVal.
+ */
+ def getAnyValClass(x: AnyVal): JClass[_] = x match {
+ case _: Byte => classOf[Byte]
+ case _: Short => classOf[Short]
+ case _: Int => classOf[Int]
+ case _: Long => classOf[Long]
+ case _: Float => classOf[Float]
+ case _: Double => classOf[Double]
+ case _: Char => classOf[Char]
+ case _: Boolean => classOf[Boolean]
+ case _: Unit => classOf[Unit]
+ }
+}