From 91ea7c1fc60530346aa0ee4174ba8b9185442e1f Mon Sep 17 00:00:00 2001 From: megri Date: Mon, 26 Jun 2017 21:25:24 +0200 Subject: [PATCH 1/4] prototype --- stage2/Lib.scala | 59 +++++++++++++++++++++++++--------------- stage2/ToolsStage2.scala | 4 ++- stage2/ToolsTasks.scala | 6 ++-- 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/stage2/Lib.scala b/stage2/Lib.scala index 8801b33a..897ee085 100644 --- a/stage2/Lib.scala +++ b/stage2/Lib.scala @@ -3,14 +3,15 @@ package cbt import java.io._ import java.net._ import java.lang.reflect.InvocationTargetException -import java.nio.file.{Path =>_,_} +import java.nio.file.{Path => _, _} import java.nio.file.Files._ import java.security.MessageDigest import java.util.jar._ import java.lang.reflect.Method import scala.util._ -import scala.reflect.NameTransformer +import scala.reflect.{ClassTag, NameTransformer} +import scala.reflect.runtime.universe // pom model case class Developer(id: String, name: String, timezone: String, url: URL) @@ -56,31 +57,45 @@ final class Lib(val logger: Logger) extends } // task reflection helpers - def taskMethods(cls:Class[_]): Map[String, Method] = - Stream - .iterate(cls.asInstanceOf[Class[Any]])(_.getSuperclass) - .takeWhile(_ != null) - .toVector - .dropRight(1) // drop Object - .reverse - .flatMap( - c => - c - .getMethods - .filter( _.isPublic ) - .filter( _.parameterTypes.length == 0 ) - .map(m => NameTransformer.decode(m.getName) -> m) - .filterNot(_._1 contains "$") - ).toMap - - def taskNames(cls: Class[_]): Seq[String] = taskMethods(cls).keys.toVector.sorted + def taskMethods[T: universe.TypeTag](cls: Class[T]) = { + implicit val ct = ClassTag[T](cls) + val mirror = universe.runtimeMirror(cls.getClassLoader) + + def declaredIn(tpe: universe.Type) = { + tpe + .decls + .collect { + case m if + !m.isConstructor && + m.isPublic && + m.isMethod && + !m.name.decodedName.toString.contains("$") && + m.asMethod.paramLists.forall(_.isEmpty) => + + val methodF: AnyRef => AnyRef = instance => mirror.reflect(instance).reflectMethod(m.asMethod).apply().asInstanceOf[AnyRef] + m.name.decodedName.toString -> methodF + } + .toMap + } + + mirror + .typeOf[T] + .baseClasses + .dropRight(2) // drop … <: Object <: Any + .foldLeft(Map.empty[String, AnyRef => AnyRef]) { + case (map, symbol) => + map ++ declaredIn(symbol.typeSignature) + } + } + + def taskNames[T: universe.TypeTag](cls: Class[T]): Seq[String] = taskMethods(cls).keys.toVector.sorted def usage(buildClass: Class[_], show: String): String = { val baseTasks = Seq( classOf[BasicBuild], classOf[PackageJars], classOf[Publish] - ).flatMap(lib.taskNames).distinct.sorted + ).flatMap(lib.taskNames(_)).distinct.sorted val thisTasks = lib.taskNames(buildClass) diff baseTasks ( s"Methods provided by $show\n\n" @@ -152,7 +167,7 @@ final class Lib(val logger: Logger) extends logger.lib("Calling task " ++ taskName.toString) taskMethods(obj.getClass).get(name).map{ method => callInternal( - Option(trapExitCodeOrValue(method.invoke(obj)).merge /* null in case of Unit */ ).getOrElse( + Option(trapExitCodeOrValue(method(obj)).merge /* null in case of Unit */ ).getOrElse( ().asInstanceOf[AnyRef] ), members.tail, diff --git a/stage2/ToolsStage2.scala b/stage2/ToolsStage2.scala index 7765e82e..3ca91a07 100644 --- a/stage2/ToolsStage2.scala +++ b/stage2/ToolsStage2.scala @@ -2,9 +2,11 @@ package cbt import java.io._ object ToolsStage2 extends Stage2Base{ def run( _args: Stage2Args ): ExitCode = { + import _args.classLoaderCache + val args = _args.args.dropWhile(Seq("tools") contains _) val lib = new Lib(_args.logger) - val toolsTasks = new ToolsTasks(lib, args, _args.cwd, _args.cache, _args.cbtHome, _args.stage2LastModified)(_args.classLoaderCache) + val toolsTasks = new ToolsTasks(lib, args, _args.cwd, _args.cache, _args.cbtHome, _args.stage2LastModified) lib.callReflective(toolsTasks, args.lift(0), null) } } diff --git a/stage2/ToolsTasks.scala b/stage2/ToolsTasks.scala index 20591a25..bbf488de 100644 --- a/stage2/ToolsTasks.scala +++ b/stage2/ToolsTasks.scala @@ -2,6 +2,8 @@ package cbt import java.net._ import java.io.{Console=>_,_} import java.nio.file._ + +import scala.reflect.runtime.universe class ToolsTasks( lib: Lib, args: Seq[String], @@ -9,8 +11,8 @@ class ToolsTasks( cache: File, cbtHome: File, cbtLastModified: Long -)(implicit classLoaderCache: ClassLoaderCache){ - def apply: String = "Available methods: " ++ lib.taskNames(getClass).mkString(" ") +)(implicit classLoaderCache: ClassLoaderCache, typeTag: universe.TypeTag[ToolsTasks]){ + def apply: String = "Available methods: " ++ lib.taskNames(classOf[ToolsTasks]).mkString(" ") override def toString = lib.usage(this.getClass, super.toString) private val paths = CbtPaths(cbtHome, cache) import paths._ From 086dc48ec84efb21df6a253a4e6d6a935e9df171 Mon Sep 17 00:00:00 2001 From: megri Date: Mon, 26 Jun 2017 21:51:29 +0200 Subject: [PATCH 2/4] attempt at "reifying" TypeTag --- stage2/Lib.scala | 22 +++++++++++++++++----- stage2/ToolsStage2.scala | 4 +--- stage2/ToolsTasks.scala | 5 ++--- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/stage2/Lib.scala b/stage2/Lib.scala index 897ee085..483f044a 100644 --- a/stage2/Lib.scala +++ b/stage2/Lib.scala @@ -10,7 +10,7 @@ import java.util.jar._ import java.lang.reflect.Method import scala.util._ -import scala.reflect.{ClassTag, NameTransformer} +import scala.reflect.{api, ClassTag, NameTransformer} import scala.reflect.runtime.universe // pom model @@ -57,8 +57,20 @@ final class Lib(val logger: Logger) extends } // task reflection helpers - def taskMethods[T: universe.TypeTag](cls: Class[T]) = { - implicit val ct = ClassTag[T](cls) + def taskMethods[T](cls: Class[T]) = { + implicit val reifiedTypeTag: universe.TypeTag[T] = { + val mirror = universe.runtimeMirror(cls.getClassLoader) // obtain runtime mirror + + val sym = mirror.classSymbol(cls) + val tpe = sym.selfType // obtain type object for `c` + // create a type tag which contains above type object + universe.TypeTag(mirror, new api.TypeCreator { + def apply[U <: api.Universe with Singleton](m: api.Mirror[U]) = + if (m eq mirror) tpe.asInstanceOf[U # Type] + else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.") + }) + } + val mirror = universe.runtimeMirror(cls.getClassLoader) def declaredIn(tpe: universe.Type) = { @@ -88,14 +100,14 @@ final class Lib(val logger: Logger) extends } } - def taskNames[T: universe.TypeTag](cls: Class[T]): Seq[String] = taskMethods(cls).keys.toVector.sorted + def taskNames(cls: Class[_]): Seq[String] = taskMethods(cls).keys.toVector.sorted def usage(buildClass: Class[_], show: String): String = { val baseTasks = Seq( classOf[BasicBuild], classOf[PackageJars], classOf[Publish] - ).flatMap(lib.taskNames(_)).distinct.sorted + ).flatMap(lib.taskNames).distinct.sorted val thisTasks = lib.taskNames(buildClass) diff baseTasks ( s"Methods provided by $show\n\n" diff --git a/stage2/ToolsStage2.scala b/stage2/ToolsStage2.scala index 3ca91a07..7765e82e 100644 --- a/stage2/ToolsStage2.scala +++ b/stage2/ToolsStage2.scala @@ -2,11 +2,9 @@ package cbt import java.io._ object ToolsStage2 extends Stage2Base{ def run( _args: Stage2Args ): ExitCode = { - import _args.classLoaderCache - val args = _args.args.dropWhile(Seq("tools") contains _) val lib = new Lib(_args.logger) - val toolsTasks = new ToolsTasks(lib, args, _args.cwd, _args.cache, _args.cbtHome, _args.stage2LastModified) + val toolsTasks = new ToolsTasks(lib, args, _args.cwd, _args.cache, _args.cbtHome, _args.stage2LastModified)(_args.classLoaderCache) lib.callReflective(toolsTasks, args.lift(0), null) } } diff --git a/stage2/ToolsTasks.scala b/stage2/ToolsTasks.scala index bbf488de..4ce98de4 100644 --- a/stage2/ToolsTasks.scala +++ b/stage2/ToolsTasks.scala @@ -3,7 +3,6 @@ import java.net._ import java.io.{Console=>_,_} import java.nio.file._ -import scala.reflect.runtime.universe class ToolsTasks( lib: Lib, args: Seq[String], @@ -11,8 +10,8 @@ class ToolsTasks( cache: File, cbtHome: File, cbtLastModified: Long -)(implicit classLoaderCache: ClassLoaderCache, typeTag: universe.TypeTag[ToolsTasks]){ - def apply: String = "Available methods: " ++ lib.taskNames(classOf[ToolsTasks]).mkString(" ") +)(implicit classLoaderCache: ClassLoaderCache){ + def apply: String = "Available methods: " ++ lib.taskNames(getClass).mkString(" ") override def toString = lib.usage(this.getClass, super.toString) private val paths = CbtPaths(cbtHome, cache) import paths._ From 2554db8728d89fd8569bf72981f5f3449a0b1738 Mon Sep 17 00:00:00 2001 From: megri Date: Mon, 26 Jun 2017 22:02:47 +0200 Subject: [PATCH 3/4] clean up --- stage2/Lib.scala | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/stage2/Lib.scala b/stage2/Lib.scala index 483f044a..71bad08d 100644 --- a/stage2/Lib.scala +++ b/stage2/Lib.scala @@ -57,22 +57,24 @@ final class Lib(val logger: Logger) extends } // task reflection helpers - def taskMethods[T](cls: Class[T]) = { - implicit val reifiedTypeTag: universe.TypeTag[T] = { - val mirror = universe.runtimeMirror(cls.getClassLoader) // obtain runtime mirror + def taskMethods(cls: Class[_]) = { + val mirror = universe.runtimeMirror(cls.getClassLoader) + // implements ideas from https://stackoverflow.com/questions/23785439/getting-typetag-from-a-classname-string + val reifiedTypeTag: universe.TypeTag[_] = { val sym = mirror.classSymbol(cls) - val tpe = sym.selfType // obtain type object for `c` - // create a type tag which contains above type object - universe.TypeTag(mirror, new api.TypeCreator { - def apply[U <: api.Universe with Singleton](m: api.Mirror[U]) = - if (m eq mirror) tpe.asInstanceOf[U # Type] - else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.") - }) + val tpe = sym.selfType + + universe.TypeTag( + mirror, + new api.TypeCreator { + def apply[U <: api.Universe with Singleton](m: api.Mirror[U]) = + if (m eq mirror) tpe.asInstanceOf[U # Type] + else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.") + } + ) } - val mirror = universe.runtimeMirror(cls.getClassLoader) - def declaredIn(tpe: universe.Type) = { tpe .decls @@ -90,8 +92,7 @@ final class Lib(val logger: Logger) extends .toMap } - mirror - .typeOf[T] + reifiedTypeTag.tpe .baseClasses .dropRight(2) // drop … <: Object <: Any .foldLeft(Map.empty[String, AnyRef => AnyRef]) { From b4e1aceb07188a669368bf6b3b66d52ec6c7c149 Mon Sep 17 00:00:00 2001 From: megri Date: Mon, 26 Jun 2017 22:07:14 +0200 Subject: [PATCH 4/4] remove newline to shrink diff --- stage2/ToolsTasks.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/stage2/ToolsTasks.scala b/stage2/ToolsTasks.scala index 4ce98de4..20591a25 100644 --- a/stage2/ToolsTasks.scala +++ b/stage2/ToolsTasks.scala @@ -2,7 +2,6 @@ package cbt import java.net._ import java.io.{Console=>_,_} import java.nio.file._ - class ToolsTasks( lib: Lib, args: Seq[String],