From a36d8401e11b5e97e79f9b8f1818da1dafe4aced Mon Sep 17 00:00:00 2001 From: jvican Date: Tue, 14 Mar 2017 15:40:45 +0100 Subject: [PATCH] Don't use runtime universe to discover autoImport The previous implementation was using the Scala runtime universe to check whether a plugin had or not an `autoImport` member. This is a bad idea for the following reasons: * The first time you use it, you class load the whole Scalac compiler universe. Not efficient. Measurements say this is about a second. * There is a small overhead of going through the reflection API. There exists a better approach that consists in checking if `autoImport` exists with pure Java reflection. Since the class is already class loaded, we check for: * A class file named after the plugin FQN that includes `autoImport$` at the end, which means that an object named `autoImport` exists. * A field in the plugin class that is named `autoImport`. This complies with the plugin sbt specification: http://www.scala-sbt.org/1.0/docs/Plugins.html#Controlling+the+import+with+autoImport --- main/src/main/scala/sbt/Plugins.scala | 34 ++++++++++++++++++++------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/main/src/main/scala/sbt/Plugins.scala b/main/src/main/scala/sbt/Plugins.scala index 5b6cf57fe..6f4341ce9 100644 --- a/main/src/main/scala/sbt/Plugins.scala +++ b/main/src/main/scala/sbt/Plugins.scala @@ -349,18 +349,34 @@ ${listConflicts(conflicting)}""") case ap: AutoPlugin => model(ap) } + private val autoImport = "autoImport" + + /** Determines whether a plugin has a stable autoImport member by: + * + * 1. Checking whether there exists a public field. + * 2. Checking whether there exists a public object. + * + * The above checks work for inherited members too. + * + * @param ap The found plugin. + * @param loader The plugin loader. + * @return True if plugin has a stable member `autoImport`, otherwise false. + */ private[sbt] def hasAutoImportGetter(ap: AutoPlugin, loader: ClassLoader): Boolean = { - import reflect.runtime.{ universe => ru } + import java.lang.reflect.Field import scala.util.control.Exception.catching - val m = ru.runtimeMirror(loader) - val im = m.reflect(ap) - val hasGetterOpt = catching(classOf[ScalaReflectionException]) opt { - im.symbol.asType.toType.decl(ru.TermName("autoImport")) match { - case ru.NoSymbol => false - case sym => sym.asTerm.isGetter || sym.asTerm.isModule - } + // Make sure that we don't detect user-defined methods called autoImport + def existsAutoImportVal(clazz: Class[_]): Option[Field] = { + catching(classOf[NoSuchFieldException]) + .opt(clazz.getDeclaredField(autoImport)) + .orElse(Option(clazz.getSuperclass).flatMap(existsAutoImportVal)) } - hasGetterOpt getOrElse false + + val pluginClazz = ap.getClass + existsAutoImportVal(pluginClazz) + .orElse(catching(classOf[ClassNotFoundException]).opt( + Class.forName(s"${pluginClazz.getName}$autoImport$$", false, loader))) + .isDefined } /** Debugging method to time how long it takes to run various compilation tasks. */