/akka-actor/src/main/scala/akka/util/Reflect.scala

https://github.com/migue/akka · Scala · 136 lines · 83 code · 11 blank · 42 comment · 28 complexity · 728a4284bc5f8b9fa0f06bf6c596fdec MD5 · raw file

  1. /**
  2. * Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
  3. */
  4. package akka.util
  5. import scala.util.control.NonFatal
  6. import java.lang.reflect.Constructor
  7. import scala.collection.immutable
  8. import java.lang.reflect.Type
  9. import scala.annotation.tailrec
  10. import java.lang.reflect.ParameterizedType
  11. import scala.util.Try
  12. /**
  13. * Collection of internal reflection utilities which may or may not be
  14. * available (most services specific to HotSpot, but fails gracefully).
  15. *
  16. * INTERNAL API
  17. */
  18. private[akka] object Reflect {
  19. /**
  20. * This optionally holds a function which looks N levels above itself
  21. * on the call stack and returns the `Class[_]` object for the code
  22. * executing in that stack frame. Implemented using
  23. * `sun.reflect.Reflection.getCallerClass` if available, None otherwise.
  24. *
  25. * Hint: when comparing to Thread.currentThread.getStackTrace, add two levels.
  26. */
  27. val getCallerClass: Option[Int Class[_]] = {
  28. try {
  29. val c = Class.forName("sun.reflect.Reflection")
  30. val m = c.getMethod("getCallerClass", Array(classOf[Int]): _*)
  31. Some((i: Int) m.invoke(null, Array[AnyRef](i.asInstanceOf[java.lang.Integer]): _*).asInstanceOf[Class[_]])
  32. } catch {
  33. case NonFatal(e) None
  34. }
  35. }
  36. /**
  37. * INTERNAL API
  38. * @param clazz the class which to instantiate an instance of
  39. * @tparam T the type of the instance that will be created
  40. * @return a new instance from the default constructor of the given class
  41. */
  42. private[akka] def instantiate[T](clazz: Class[T]): T = try clazz.newInstance catch {
  43. case iae: IllegalAccessException
  44. val ctor = clazz.getDeclaredConstructor()
  45. ctor.setAccessible(true)
  46. ctor.newInstance()
  47. }
  48. /**
  49. * INTERNAL API
  50. * Calls findConstructor and invokes it with the given arguments.
  51. */
  52. private[akka] def instantiate[T](clazz: Class[T], args: immutable.Seq[Any]): T = {
  53. instantiate(findConstructor(clazz, args), args)
  54. }
  55. /**
  56. * INTERNAL API
  57. * Invokes the constructor with the the given arguments.
  58. */
  59. private[akka] def instantiate[T](constructor: Constructor[T], args: immutable.Seq[Any]): T = {
  60. constructor.setAccessible(true)
  61. try constructor.newInstance(args.asInstanceOf[Seq[AnyRef]]: _*)
  62. catch {
  63. case e: IllegalArgumentException
  64. val argString = args map safeGetClass mkString ("[", ", ", "]")
  65. throw new IllegalArgumentException(s"constructor $constructor is incompatible with arguments $argString", e)
  66. }
  67. }
  68. /**
  69. * INTERNAL API
  70. * Implements a primitive form of overload resolution a.k.a. finding the
  71. * right constructor.
  72. */
  73. private[akka] def findConstructor[T](clazz: Class[T], args: immutable.Seq[Any]): Constructor[T] = {
  74. def error(msg: String): Nothing = {
  75. val argClasses = args map safeGetClass mkString ", "
  76. throw new IllegalArgumentException(s"$msg found on $clazz for arguments [$argClasses]")
  77. }
  78. val constructor: Constructor[T] =
  79. if (args.isEmpty) Try { clazz.getDeclaredConstructor() } getOrElse (null)
  80. else {
  81. val length = args.length
  82. val candidates =
  83. clazz.getDeclaredConstructors.asInstanceOf[Array[Constructor[T]]].iterator filter { c
  84. val parameterTypes = c.getParameterTypes
  85. parameterTypes.length == length &&
  86. (parameterTypes.iterator zip args.iterator forall {
  87. case (found, required)
  88. found.isInstance(required) || BoxedType(found).isInstance(required) ||
  89. (required == null && !found.isPrimitive)
  90. })
  91. }
  92. if (candidates.hasNext) {
  93. val cstrtr = candidates.next()
  94. if (candidates.hasNext) error("multiple matching constructors")
  95. else cstrtr
  96. } else null
  97. }
  98. if (constructor == null) error("no matching constructor")
  99. else constructor
  100. }
  101. private def safeGetClass(a: Any): Class[_] =
  102. if (a == null) classOf[AnyRef] else a.getClass
  103. /**
  104. * INTERNAL API
  105. * @param clazz the class which to instantiate an instance of
  106. * @tparam T the type of the instance that will be created
  107. * @return a function which when applied will create a new instance from the default constructor of the given class
  108. */
  109. private[akka] def instantiator[T](clazz: Class[T]): () T = () instantiate(clazz)
  110. def findMarker(root: Class[_], marker: Class[_]): Type = {
  111. @tailrec def rec(curr: Class[_]): Type = {
  112. if (curr.getSuperclass != null && marker.isAssignableFrom(curr.getSuperclass)) rec(curr.getSuperclass)
  113. else curr.getGenericInterfaces collectFirst {
  114. case c: Class[_] if marker isAssignableFrom c c
  115. case t: ParameterizedType if marker isAssignableFrom t.getRawType.asInstanceOf[Class[_]] t
  116. } match {
  117. case None throw new IllegalArgumentException(s"cannot find [$marker] in ancestors of [$root]")
  118. case Some(c: Class[_]) if (c == marker) c else rec(c)
  119. case Some(t: ParameterizedType) if (t.getRawType == marker) t else rec(t.getRawType.asInstanceOf[Class[_]])
  120. case _ ??? // cannot happen due to collectFirst
  121. }
  122. }
  123. rec(root)
  124. }
  125. }