From a3e5f304e99ff47165af6ed20182f06700e15b33 Mon Sep 17 00:00:00 2001 From: Christopher Vogt Date: Tue, 28 Mar 2017 09:35:24 -0400 Subject: minor reflection related refactor --- libraries/reflect/StaticMethod.scala | 3 ++- libraries/reflect/build/build.scala | 2 +- libraries/reflect/reflect.scala | 47 +++++++++++++++++++++--------------- stage1/Stage1Lib.scala | 25 +++---------------- stage1/resolver.scala | 22 ++++++++++------- stage2/DirectoryDependency.scala | 2 +- test/build/build.scala | 2 +- 7 files changed, 50 insertions(+), 53 deletions(-) diff --git a/libraries/reflect/StaticMethod.scala b/libraries/reflect/StaticMethod.scala index e2a0d07..d7ab3c5 100644 --- a/libraries/reflect/StaticMethod.scala +++ b/libraries/reflect/StaticMethod.scala @@ -1,4 +1,5 @@ package cbt.reflect -case class StaticMethod[Arg, Result]( function: Arg => Result, name: String ) extends ( Arg => Result ) { +import java.lang.reflect.Method +case class StaticMethod[Arg, Result]( function: Arg => Result, method: Method ) extends ( Arg => Result ) { def apply( arg: Arg ): Result = function( arg ) } diff --git a/libraries/reflect/build/build.scala b/libraries/reflect/build/build.scala index 5c27090..3b6658a 100644 --- a/libraries/reflect/build/build.scala +++ b/libraries/reflect/build/build.scala @@ -4,5 +4,5 @@ import cbt_internal._ class Build(val context: Context) extends Library{ override def inceptionYear = 2017 override def description = "discover classes on your classpath and invoke methods reflectively, preventing System.exit" - override def dependencies = super.dependencies :+ libraries.file + override def dependencies = super.dependencies :+ libraries.file :+ libraries.common_1 } diff --git a/libraries/reflect/reflect.scala b/libraries/reflect/reflect.scala index c18d926..bd7c245 100644 --- a/libraries/reflect/reflect.scala +++ b/libraries/reflect/reflect.scala @@ -7,6 +7,7 @@ import scala.reflect.ClassTag import cbt.ExitCode import cbt.file._ +import cbt.common_1._ object `package` extends Module { implicit class CbtClassOps( val c: Class[_] ) extends AnyVal with ops.CbtClassOps @@ -54,27 +55,37 @@ package ops { def isSynchronized = Modifier.isSynchronized( m.getModifiers ) def isTransient = Modifier.isTransient( m.getModifiers ) def isVolatile = Modifier.isVolatile( m.getModifiers ) + + def show = ( + m.name ~ "( " + ~ m.parameters.map( _.getType.name ).mkString( ", " ) + ~ " )" + ) } } trait Module { - def runMain( cls: Class[_], args: Seq[String] ): ExitCode = - discoverStaticExitMethodForced[Array[String]]( cls, "main" ).apply( args.to ) + def getMain( cls: Class[_] ): StaticMethod[Seq[String], ExitCode] = { + val f = findStaticExitMethodForced[Array[String]]( cls, "main" ) + f.copy( + function = ( args: Seq[String] ) => f.function( args.to ) + ) + } - def discoverMain( cls: Class[_] ): Option[StaticMethod[Seq[String], ExitCode]] = { - discoverStaticExitMethod[Array[String]]( cls, "main" ) + def findMain( cls: Class[_] ): Option[StaticMethod[Seq[String], ExitCode]] = { + findStaticExitMethod[Array[String]]( cls, "main" ) .map( f => f.copy( - function = ( arg: Seq[String] ) => f.function( arg.to ) + function = ( args: Seq[String] ) => f.function( args.to ) ) ) } /** ignoreMissingClasses allows ignoring other classes root directories which are subdirectories of this one */ - def iterateClasses( + def topLevelClasses( classesRootDirectory: File, classLoader: ClassLoader, ignoreMissingClasses: Boolean ): Seq[Class[_]] = - iterateClassNames( classesRootDirectory ) + topLevelClassNames( classesRootDirectory ) .map { name => try { classLoader.loadClass( name ) @@ -85,10 +96,10 @@ trait Module { } .filterNot( ignoreMissingClasses && _ == null ) - /** Given a directory corresponding to the root package, iterate - * the names of all classes derived from the class files found + /** Given a directory corresponding to the root package, return + * the names of all top-level classes based on the class files found */ - def iterateClassNames( classesRootDirectory: File ): Seq[String] = + def topLevelClassNames( classesRootDirectory: File ): Seq[String] = classesRootDirectory.listRecursive .filter( _.isFile ) .map( _.getPath ) @@ -102,16 +113,16 @@ trait Module { .replace( File.separator, "." ) } - def discoverStaticExitMethodForced[Arg: ClassTag]( + def findStaticExitMethodForced[Arg: ClassTag]( cls: Class[_], name: String ): StaticMethod[Arg, ExitCode] = { - val f = discoverStaticMethodForced[Arg, Unit]( cls, name ) + val f = findStaticMethodForced[Arg, Unit]( cls, name ) f.copy( function = arg => trapExitCode { f.function( arg ); ExitCode.Success } ) } - def discoverStaticMethodForced[Arg, Result]( + def findStaticMethodForced[Arg, Result]( cls: Class[_], name: String )( implicit @@ -122,15 +133,15 @@ trait Module { typeStaticMethod( m ) } - def discoverStaticExitMethod[Arg: ClassTag]( + def findStaticExitMethod[Arg: ClassTag]( cls: Class[_], name: String ): Option[StaticMethod[Arg, ExitCode]] = - discoverStaticMethod[Arg, Unit]( cls, name ).map( f => + findStaticMethod[Arg, Unit]( cls, name ).map( f => f.copy( function = arg => trapExitCode { f.function( arg ); ExitCode.Success } ) ) - def discoverStaticMethod[Arg, Result]( + def findStaticMethod[Arg, Result]( cls: Class[_], name: String )( implicit @@ -157,9 +168,7 @@ trait Module { else m.declaringClass.newInstance // Dottydoc needs this. It's main method is not static. StaticMethod( arg => m.invoke( instance, arg.asInstanceOf[AnyRef] ).asInstanceOf[Result], - m.getClass.name.stripSuffix( "$" ) ++ "." ++ m.name ++ "( " - ++ m.parameters.map( _.getType.name ).mkString( ", " ) - ++ " )" + m ) } diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala index 3ffc878..55c9234 100644 --- a/stage1/Stage1Lib.scala +++ b/stage1/Stage1Lib.scala @@ -79,28 +79,11 @@ class Stage1Lib( logger: Logger ) extends } } - /* - // ========== compilation / execution ========== - // TODO: move classLoader first - def runMain( className: String, args: Seq[String], classLoader: ClassLoader ): ExitCode = { - import java.lang.reflect.Modifier - logger.run(s"Running $className.main($args) with classLoader: " ++ classLoader.toString) - trapExitCode{ - /* - val cls = classLoader.loadClass(className) - discoverCbtMain( cls ) orElse discoverMain( cls ) getOrElse ( - throw new NoSuchMethodException( "No main method found in " ++ cbt ) - ).apply( arg.toVector )*/ - ExitCode.Success - } - } - */ - - def discoverCbtMainForced( cls: Class[_] ): cbt.reflect.StaticMethod[Context, ExitCode] = - discoverStaticMethodForced[Context, ExitCode]( cls, "cbtMain" ) + def getCbtMain( cls: Class[_] ): cbt.reflect.StaticMethod[Context, ExitCode] = + findStaticMethodForced[Context, ExitCode]( cls, "cbtMain" ) - def discoverCbtMain( cls: Class[_] ): Option[cbt.reflect.StaticMethod[Context, ExitCode]] = - discoverStaticMethod[Context, ExitCode]( cls, "cbtMain" ) + def findCbtMain( cls: Class[_] ): Option[cbt.reflect.StaticMethod[Context, ExitCode]] = + findStaticMethod[Context, ExitCode]( cls, "cbtMain" ) /** shows an interactive dialogue in the shell asking the user to pick one of many choices */ def pickOne[T]( msg: String, choices: Seq[T] )( show: T => String ): Option[T] = { diff --git a/stage1/resolver.scala b/stage1/resolver.scala index 0e5d221..be4d278 100644 --- a/stage1/resolver.scala +++ b/stage1/resolver.scala @@ -77,24 +77,28 @@ trait DependencyImplementation extends Dependency{ ) } */ - def flatClassLoader: Boolean = false - def runMain( className: String, args: Seq[String] ): ExitCode = lib.trapExitCode{ - lib.runMain( classLoader.loadClass( className ), args ) + def runMain( className: String, args: Seq[String] ): ExitCode = { + lib.getMain( classLoader.loadClass( className ) )( args ) } - def runMain( args: Seq[String] ): ExitCode = lib.trapExitCode{ - mainMethod.getOrElse( + def runMain( args: Seq[String] ): ExitCode = { + val c = mainClass.getOrElse( throw new RuntimeException( "No main class found in " + this ) - )( args ) + ) + runMain( c.getName, args ) } - def mainMethod = lib.pickOne( "Which one do you want to run?", mainMethods )( _.name ) + def mainClass = lib.pickOne( + "Which one do you want to run?", + classes.filter( lib.findMain(_).nonEmpty ) + )( _.name.stripSuffix( "$" ) ) def classes = exportedClasspath.files.flatMap( - lib.iterateClasses( _, classLoader, false ) + lib.topLevelClasses( _, classLoader, false ) ) - def mainMethods = classes.flatMap( lib.discoverMain ) + + def flatClassLoader: Boolean = false def classLoader: ClassLoader = taskCache[DependencyImplementation]( "classLoader" ).memoize{ if( flatClassLoader ){ diff --git a/stage2/DirectoryDependency.scala b/stage2/DirectoryDependency.scala index 9b07702..6ebb988 100644 --- a/stage2/DirectoryDependency.scala +++ b/stage2/DirectoryDependency.scala @@ -77,7 +77,7 @@ object DirectoryDependency { val buildClasses = buildBuild.exportedClasspath.files.flatMap( - lib.iterateClasses( _, classLoader, false ) + lib.topLevelClasses( _, classLoader, false ) .filter( _.getSimpleName === lib.buildClassName ) .filter( classOf[BaseBuild] isAssignableFrom _ ) ) diff --git a/test/build/build.scala b/test/build/build.scala index 792b34d..27de2bb 100644 --- a/test/build/build.scala +++ b/test/build/build.scala @@ -4,7 +4,7 @@ class Build(val context: cbt.Context) extends BaseBuild{ override def dependencies = super.dependencies :+ context.cbtDependency def apply = run override def run = { - classes.flatMap( lib.discoverCbtMain ).head( context ) + classes.flatMap( lib.findCbtMain ).head( context ) } def args = context.args } -- cgit v1.2.3