mirror of https://github.com/sbt/sbt.git
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
This commit is contained in:
parent
27b4faebed
commit
a36d8401e1
|
|
@ -349,18 +349,34 @@ ${listConflicts(conflicting)}""")
|
||||||
case ap: AutoPlugin => model(ap)
|
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 = {
|
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
|
import scala.util.control.Exception.catching
|
||||||
val m = ru.runtimeMirror(loader)
|
// Make sure that we don't detect user-defined methods called autoImport
|
||||||
val im = m.reflect(ap)
|
def existsAutoImportVal(clazz: Class[_]): Option[Field] = {
|
||||||
val hasGetterOpt = catching(classOf[ScalaReflectionException]) opt {
|
catching(classOf[NoSuchFieldException])
|
||||||
im.symbol.asType.toType.decl(ru.TermName("autoImport")) match {
|
.opt(clazz.getDeclaredField(autoImport))
|
||||||
case ru.NoSymbol => false
|
.orElse(Option(clazz.getSuperclass).flatMap(existsAutoImportVal))
|
||||||
case sym => sym.asTerm.isGetter || sym.asTerm.isModule
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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. */
|
/** Debugging method to time how long it takes to run various compilation tasks. */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue