From b4c1d502d0d942fd21bd7959829c237cb1778df2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Jan 2020 13:28:45 +0100 Subject: [PATCH 1/2] Fix #8020: Create dotty-tasty-consumer library --- .drone.yml | 2 +- build.sbt | 1 + .../dotc/consumetasty/ConsumeTasty.scala | 55 ------------------- .../dotc/util/ClasspathFromClassloader.scala | 47 ++++++++++++++++ compiler/test/dotty/Properties.scala | 3 + .../BootstrappedOnlyCompilationTests.scala | 3 +- .../tools/vulpix/TestConfiguration.scala | 5 ++ dist/bin/common | 1 + dist/bin/dotr | 2 +- .../metaprogramming/tasty-inspect.md | 17 +++++- .../src/scala/tasty/file/ConsumeTasty.scala | 26 --------- project/Build.scala | 34 +++++++++--- .../app/Main.scala | 19 +++++++ .../tasty-consumer-example-project/build.sbt | 15 +++++ .../lib/Foo.scala | 5 ++ .../project/plugins.sbt | 1 + .../tasty-consumer-example-project/test | 1 + .../scala/quoted/staging/QuoteDriver.scala | 42 +------------- .../src/scala/tasty/file/ConsumeTasty.scala | 39 +++++++++++++ .../src/scala/tasty/file/TastyConsumer.scala | 0 .../tasty/file}/TastyConsumerPhase.scala | 4 +- .../scala/tasty/file}/TastyFromClass.scala | 6 +- .../tasty-comment-consumer/Foo.scala | 0 .../tasty-comment-consumer/Test.scala | 0 .../tasty-consumer}/tasty-consumer/Foo.scala | 0 .../tasty-consumer}/tasty-consumer/Test.scala | 0 .../tasty-interpreter/InterpretedMain.scala | 0 .../tasty-interpreter/Precompiled.scala | 0 .../tasty-interpreter/Test.scala | 0 .../interpreter/TastyInterpreter.scala | 0 .../interpreter/TreeInterpreter.scala | 0 .../interpreter/jvm/Interpreter.scala | 0 .../interpreter/jvm/JVMReflection.scala | 0 .../tasty-interpreter/notes.md | 0 34 files changed, 189 insertions(+), 139 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/consumetasty/ConsumeTasty.scala create mode 100644 compiler/src/dotty/tools/dotc/util/ClasspathFromClassloader.scala delete mode 100644 library/src/scala/tasty/file/ConsumeTasty.scala create mode 100644 sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/app/Main.scala create mode 100644 sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/build.sbt create mode 100644 sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/lib/Foo.scala create mode 100644 sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/project/plugins.sbt create mode 100644 sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/test create mode 100644 tasty-consumer/src/scala/tasty/file/ConsumeTasty.scala rename {library => tasty-consumer}/src/scala/tasty/file/TastyConsumer.scala (100%) rename {compiler/src/dotty/tools/dotc/consumetasty => tasty-consumer/src/scala/tasty/file}/TastyConsumerPhase.scala (79%) rename {compiler/src/dotty/tools/dotc/consumetasty => tasty-consumer/src/scala/tasty/file}/TastyFromClass.scala (83%) rename tests/{run-with-compiler => run-custom-args/tasty-consumer}/tasty-comment-consumer/Foo.scala (100%) rename tests/{run-with-compiler => run-custom-args/tasty-consumer}/tasty-comment-consumer/Test.scala (100%) rename tests/{run-with-compiler => run-custom-args/tasty-consumer}/tasty-consumer/Foo.scala (100%) rename tests/{run-with-compiler => run-custom-args/tasty-consumer}/tasty-consumer/Test.scala (100%) rename tests/{run-with-compiler-custom-args => run-custom-args}/tasty-interpreter/InterpretedMain.scala (100%) rename tests/{run-with-compiler-custom-args => run-custom-args}/tasty-interpreter/Precompiled.scala (100%) rename tests/{run-with-compiler-custom-args => run-custom-args}/tasty-interpreter/Test.scala (100%) rename tests/{run-with-compiler-custom-args => run-custom-args}/tasty-interpreter/interpreter/TastyInterpreter.scala (100%) rename tests/{run-with-compiler-custom-args => run-custom-args}/tasty-interpreter/interpreter/TreeInterpreter.scala (100%) rename tests/{run-with-compiler-custom-args => run-custom-args}/tasty-interpreter/interpreter/jvm/Interpreter.scala (100%) rename tests/{run-with-compiler-custom-args => run-custom-args}/tasty-interpreter/interpreter/jvm/JVMReflection.scala (100%) rename tests/{run-with-compiler-custom-args => run-custom-args}/tasty-interpreter/notes.md (100%) diff --git a/.drone.yml b/.drone.yml index 56f49929a59b..5218a7957bd3 100644 --- a/.drone.yml +++ b/.drone.yml @@ -40,7 +40,7 @@ steps: depends_on: [ clone ] commands: - cp -R . /tmp/2/ && cd /tmp/2/ - - ./project/scripts/sbt ";dotty-bootstrapped/compile ;dotty-bootstrapped/test ;dotty-staging/test ;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test ;configureIDE" + - ./project/scripts/sbt ";dotty-bootstrapped/compile ;dotty-bootstrapped/test;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test ;configureIDE" - ./project/scripts/bootstrapCmdTests - name: community_build diff --git a/build.sbt b/build.sbt index 54f8fe6bd63a..c4aea02437c7 100644 --- a/build.sbt +++ b/build.sbt @@ -11,6 +11,7 @@ val `dotty-library-bootstrappedJS` = Build.`dotty-library-bootstrappedJS` val `dotty-sbt-bridge` = Build.`dotty-sbt-bridge` val `dotty-sbt-bridge-tests` = Build.`dotty-sbt-bridge-tests` val `dotty-staging` = Build.`dotty-staging` +val `dotty-tasty-consumer` = Build.`dotty-tasty-consumer` val `dotty-language-server` = Build.`dotty-language-server` val `dotty-bench` = Build.`dotty-bench` val `dotty-bench-bootstrapped` = Build.`dotty-bench-bootstrapped` diff --git a/compiler/src/dotty/tools/dotc/consumetasty/ConsumeTasty.scala b/compiler/src/dotty/tools/dotc/consumetasty/ConsumeTasty.scala deleted file mode 100644 index 59f3b9464ed0..000000000000 --- a/compiler/src/dotty/tools/dotc/consumetasty/ConsumeTasty.scala +++ /dev/null @@ -1,55 +0,0 @@ -package dotty.tools.dotc.consumetasty - -import java.net.URLClassLoader - -import dotty.tools.dotc -import dotty.tools.dotc.core.Contexts._ - -import java.nio.file.Paths -import java.io.File -import java.io.File.{ pathSeparator => sep } -import scala.annotation.tailrec - -import scala.tasty.file.TastyConsumer - -object ConsumeTasty { - - def apply(classpath: String, classes: List[String], tastyConsumer: TastyConsumer): Unit = { - if (classes.isEmpty) - throw new IllegalArgumentException("Parameter classes should no be empty") - - class Consume extends dotc.Driver { - override protected def newCompiler(implicit ctx: Context): dotc.Compiler = - new TastyFromClass(tastyConsumer) - } - - val currentClasspath = classpathFromClassloader(getClass.getClassLoader) - val args = "-from-tasty" :: "-Yretain-trees" :: "-classpath" :: s"$classpath$sep$currentClasspath" :: classes - (new Consume).process(args.toArray) - } - - /** Attempt to recreate a classpath from a classloader. - * - * BEWARE: with exotic enough classloaders, this may not work at all or do - * the wrong thing. - */ - private def classpathFromClassloader(cl: ClassLoader): String = { - @tailrec - def loop(cl: ClassLoader, suffixClasspath: String): String = - cl match { - case cl: URLClassLoader => - val updatedClasspath = cl.getURLs - .map(url => Paths.get(url.toURI).toAbsolutePath.toString) - .mkString( - "", - File.pathSeparator, - if (suffixClasspath.isEmpty) "" else File.pathSeparator + suffixClasspath - ) - loop(cl.getParent, updatedClasspath) - case _ => - suffixClasspath - } - - loop(cl, "") - } -} \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/util/ClasspathFromClassloader.scala b/compiler/src/dotty/tools/dotc/util/ClasspathFromClassloader.scala new file mode 100644 index 000000000000..de7edd804d85 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/util/ClasspathFromClassloader.scala @@ -0,0 +1,47 @@ +package dotty.tools.dotc.util + +import java.net.URLClassLoader +import java.nio.file.Paths + +import dotty.tools.repl.AbstractFileClassLoader + +object ClasspathFromClassloader { + + /** Attempt to recreate a classpath from a classloader. + * + * BEWARE: with exotic enough classloaders, this may not work at all or do + * the wrong thing. + */ + def apply(cl: ClassLoader): String = { + val classpathBuff = List.newBuilder[String] + def collectClassLoaderPaths(cl: ClassLoader): Unit = { + if (cl != null) { + cl match { + case cl: URLClassLoader => + // This is wrong if we're in a subclass of URLClassLoader + // that filters loading classes from its parent ¯\_(ツ)_/¯ + collectClassLoaderPaths(cl.getParent) + // Parent classloaders are searched before their child, so the part of + // the classpath coming from the child is added at the _end_ of the + // classpath. + classpathBuff ++= + cl.getURLs.iterator.map(url => Paths.get(url.toURI).toAbsolutePath.toString) + case _ => + // HACK: We can't just collect the classpath from arbitrary parent + // classloaders since the current classloader might intentionally + // filter loading classes from its parent (for example + // BootFilteredLoader in the sbt launcher does this and we really + // don't want to include the scala-library that sbt depends on + // here), but we do need to look at the parent of the REPL + // classloader, so we special case it. We can't do this using a type + // test since the REPL classloader class itself is normally loaded + // with a different classloader. + if (cl.getClass.getName == classOf[AbstractFileClassLoader].getName) + collectClassLoaderPaths(cl.getParent) + } + } + } + collectClassLoaderPaths(cl) + classpathBuff.result().mkString(java.io.File.pathSeparator) + } +} diff --git a/compiler/test/dotty/Properties.scala b/compiler/test/dotty/Properties.scala index ed377859a047..586d08a8456d 100644 --- a/compiler/test/dotty/Properties.scala +++ b/compiler/test/dotty/Properties.scala @@ -53,6 +53,9 @@ object Properties { /** dotty-staging jar */ def dottyStaging: String = sys.props("dotty.tests.classes.dottyStaging") + /** dotty-tasty-consumer jar */ + def dottyTastyConsumer: String = sys.props("dotty.tests.classes.dottyTastyConsumer") + /** tasty-core jar */ def tastyCore: String = sys.props("dotty.tests.classes.tastyCore") diff --git a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala index 104783d9c867..4ba5bcb1d541 100644 --- a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala @@ -127,7 +127,8 @@ class BootstrappedOnlyCompilationTests extends ParallelTesting { aggregateTests( compileFilesInDir("tests/run-with-compiler", withCompilerOptions), compileFilesInDir("tests/run-staging", withStagingOptions), - compileDir("tests/run-with-compiler-custom-args/tasty-interpreter", withCompilerOptions) + compileFilesInDir("tests/run-custom-args/tasty-consumer", withTastyConsumerOptions), + compileDir("tests/run-custom-args/tasty-interpreter", withTastyConsumerOptions), ).checkRuns() } diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index fdcd4f83f44f..f4b98c96c1f9 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -39,6 +39,9 @@ object TestConfiguration { lazy val withStagingClasspath = withCompilerClasspath + File.pathSeparator + mkClasspath(List(Properties.dottyStaging)) + lazy val withTastyConsumerClasspath = + withCompilerClasspath + File.pathSeparator + mkClasspath(List(Properties.dottyTastyConsumer)) + def mkClasspath(classpaths: List[String]): String = classpaths.map({ p => val file = new java.io.File(p) @@ -54,6 +57,8 @@ object TestConfiguration { defaultOptions.withClasspath(withCompilerClasspath).withRunClasspath(withCompilerClasspath) lazy val withStagingOptions = defaultOptions.withClasspath(withStagingClasspath).withRunClasspath(withStagingClasspath) + lazy val withTastyConsumerOptions = + defaultOptions.withClasspath(withTastyConsumerClasspath).withRunClasspath(withTastyConsumerClasspath) val allowDeepSubtypes = defaultOptions without "-Yno-deep-subtypes" val allowDoubleBindings = defaultOptions without "-Yno-double-bindings" val picklingOptions = defaultOptions and ( diff --git a/dist/bin/common b/dist/bin/common index 0c7822f6c953..13097da97419 100755 --- a/dist/bin/common +++ b/dist/bin/common @@ -108,6 +108,7 @@ DOTTY_COMP=$(find_lib "*dotty-compiler*") DOTTY_INTF=$(find_lib "*dotty-interfaces*") DOTTY_LIB=$(find_lib "*dotty-library*") DOTTY_STAGING=$(find_lib "*dotty-staging*") +DOTTY_CONSUME_TASTY=$(find_lib "*dotty-consume-tasty*") TASTY_CORE=$(find_lib "*tasty-core*") SCALA_ASM=$(find_lib "*scala-asm*") SCALA_LIB=$(find_lib "*scala-library*") diff --git a/dist/bin/dotr b/dist/bin/dotr index 029a0aee97af..78f18a910dfa 100755 --- a/dist/bin/dotr +++ b/dist/bin/dotr @@ -102,7 +102,7 @@ elif [ $execute_repl == true ] || [ ${#residual_args[@]} -ne 0 ]; then echo "warning: multiple classpaths are found, dotr only use the last one." fi if [ $with_compiler == true ]; then - cp_arg+="$PSEP$DOTTY_COMP$PSEP$TASTY_CORE$PSEP$DOTTY_INTF$PSEP$SCALA_ASM$PSEP$DOTTY_STAGING" + cp_arg+="$PSEP$DOTTY_COMP$PSEP$TASTY_CORE$PSEP$DOTTY_INTF$PSEP$SCALA_ASM$PSEP$DOTTY_STAGING$PSEP$DOTTY_CONSUME_TASTY" fi eval exec "\"$JAVACMD\"" "$DEBUG" "-classpath \"$cp_arg\"" "${jvm_options[@]}" "${residual_args[@]}" else diff --git a/docs/docs/reference/metaprogramming/tasty-inspect.md b/docs/docs/reference/metaprogramming/tasty-inspect.md index ca6503f3525a..1eefcc0c6daa 100644 --- a/docs/docs/reference/metaprogramming/tasty-inspect.md +++ b/docs/docs/reference/metaprogramming/tasty-inspect.md @@ -3,6 +3,10 @@ layout: doc-page title: "TASTy Inspection" --- +```scala +libraryDependencies += "ch.epfl.lamp" %% "dotty-tasty-consumer" % scalaVersion.value +``` + TASTy files contain the full typed tree of a class including source positions and documentation. This is ideal for tools that analyze or extract semantic information of the code. To avoid the hassle of working directly with the TASTy @@ -16,6 +20,9 @@ To inspect the TASTy Reflect trees of a TASTy file a consumer can be defined in the following way. ```scala +import scala.tasty.Reflection +import scala.tasty.file._ + class Consumer extends TastyConsumer { final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ @@ -33,4 +40,12 @@ object Test { ConsumeTasty("", List("foo.Bar"), new Consumer) } } -``` \ No newline at end of file +``` + +Note that if we need to run the main (in an object called `Test`) after +compilation we need make available the compiler to the runtime: + +```shell +dotc -with-compiler -d out Test.scala +dotr -with-compiler -classpath out Test +``` diff --git a/library/src/scala/tasty/file/ConsumeTasty.scala b/library/src/scala/tasty/file/ConsumeTasty.scala deleted file mode 100644 index 5a2892242d99..000000000000 --- a/library/src/scala/tasty/file/ConsumeTasty.scala +++ /dev/null @@ -1,26 +0,0 @@ -package scala.tasty.file - -object ConsumeTasty { - - /** Load and process TASTy files using TASTy reflect - * - * @param classpath Classpath where the classes are located - * @param classes classes to be consumed - * @param tastyConsumer consumer that will process the tasty trees - */ - def apply(classpath: String, classes: List[String], tastyConsumer: TastyConsumer): Unit = { - val cl = getClass.getClassLoader - try { - val dottyConsumeTastyCls = cl.loadClass("dotty.tools.dotc.consumetasty.ConsumeTasty") - val makeMeth = dottyConsumeTastyCls.getMethod("apply", classOf[String], classOf[List[_]], classOf[TastyConsumer]) - makeMeth.invoke(null, classpath, classes, tastyConsumer) - } - catch { - case ex: ClassNotFoundException => - throw new Exception( - s"""Could not load the dotty.tools.dotc.consumetasty.ConsumeTasty class `${ex.getMessage}` from the JVM classpath. Make sure that the compiler is on the JVM classpath.""", - ex - ) - } - } -} diff --git a/project/Build.scala b/project/Build.scala index e972d58f0ef7..156f1f045e32 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -595,9 +595,10 @@ object Build { val asm = findArtifactPath(externalDeps, "scala-asm") val dottyCompiler = jars("dotty-compiler") val dottyStaging = jars("dotty-staging") + val dottyTastyConsumer = jars("dotty-tasty-consumer") val dottyInterfaces = jars("dotty-interfaces") val tastyCore = jars("tasty-core") - run(insertClasspathInArgs(args1, List(dottyCompiler, dottyInterfaces, asm, dottyStaging, tastyCore).mkString(File.pathSeparator))) + run(insertClasspathInArgs(args1, List(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyConsumer, tastyCore).mkString(File.pathSeparator))) } else run(args) }, @@ -671,9 +672,10 @@ object Build { } val dottyInterfaces = jars("dotty-interfaces") val dottyStaging = jars("dotty-staging") + val dottyTastyConsumer = jars("dotty-tasty-consumer") val tastyCore = jars("tasty-core") val asm = findArtifactPath(externalDeps, "scala-asm") - extraClasspath ++= Seq(dottyCompiler, dottyInterfaces, asm, dottyStaging, tastyCore) + extraClasspath ++= Seq(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyConsumer, tastyCore) } val fullArgs = main :: insertClasspathInArgs(args, extraClasspath.mkString(File.pathSeparator)) @@ -715,14 +717,18 @@ object Build { ) lazy val bootstrapedDottyCompilerSettings = commonDottyCompilerSettings ++ Seq( - javaOptions += { + javaOptions ++= { val jars = packageAll.value - "-Ddotty.tests.classes.dottyStaging=" + jars("dotty-staging") + Seq( + "-Ddotty.tests.classes.dottyStaging=" + jars("dotty-staging"), + "-Ddotty.tests.classes.dottyTastyConsumer=" + jars("dotty-tasty-consumer"), + ) }, packageAll := { packageAll.in(`dotty-compiler`).value ++ Seq( "dotty-compiler" -> packageBin.in(Compile).value.getAbsolutePath, "dotty-staging" -> packageBin.in(LocalProject("dotty-staging"), Compile).value.getAbsolutePath, + "dotty-tasty-consumer" -> packageBin.in(LocalProject("dotty-tasty-consumer"), Compile).value.getAbsolutePath, "tasty-core" -> packageBin.in(LocalProject("tasty-core-bootstrapped"), Compile).value.getAbsolutePath, ) } @@ -795,9 +801,18 @@ object Build { // We want the compiler to be present in the compiler classpath when compiling this project but not // when compiling a project that depends on dotty-staging (see sbt-dotty/sbt-test/sbt-dotty/quoted-example-project), // but we always need it to be present on the JVM classpath at runtime. - dependsOn(dottyCompiler(Bootstrapped) % "provided"). - dependsOn(dottyCompiler(Bootstrapped) % "compile->runtime"). - dependsOn(dottyCompiler(Bootstrapped) % "test->test"). + dependsOn(dottyCompiler(Bootstrapped) % "provided; compile->runtime; test->test"). + settings(commonBootstrappedSettings). + settings( + javaOptions := (javaOptions in `dotty-compiler-bootstrapped`).value + ) + + lazy val `dotty-tasty-consumer` = project.in(file("tasty-consumer")). + withCommonSettings(Bootstrapped). + // We want the compiler to be present in the compiler classpath when compiling this project but not + // when compiling a project that depends on dotty-tasty-consumer (see sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project), + // but we always need it to be present on the JVM classpath at runtime. + dependsOn(dottyCompiler(Bootstrapped) % "provided; compile->runtime; test->test"). settings(commonBootstrappedSettings). settings( javaOptions := (javaOptions in `dotty-compiler-bootstrapped`).value @@ -1106,6 +1121,7 @@ object Build { publishLocal in `dotty-library-bootstrapped`, publishLocal in `tasty-core-bootstrapped`, publishLocal in `dotty-staging`, + publishLocal in `dotty-tasty-consumer`, publishLocal in `scala-library`, publishLocal in `scala-reflect`, publishLocal in `dotty-doc-bootstrapped`, @@ -1315,7 +1331,8 @@ object Build { // FIXME: we do not aggregate `bin` because its tests delete jars, thus breaking other tests def asDottyRoot(implicit mode: Mode): Project = project.withCommonSettings. aggregate(`dotty-interfaces`, dottyLibrary, dottyCompiler, tastyCore, dottyDoc, `dotty-sbt-bridge`). - bootstrappedAggregate(`scala-library`, `scala-compiler`, `scala-reflect`, scalap, `dotty-language-server`, `dotty-staging`). + bootstrappedAggregate(`scala-library`, `scala-compiler`, `scala-reflect`, scalap, + `dotty-language-server`, `dotty-staging`, `dotty-tasty-consumer`, `dotty-tastydoc`). dependsOn(tastyCore). dependsOn(dottyCompiler). dependsOn(dottyLibrary). @@ -1354,6 +1371,7 @@ object Build { def asDottyTastydoc(implicit mode: Mode): Project = project.withCommonSettings. aggregate(`dotty-tastydoc-input`). dependsOn(dottyCompiler). + dependsOn(`dotty-tasty-consumer`). settings(commonDocSettings) def asDottyTastydocInput(implicit mode: Mode): Project = project.withCommonSettings. diff --git a/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/app/Main.scala b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/app/Main.scala new file mode 100644 index 000000000000..22824300cab3 --- /dev/null +++ b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/app/Main.scala @@ -0,0 +1,19 @@ +package hello + +import scala.tasty.Reflection +import scala.tasty.file._ + +object Main extends App { + + + class Consumer extends TastyConsumer { + final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { + import reflect._ + val tastyStr = root.show + println(tastyStr) + } + } + + ConsumeTasty("", List("lib.Foo"), new Consumer) + +} diff --git a/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/build.sbt b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/build.sbt new file mode 100644 index 000000000000..cae75a5daa92 --- /dev/null +++ b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/build.sbt @@ -0,0 +1,15 @@ +lazy val dottyVersion = sys.props("plugin.scalaVersion") + +lazy val lib = project + .in(file("lib")) + .settings( + scalaVersion := dottyVersion + ) + +lazy val app = project + .in(file("app")) + .settings( + scalaVersion := dottyVersion, + libraryDependencies += "ch.epfl.lamp" %% "dotty-tasty-consumer" % scalaVersion.value, + ) + .dependsOn(lib) diff --git a/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/lib/Foo.scala b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/lib/Foo.scala new file mode 100644 index 000000000000..d3d2d13c2c5a --- /dev/null +++ b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/lib/Foo.scala @@ -0,0 +1,5 @@ +package lib + +class Foo { + def foo(x: Int) = x +} diff --git a/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/project/plugins.sbt b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/test b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/test new file mode 100644 index 000000000000..63092ffa4a03 --- /dev/null +++ b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/test @@ -0,0 +1 @@ +> app/run diff --git a/staging/src/scala/quoted/staging/QuoteDriver.scala b/staging/src/scala/quoted/staging/QuoteDriver.scala index fe4410bafeb6..f9b0c6ed6736 100644 --- a/staging/src/scala/quoted/staging/QuoteDriver.scala +++ b/staging/src/scala/quoted/staging/QuoteDriver.scala @@ -8,10 +8,9 @@ import dotty.tools.dotc.tastyreflect.ReflectionImpl import dotty.tools.io.{AbstractFile, Directory, PlainDirectory, VirtualDirectory} import dotty.tools.repl.AbstractFileClassLoader import dotty.tools.dotc.reporting._ +import dotty.tools.dotc.util.ClasspathFromClassloader import scala.quoted._ import scala.quoted.staging.Toolbox -import java.net.URLClassLoader -import java.nio.file.Paths import java.io.File import scala.annotation.tailrec @@ -56,7 +55,7 @@ private class QuoteDriver(appClassloader: ClassLoader) extends Driver { override def initCtx: Context = { val ictx = contextBase.initialCtx - ictx.settings.classpath.update(classpathFromClassloader(appClassloader))(ictx) + ictx.settings.classpath.update(ClasspathFromClassloader(appClassloader))(ictx) ictx } @@ -67,42 +66,5 @@ private class QuoteDriver(appClassloader: ClassLoader) extends Driver { ctx.setReporter(new ThrowingReporter(ctx.reporter)) } - /** Attempt to recreate a classpath from a classloader. - * - * BEWARE: with exotic enough classloaders, this may not work at all or do - * the wrong thing. - */ - private def classpathFromClassloader(cl: ClassLoader): String = { - val classpathBuff = List.newBuilder[String] - def collectClassLoaderPaths(cl: ClassLoader): Unit = { - if (cl != null) { - cl match { - case cl: URLClassLoader => - // This is wrong if we're in a subclass of URLClassLoader - // that filters loading classes from its parent ¯\_(ツ)_/¯ - collectClassLoaderPaths(cl.getParent) - // Parent classloaders are searched before their child, so the part of - // the classpath coming from the child is added at the _end_ of the - // classpath. - classpathBuff ++= - cl.getURLs.iterator.map(url => Paths.get(url.toURI).toAbsolutePath.toString) - case _ => - // HACK: We can't just collect the classpath from arbitrary parent - // classloaders since the current classloader might intentionally - // filter loading classes from its parent (for example - // BootFilteredLoader in the sbt launcher does this and we really - // don't want to include the scala-library that sbt depends on - // here), but we do need to look at the parent of the REPL - // classloader, so we special case it. We can't do this using a type - // test since the REPL classloader class itself is normally loaded - // with a different classloader. - if (cl.getClass.getName == classOf[AbstractFileClassLoader].getName) - collectClassLoaderPaths(cl.getParent) - } - } - } - collectClassLoaderPaths(cl) - classpathBuff.result().mkString(java.io.File.pathSeparator) - } } diff --git a/tasty-consumer/src/scala/tasty/file/ConsumeTasty.scala b/tasty-consumer/src/scala/tasty/file/ConsumeTasty.scala new file mode 100644 index 000000000000..e1c3120b89ce --- /dev/null +++ b/tasty-consumer/src/scala/tasty/file/ConsumeTasty.scala @@ -0,0 +1,39 @@ +package scala.tasty.file + +import java.net.URLClassLoader + +import dotty.tools.dotc +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.util.ClasspathFromClassloader + +import java.nio.file.Paths +import java.io.File +import java.io.File.{ pathSeparator => sep } +import scala.annotation.tailrec + +import scala.tasty.file.TastyConsumer + +// TODO: Move this logic into TastyConumer to simplify the API +object ConsumeTasty { + + /** Load and process TASTy files using TASTy reflect + * + * @param classpath Classpath where the classes are located + * @param classes classes to be consumed + * @param tastyConsumer consumer that will process the tasty trees + */ + def apply(classpath: String, classes: List[String], tastyConsumer: TastyConsumer): Unit = { + if (classes.isEmpty) + throw new IllegalArgumentException("Parameter classes should no be empty") + + class Consume extends dotc.Driver { + override protected def newCompiler(implicit ctx: Context): dotc.Compiler = + new TastyFromClass(tastyConsumer) + } + + val currentClasspath = ClasspathFromClassloader(getClass.getClassLoader) + val args = "-from-tasty" :: "-Yretain-trees" :: "-classpath" :: s"$classpath$sep$currentClasspath" :: classes + (new Consume).process(args.toArray) + } + +} diff --git a/library/src/scala/tasty/file/TastyConsumer.scala b/tasty-consumer/src/scala/tasty/file/TastyConsumer.scala similarity index 100% rename from library/src/scala/tasty/file/TastyConsumer.scala rename to tasty-consumer/src/scala/tasty/file/TastyConsumer.scala diff --git a/compiler/src/dotty/tools/dotc/consumetasty/TastyConsumerPhase.scala b/tasty-consumer/src/scala/tasty/file/TastyConsumerPhase.scala similarity index 79% rename from compiler/src/dotty/tools/dotc/consumetasty/TastyConsumerPhase.scala rename to tasty-consumer/src/scala/tasty/file/TastyConsumerPhase.scala index 938043ffd7ab..46b710d72977 100644 --- a/compiler/src/dotty/tools/dotc/consumetasty/TastyConsumerPhase.scala +++ b/tasty-consumer/src/scala/tasty/file/TastyConsumerPhase.scala @@ -1,4 +1,4 @@ -package dotty.tools.dotc.consumetasty +package scala.tasty.file import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Phases.Phase @@ -6,7 +6,7 @@ import dotty.tools.dotc.tastyreflect.ReflectionImpl import scala.tasty.file.TastyConsumer -class TastyConsumerPhase(consumer: TastyConsumer) extends Phase { +private class TastyConsumerPhase(consumer: TastyConsumer) extends Phase { override def phaseName: String = "tastyConsumer" diff --git a/compiler/src/dotty/tools/dotc/consumetasty/TastyFromClass.scala b/tasty-consumer/src/scala/tasty/file/TastyFromClass.scala similarity index 83% rename from compiler/src/dotty/tools/dotc/consumetasty/TastyFromClass.scala rename to tasty-consumer/src/scala/tasty/file/TastyFromClass.scala index afc811e54cc1..38c0356b0d03 100644 --- a/compiler/src/dotty/tools/dotc/consumetasty/TastyFromClass.scala +++ b/tasty-consumer/src/scala/tasty/file/TastyFromClass.scala @@ -1,4 +1,4 @@ -package dotty.tools.dotc.consumetasty +package scala.tasty.file import dotty.tools.dotc.Run import dotty.tools.dotc.core.Mode @@ -6,9 +6,7 @@ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.fromtasty._ -import scala.tasty.file.TastyConsumer - -class TastyFromClass(consumer: TastyConsumer) extends TASTYCompiler { +private class TastyFromClass(consumer: TastyConsumer) extends TASTYCompiler { override protected def frontendPhases: List[List[Phase]] = List(new ReadTasty) :: // Load classes from tasty diff --git a/tests/run-with-compiler/tasty-comment-consumer/Foo.scala b/tests/run-custom-args/tasty-consumer/tasty-comment-consumer/Foo.scala similarity index 100% rename from tests/run-with-compiler/tasty-comment-consumer/Foo.scala rename to tests/run-custom-args/tasty-consumer/tasty-comment-consumer/Foo.scala diff --git a/tests/run-with-compiler/tasty-comment-consumer/Test.scala b/tests/run-custom-args/tasty-consumer/tasty-comment-consumer/Test.scala similarity index 100% rename from tests/run-with-compiler/tasty-comment-consumer/Test.scala rename to tests/run-custom-args/tasty-consumer/tasty-comment-consumer/Test.scala diff --git a/tests/run-with-compiler/tasty-consumer/Foo.scala b/tests/run-custom-args/tasty-consumer/tasty-consumer/Foo.scala similarity index 100% rename from tests/run-with-compiler/tasty-consumer/Foo.scala rename to tests/run-custom-args/tasty-consumer/tasty-consumer/Foo.scala diff --git a/tests/run-with-compiler/tasty-consumer/Test.scala b/tests/run-custom-args/tasty-consumer/tasty-consumer/Test.scala similarity index 100% rename from tests/run-with-compiler/tasty-consumer/Test.scala rename to tests/run-custom-args/tasty-consumer/tasty-consumer/Test.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala b/tests/run-custom-args/tasty-interpreter/InterpretedMain.scala similarity index 100% rename from tests/run-with-compiler-custom-args/tasty-interpreter/InterpretedMain.scala rename to tests/run-custom-args/tasty-interpreter/InterpretedMain.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/Precompiled.scala b/tests/run-custom-args/tasty-interpreter/Precompiled.scala similarity index 100% rename from tests/run-with-compiler-custom-args/tasty-interpreter/Precompiled.scala rename to tests/run-custom-args/tasty-interpreter/Precompiled.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala b/tests/run-custom-args/tasty-interpreter/Test.scala similarity index 100% rename from tests/run-with-compiler-custom-args/tasty-interpreter/Test.scala rename to tests/run-custom-args/tasty-interpreter/Test.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala b/tests/run-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala similarity index 100% rename from tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala rename to tests/run-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala b/tests/run-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala similarity index 100% rename from tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala rename to tests/run-custom-args/tasty-interpreter/interpreter/TreeInterpreter.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala b/tests/run-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala similarity index 100% rename from tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala rename to tests/run-custom-args/tasty-interpreter/interpreter/jvm/Interpreter.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala b/tests/run-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala similarity index 100% rename from tests/run-with-compiler-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala rename to tests/run-custom-args/tasty-interpreter/interpreter/jvm/JVMReflection.scala diff --git a/tests/run-with-compiler-custom-args/tasty-interpreter/notes.md b/tests/run-custom-args/tasty-interpreter/notes.md similarity index 100% rename from tests/run-with-compiler-custom-args/tasty-interpreter/notes.md rename to tests/run-custom-args/tasty-interpreter/notes.md From 83fc85a60c41c119d3a8d8b1438d67e4befe2492 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 24 Jan 2020 14:52:57 +0100 Subject: [PATCH 2/2] Rename tasty-consumer to tasty-inspector Also rework TastyInspector API to simplify use. --- build.sbt | 2 +- compiler/test/dotty/Properties.scala | 4 +- .../BootstrappedOnlyCompilationTests.scala | 4 +- .../tools/vulpix/TestConfiguration.scala | 8 +-- .../metaprogramming/tasty-inspect.md | 8 +-- project/Build.scala | 22 +++--- .../app/Main.scala | 4 +- .../tasty-consumer-example-project/build.sbt | 2 +- .../src/scala/tasty/file/ConsumeTasty.scala | 39 ----------- .../src/scala/tasty/file/TastyConsumer.scala | 7 -- .../scala/tasty/file/TastyConsumerPhase.scala | 18 ----- .../src/scala/tasty/file/TastyFromClass.scala | 26 ------- .../tasty/inspector/TastyInspector.scala | 67 +++++++++++++++++++ tastydoc/src/dotty/tastydoc/Main.scala | 6 +- .../src/dotty/tastydoc/TastydocConsumer.scala | 19 ------ .../dotty/tastydoc/TastydocInspector.scala | 19 ++++++ tastydoc/test/dotty/tastydoc/Tests.scala | 2 - .../tasty-comment-inspector}/Foo.scala | 0 .../tasty-comment-inspector}/Test.scala | 8 +-- .../tasty-inspector}/Foo.scala | 0 .../tasty-inspector}/Test.scala | 8 +-- .../tasty-interpreter/Test.scala | 3 +- .../interpreter/TastyInterpreter.scala | 6 +- 23 files changed, 128 insertions(+), 154 deletions(-) delete mode 100644 tasty-consumer/src/scala/tasty/file/ConsumeTasty.scala delete mode 100644 tasty-consumer/src/scala/tasty/file/TastyConsumer.scala delete mode 100644 tasty-consumer/src/scala/tasty/file/TastyConsumerPhase.scala delete mode 100644 tasty-consumer/src/scala/tasty/file/TastyFromClass.scala create mode 100644 tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala delete mode 100644 tastydoc/src/dotty/tastydoc/TastydocConsumer.scala create mode 100644 tastydoc/src/dotty/tastydoc/TastydocInspector.scala rename tests/run-custom-args/{tasty-consumer/tasty-comment-consumer => tasty-inspector/tasty-comment-inspector}/Foo.scala (100%) rename tests/run-custom-args/{tasty-consumer/tasty-comment-consumer => tasty-inspector/tasty-comment-inspector}/Test.scala (73%) rename tests/run-custom-args/{tasty-consumer/tasty-consumer => tasty-inspector/tasty-inspector}/Foo.scala (100%) rename tests/run-custom-args/{tasty-consumer/tasty-consumer => tasty-inspector/tasty-inspector}/Test.scala (70%) diff --git a/build.sbt b/build.sbt index c4aea02437c7..067eee0b0969 100644 --- a/build.sbt +++ b/build.sbt @@ -11,7 +11,7 @@ val `dotty-library-bootstrappedJS` = Build.`dotty-library-bootstrappedJS` val `dotty-sbt-bridge` = Build.`dotty-sbt-bridge` val `dotty-sbt-bridge-tests` = Build.`dotty-sbt-bridge-tests` val `dotty-staging` = Build.`dotty-staging` -val `dotty-tasty-consumer` = Build.`dotty-tasty-consumer` +val `dotty-tasty-inspector` = Build.`dotty-tasty-inspector` val `dotty-language-server` = Build.`dotty-language-server` val `dotty-bench` = Build.`dotty-bench` val `dotty-bench-bootstrapped` = Build.`dotty-bench-bootstrapped` diff --git a/compiler/test/dotty/Properties.scala b/compiler/test/dotty/Properties.scala index 586d08a8456d..cf9a92d58832 100644 --- a/compiler/test/dotty/Properties.scala +++ b/compiler/test/dotty/Properties.scala @@ -53,8 +53,8 @@ object Properties { /** dotty-staging jar */ def dottyStaging: String = sys.props("dotty.tests.classes.dottyStaging") - /** dotty-tasty-consumer jar */ - def dottyTastyConsumer: String = sys.props("dotty.tests.classes.dottyTastyConsumer") + /** dotty-tasty-inspector jar */ + def dottyTastyInspector: String = sys.props("dotty.tests.classes.dottyTastyInspector") /** tasty-core jar */ def tastyCore: String = sys.props("dotty.tests.classes.tastyCore") diff --git a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala index 4ba5bcb1d541..6f8d4c162b88 100644 --- a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala @@ -127,8 +127,8 @@ class BootstrappedOnlyCompilationTests extends ParallelTesting { aggregateTests( compileFilesInDir("tests/run-with-compiler", withCompilerOptions), compileFilesInDir("tests/run-staging", withStagingOptions), - compileFilesInDir("tests/run-custom-args/tasty-consumer", withTastyConsumerOptions), - compileDir("tests/run-custom-args/tasty-interpreter", withTastyConsumerOptions), + compileFilesInDir("tests/run-custom-args/tasty-inspector", withTastyInspectorOptions), + compileDir("tests/run-custom-args/tasty-interpreter", withTastyInspectorOptions), ).checkRuns() } diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index f4b98c96c1f9..cdea285eceb0 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -39,8 +39,8 @@ object TestConfiguration { lazy val withStagingClasspath = withCompilerClasspath + File.pathSeparator + mkClasspath(List(Properties.dottyStaging)) - lazy val withTastyConsumerClasspath = - withCompilerClasspath + File.pathSeparator + mkClasspath(List(Properties.dottyTastyConsumer)) + lazy val withTastyInspectorClasspath = + withCompilerClasspath + File.pathSeparator + mkClasspath(List(Properties.dottyTastyInspector)) def mkClasspath(classpaths: List[String]): String = classpaths.map({ p => @@ -57,8 +57,8 @@ object TestConfiguration { defaultOptions.withClasspath(withCompilerClasspath).withRunClasspath(withCompilerClasspath) lazy val withStagingOptions = defaultOptions.withClasspath(withStagingClasspath).withRunClasspath(withStagingClasspath) - lazy val withTastyConsumerOptions = - defaultOptions.withClasspath(withTastyConsumerClasspath).withRunClasspath(withTastyConsumerClasspath) + lazy val withTastyInspectorOptions = + defaultOptions.withClasspath(withTastyInspectorClasspath).withRunClasspath(withTastyInspectorClasspath) val allowDeepSubtypes = defaultOptions without "-Yno-deep-subtypes" val allowDoubleBindings = defaultOptions without "-Yno-double-bindings" val picklingOptions = defaultOptions and ( diff --git a/docs/docs/reference/metaprogramming/tasty-inspect.md b/docs/docs/reference/metaprogramming/tasty-inspect.md index 1eefcc0c6daa..01e988470d72 100644 --- a/docs/docs/reference/metaprogramming/tasty-inspect.md +++ b/docs/docs/reference/metaprogramming/tasty-inspect.md @@ -4,13 +4,13 @@ title: "TASTy Inspection" --- ```scala -libraryDependencies += "ch.epfl.lamp" %% "dotty-tasty-consumer" % scalaVersion.value +libraryDependencies += "ch.epfl.lamp" %% "dotty-tasty-inspector" % scalaVersion.value ``` TASTy files contain the full typed tree of a class including source positions and documentation. This is ideal for tools that analyze or extract semantic information of the code. To avoid the hassle of working directly with the TASTy -file we provide the `TastyConsumer` which loads the contents and exposes it +file we provide the `TastyInspector` which loads the contents and exposes it through the TASTy reflect API. @@ -23,7 +23,7 @@ the following way. import scala.tasty.Reflection import scala.tasty.file._ -class Consumer extends TastyConsumer { +class Consumer extends TastyInspector { final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ // Do something with the tree @@ -37,7 +37,7 @@ the class `foo.Bar` for a foo in the classpath. ```scala object Test { def main(args: Array[String]): Unit = { - ConsumeTasty("", List("foo.Bar"), new Consumer) + InspectTasty("", List("foo.Bar"), new Consumer) } } ``` diff --git a/project/Build.scala b/project/Build.scala index 156f1f045e32..9257e880aaae 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -595,10 +595,10 @@ object Build { val asm = findArtifactPath(externalDeps, "scala-asm") val dottyCompiler = jars("dotty-compiler") val dottyStaging = jars("dotty-staging") - val dottyTastyConsumer = jars("dotty-tasty-consumer") + val dottyTastyInspector = jars("dotty-tasty-inspector") val dottyInterfaces = jars("dotty-interfaces") val tastyCore = jars("tasty-core") - run(insertClasspathInArgs(args1, List(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyConsumer, tastyCore).mkString(File.pathSeparator))) + run(insertClasspathInArgs(args1, List(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyInspector, tastyCore).mkString(File.pathSeparator))) } else run(args) }, @@ -672,10 +672,10 @@ object Build { } val dottyInterfaces = jars("dotty-interfaces") val dottyStaging = jars("dotty-staging") - val dottyTastyConsumer = jars("dotty-tasty-consumer") + val dottyTastyInspector = jars("dotty-tasty-inspector") val tastyCore = jars("tasty-core") val asm = findArtifactPath(externalDeps, "scala-asm") - extraClasspath ++= Seq(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyConsumer, tastyCore) + extraClasspath ++= Seq(dottyCompiler, dottyInterfaces, asm, dottyStaging, dottyTastyInspector, tastyCore) } val fullArgs = main :: insertClasspathInArgs(args, extraClasspath.mkString(File.pathSeparator)) @@ -721,14 +721,14 @@ object Build { val jars = packageAll.value Seq( "-Ddotty.tests.classes.dottyStaging=" + jars("dotty-staging"), - "-Ddotty.tests.classes.dottyTastyConsumer=" + jars("dotty-tasty-consumer"), + "-Ddotty.tests.classes.dottyTastyInspector=" + jars("dotty-tasty-inspector"), ) }, packageAll := { packageAll.in(`dotty-compiler`).value ++ Seq( "dotty-compiler" -> packageBin.in(Compile).value.getAbsolutePath, "dotty-staging" -> packageBin.in(LocalProject("dotty-staging"), Compile).value.getAbsolutePath, - "dotty-tasty-consumer" -> packageBin.in(LocalProject("dotty-tasty-consumer"), Compile).value.getAbsolutePath, + "dotty-tasty-inspector" -> packageBin.in(LocalProject("dotty-tasty-inspector"), Compile).value.getAbsolutePath, "tasty-core" -> packageBin.in(LocalProject("tasty-core-bootstrapped"), Compile).value.getAbsolutePath, ) } @@ -807,10 +807,10 @@ object Build { javaOptions := (javaOptions in `dotty-compiler-bootstrapped`).value ) - lazy val `dotty-tasty-consumer` = project.in(file("tasty-consumer")). + lazy val `dotty-tasty-inspector` = project.in(file("tasty-inspector")). withCommonSettings(Bootstrapped). // We want the compiler to be present in the compiler classpath when compiling this project but not - // when compiling a project that depends on dotty-tasty-consumer (see sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project), + // when compiling a project that depends on dotty-tasty-inspector (see sbt-dotty/sbt-test/sbt-dotty/tasty-inspector-example-project), // but we always need it to be present on the JVM classpath at runtime. dependsOn(dottyCompiler(Bootstrapped) % "provided; compile->runtime; test->test"). settings(commonBootstrappedSettings). @@ -1121,7 +1121,7 @@ object Build { publishLocal in `dotty-library-bootstrapped`, publishLocal in `tasty-core-bootstrapped`, publishLocal in `dotty-staging`, - publishLocal in `dotty-tasty-consumer`, + publishLocal in `dotty-tasty-inspector`, publishLocal in `scala-library`, publishLocal in `scala-reflect`, publishLocal in `dotty-doc-bootstrapped`, @@ -1332,7 +1332,7 @@ object Build { def asDottyRoot(implicit mode: Mode): Project = project.withCommonSettings. aggregate(`dotty-interfaces`, dottyLibrary, dottyCompiler, tastyCore, dottyDoc, `dotty-sbt-bridge`). bootstrappedAggregate(`scala-library`, `scala-compiler`, `scala-reflect`, scalap, - `dotty-language-server`, `dotty-staging`, `dotty-tasty-consumer`, `dotty-tastydoc`). + `dotty-language-server`, `dotty-staging`, `dotty-tasty-inspector`, `dotty-tastydoc`). dependsOn(tastyCore). dependsOn(dottyCompiler). dependsOn(dottyLibrary). @@ -1371,7 +1371,7 @@ object Build { def asDottyTastydoc(implicit mode: Mode): Project = project.withCommonSettings. aggregate(`dotty-tastydoc-input`). dependsOn(dottyCompiler). - dependsOn(`dotty-tasty-consumer`). + dependsOn(`dotty-tasty-inspector`). settings(commonDocSettings) def asDottyTastydocInput(implicit mode: Mode): Project = project.withCommonSettings. diff --git a/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/app/Main.scala b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/app/Main.scala index 22824300cab3..be465eefd946 100644 --- a/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/app/Main.scala +++ b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/app/Main.scala @@ -6,7 +6,7 @@ import scala.tasty.file._ object Main extends App { - class Consumer extends TastyConsumer { + class Consumer extends TastyInspector { final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect._ val tastyStr = root.show @@ -14,6 +14,6 @@ object Main extends App { } } - ConsumeTasty("", List("lib.Foo"), new Consumer) + InspectTasty("", List("lib.Foo"), new Consumer) } diff --git a/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/build.sbt b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/build.sbt index cae75a5daa92..9169f4809813 100644 --- a/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/build.sbt +++ b/sbt-dotty/sbt-test/sbt-dotty/tasty-consumer-example-project/build.sbt @@ -10,6 +10,6 @@ lazy val app = project .in(file("app")) .settings( scalaVersion := dottyVersion, - libraryDependencies += "ch.epfl.lamp" %% "dotty-tasty-consumer" % scalaVersion.value, + libraryDependencies += "ch.epfl.lamp" %% "dotty-tasty-inspector" % scalaVersion.value, ) .dependsOn(lib) diff --git a/tasty-consumer/src/scala/tasty/file/ConsumeTasty.scala b/tasty-consumer/src/scala/tasty/file/ConsumeTasty.scala deleted file mode 100644 index e1c3120b89ce..000000000000 --- a/tasty-consumer/src/scala/tasty/file/ConsumeTasty.scala +++ /dev/null @@ -1,39 +0,0 @@ -package scala.tasty.file - -import java.net.URLClassLoader - -import dotty.tools.dotc -import dotty.tools.dotc.core.Contexts._ -import dotty.tools.dotc.util.ClasspathFromClassloader - -import java.nio.file.Paths -import java.io.File -import java.io.File.{ pathSeparator => sep } -import scala.annotation.tailrec - -import scala.tasty.file.TastyConsumer - -// TODO: Move this logic into TastyConumer to simplify the API -object ConsumeTasty { - - /** Load and process TASTy files using TASTy reflect - * - * @param classpath Classpath where the classes are located - * @param classes classes to be consumed - * @param tastyConsumer consumer that will process the tasty trees - */ - def apply(classpath: String, classes: List[String], tastyConsumer: TastyConsumer): Unit = { - if (classes.isEmpty) - throw new IllegalArgumentException("Parameter classes should no be empty") - - class Consume extends dotc.Driver { - override protected def newCompiler(implicit ctx: Context): dotc.Compiler = - new TastyFromClass(tastyConsumer) - } - - val currentClasspath = ClasspathFromClassloader(getClass.getClassLoader) - val args = "-from-tasty" :: "-Yretain-trees" :: "-classpath" :: s"$classpath$sep$currentClasspath" :: classes - (new Consume).process(args.toArray) - } - -} diff --git a/tasty-consumer/src/scala/tasty/file/TastyConsumer.scala b/tasty-consumer/src/scala/tasty/file/TastyConsumer.scala deleted file mode 100644 index 50ab240436f2..000000000000 --- a/tasty-consumer/src/scala/tasty/file/TastyConsumer.scala +++ /dev/null @@ -1,7 +0,0 @@ -package scala.tasty.file - -import scala.tasty.Reflection - -trait TastyConsumer { - def apply(reflect: Reflection)(root: reflect.Tree): Unit -} diff --git a/tasty-consumer/src/scala/tasty/file/TastyConsumerPhase.scala b/tasty-consumer/src/scala/tasty/file/TastyConsumerPhase.scala deleted file mode 100644 index 46b710d72977..000000000000 --- a/tasty-consumer/src/scala/tasty/file/TastyConsumerPhase.scala +++ /dev/null @@ -1,18 +0,0 @@ -package scala.tasty.file - -import dotty.tools.dotc.core.Contexts._ -import dotty.tools.dotc.core.Phases.Phase -import dotty.tools.dotc.tastyreflect.ReflectionImpl - -import scala.tasty.file.TastyConsumer - -private class TastyConsumerPhase(consumer: TastyConsumer) extends Phase { - - override def phaseName: String = "tastyConsumer" - - override def run(implicit ctx: Context): Unit = { - val reflect = ReflectionImpl(ctx) - consumer(reflect)(ctx.compilationUnit.tpdTree.asInstanceOf[reflect.Tree]) - } -} - diff --git a/tasty-consumer/src/scala/tasty/file/TastyFromClass.scala b/tasty-consumer/src/scala/tasty/file/TastyFromClass.scala deleted file mode 100644 index 38c0356b0d03..000000000000 --- a/tasty-consumer/src/scala/tasty/file/TastyFromClass.scala +++ /dev/null @@ -1,26 +0,0 @@ -package scala.tasty.file - -import dotty.tools.dotc.Run -import dotty.tools.dotc.core.Mode -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Phases.Phase -import dotty.tools.dotc.fromtasty._ - -private class TastyFromClass(consumer: TastyConsumer) extends TASTYCompiler { - - override protected def frontendPhases: List[List[Phase]] = - List(new ReadTasty) :: // Load classes from tasty - Nil - - override protected def picklerPhases: List[List[Phase]] = Nil - override protected def transformPhases: List[List[Phase]] = Nil - - override protected def backendPhases: List[List[Phase]] = - List(new TastyConsumerPhase(consumer)) :: // Print all loaded classes - Nil - - override def newRun(implicit ctx: Context): Run = { - reset() - new TASTYRun(this, ctx.fresh.addMode(Mode.ReadPositions).addMode(Mode.ReadComments)) - } -} diff --git a/tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala b/tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala new file mode 100644 index 000000000000..e4a0a0514777 --- /dev/null +++ b/tasty-inspector/src/scala/tasty/inspector/TastyInspector.scala @@ -0,0 +1,67 @@ +package scala.tasty.inspector + +import scala.tasty.Reflection + +import dotty.tools.dotc.Compiler +import dotty.tools.dotc.Driver +import dotty.tools.dotc.Run +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Mode +import dotty.tools.dotc.core.Phases.Phase +import dotty.tools.dotc.fromtasty._ +import dotty.tools.dotc.tastyreflect.ReflectionImpl +import dotty.tools.dotc.util.ClasspathFromClassloader + +import java.io.File.pathSeparator + +trait TastyInspector { self => + + /** Process a TASTy file using TASTy reflect */ + protected def processCompilationUnit(reflect: Reflection)(root: reflect.Tree): Unit + + /** Load and process TASTy files using TASTy reflect + * + * @param classpath Classpath where the classes are located + * @param classes classes to be inspected + */ + def inspect(classpath: String, classes: List[String]): Unit = { + if (classes.isEmpty) + throw new IllegalArgumentException("Parameter classes should no be empty") + + class InspectorDriver extends Driver { + override protected def newCompiler(implicit ctx: Context): Compiler = new TastyFromClass + } + + class TastyFromClass extends TASTYCompiler { + override protected def frontendPhases: List[List[Phase]] = + List(new ReadTasty) :: // Load classes from tasty + Nil + + override protected def picklerPhases: List[List[Phase]] = Nil + override protected def transformPhases: List[List[Phase]] = Nil + + override protected def backendPhases: List[List[Phase]] = + List(new TastyInspectorPhase) :: // Print all loaded classes + Nil + + override def newRun(implicit ctx: Context): Run = { + reset() + new TASTYRun(this, ctx.fresh.addMode(Mode.ReadPositions).addMode(Mode.ReadComments)) + } + } + + class TastyInspectorPhase extends Phase { + override def phaseName: String = "tastyInspector" + + override def run(implicit ctx: Context): Unit = { + val reflect = ReflectionImpl(ctx) + self.processCompilationUnit(reflect)(ctx.compilationUnit.tpdTree.asInstanceOf[reflect.Tree]) + } + } + + val currentClasspath = ClasspathFromClassloader(getClass.getClassLoader) + val args = "-from-tasty" :: "-Yretain-trees" :: "-classpath" :: s"$classpath$pathSeparator$currentClasspath" :: classes + (new InspectorDriver).process(args.toArray) + } + +} diff --git a/tastydoc/src/dotty/tastydoc/Main.scala b/tastydoc/src/dotty/tastydoc/Main.scala index 01af8156975c..0c85a0d0694a 100644 --- a/tastydoc/src/dotty/tastydoc/Main.scala +++ b/tastydoc/src/dotty/tastydoc/Main.scala @@ -1,6 +1,6 @@ package dotty.tastydoc -import scala.tasty.file._ +import scala.tasty.inspector._ import dotty.tastydoc.representations._ @@ -92,8 +92,8 @@ object Main { } else { println("Running Dotty Tastydoc on: " + classes.mkString(" ")) val mutablePackagesMap: scala.collection.mutable.HashMap[String, EmulatedPackageRepresentation] = new scala.collection.mutable.HashMap[String, EmulatedPackageRepresentation]() - val tc = new TastydocConsumer(mutablePackagesMap) - ConsumeTasty(extraClasspath, classes, tc) + val tc = new TastydocInspector(mutablePackagesMap) + tc.inspect(extraClasspath, classes) representations.setSubClasses(mutablePackagesMap) diff --git a/tastydoc/src/dotty/tastydoc/TastydocConsumer.scala b/tastydoc/src/dotty/tastydoc/TastydocConsumer.scala deleted file mode 100644 index 9e78f8b59c0c..000000000000 --- a/tastydoc/src/dotty/tastydoc/TastydocConsumer.scala +++ /dev/null @@ -1,19 +0,0 @@ -package dotty.tastydoc - -import scala.tasty.Reflection -import scala.tasty.file.TastyConsumer - -import dotty.tastydoc.representations._ - -/* Extends TastyConsumer and consumes Tasty Files to produce Representations - * - * @param mutablePackagesMap A mutable HashMap where seen packages are added - */ -class TastydocConsumer(mutablePackagesMap: scala.collection.mutable.HashMap[String, EmulatedPackageRepresentation]) extends TastyConsumer { - - final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { - import reflect._ - - representations.convertToRepresentation(reflect)(root, None)(given mutablePackagesMap) - } -} diff --git a/tastydoc/src/dotty/tastydoc/TastydocInspector.scala b/tastydoc/src/dotty/tastydoc/TastydocInspector.scala new file mode 100644 index 000000000000..610e67c5f1c1 --- /dev/null +++ b/tastydoc/src/dotty/tastydoc/TastydocInspector.scala @@ -0,0 +1,19 @@ +package dotty.tastydoc + +import scala.tasty.Reflection +import scala.tasty.inspector.TastyInspector + +import dotty.tastydoc.representations._ + +/* Extends TastyInspector and consumes Tasty Files to produce Representations + * + * @param mutablePackagesMap A mutable HashMap where seen packages are added + */ +class TastydocInspector(mutablePackagesMap: scala.collection.mutable.HashMap[String, EmulatedPackageRepresentation]) extends TastyInspector { + + protected def processCompilationUnit(reflect: Reflection)(root: reflect.Tree): Unit = { + import reflect._ + + representations.convertToRepresentation(reflect)(root, None)(given mutablePackagesMap) + } +} diff --git a/tastydoc/test/dotty/tastydoc/Tests.scala b/tastydoc/test/dotty/tastydoc/Tests.scala index 2783565d41a5..e05499ee1547 100644 --- a/tastydoc/test/dotty/tastydoc/Tests.scala +++ b/tastydoc/test/dotty/tastydoc/Tests.scala @@ -1,7 +1,6 @@ package dotty.tastydoc import scala.tasty.Reflection -import scala.tasty.file._ import scala.collection.mutable.HashMap import org.junit.Test @@ -10,7 +9,6 @@ import java.nio.file._ import scala.collection.JavaConverters._ import java.io.File import scala.tasty.Reflection -import scala.tasty.file.TastyConsumer import java.lang.reflect.InvocationTargetException class Tests { diff --git a/tests/run-custom-args/tasty-consumer/tasty-comment-consumer/Foo.scala b/tests/run-custom-args/tasty-inspector/tasty-comment-inspector/Foo.scala similarity index 100% rename from tests/run-custom-args/tasty-consumer/tasty-comment-consumer/Foo.scala rename to tests/run-custom-args/tasty-inspector/tasty-comment-inspector/Foo.scala diff --git a/tests/run-custom-args/tasty-consumer/tasty-comment-consumer/Test.scala b/tests/run-custom-args/tasty-inspector/tasty-comment-inspector/Test.scala similarity index 73% rename from tests/run-custom-args/tasty-consumer/tasty-comment-consumer/Test.scala rename to tests/run-custom-args/tasty-inspector/tasty-comment-inspector/Test.scala index 0980dc5d6a32..71dbf6518c1a 100644 --- a/tests/run-custom-args/tasty-consumer/tasty-comment-consumer/Test.scala +++ b/tests/run-custom-args/tasty-inspector/tasty-comment-inspector/Test.scala @@ -1,15 +1,15 @@ import scala.tasty.Reflection -import scala.tasty.file._ +import scala.tasty.inspector._ object Test { def main(args: Array[String]): Unit = { - ConsumeTasty("", List("Foo"), new CommentConsumer) + new CommentInspector().inspect("", List("Foo")) } } -class CommentConsumer extends TastyConsumer { +class CommentInspector extends TastyInspector { - final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { + def processCompilationUnit(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect.{_, given _} object Traverser extends TreeTraverser { diff --git a/tests/run-custom-args/tasty-consumer/tasty-consumer/Foo.scala b/tests/run-custom-args/tasty-inspector/tasty-inspector/Foo.scala similarity index 100% rename from tests/run-custom-args/tasty-consumer/tasty-consumer/Foo.scala rename to tests/run-custom-args/tasty-inspector/tasty-inspector/Foo.scala diff --git a/tests/run-custom-args/tasty-consumer/tasty-consumer/Test.scala b/tests/run-custom-args/tasty-inspector/tasty-inspector/Test.scala similarity index 70% rename from tests/run-custom-args/tasty-consumer/tasty-consumer/Test.scala rename to tests/run-custom-args/tasty-inspector/tasty-inspector/Test.scala index 87e4aab461e7..1d64ffde07f2 100644 --- a/tests/run-custom-args/tasty-consumer/tasty-consumer/Test.scala +++ b/tests/run-custom-args/tasty-inspector/tasty-inspector/Test.scala @@ -1,15 +1,15 @@ import scala.tasty.Reflection -import scala.tasty.file._ +import scala.tasty.inspector._ object Test { def main(args: Array[String]): Unit = { - ConsumeTasty("", List("Foo"), new DBConsumer) + new DBInspector().inspect("", List("Foo")) } } -class DBConsumer extends TastyConsumer { +class DBInspector extends TastyInspector { - final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { + protected def processCompilationUnit(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect.{_, given _} object Traverser extends TreeTraverser { diff --git a/tests/run-custom-args/tasty-interpreter/Test.scala b/tests/run-custom-args/tasty-interpreter/Test.scala index 9134c304cc7f..7feff7c01300 100644 --- a/tests/run-custom-args/tasty-interpreter/Test.scala +++ b/tests/run-custom-args/tasty-interpreter/Test.scala @@ -7,7 +7,6 @@ import dotty.tools.dotc.util.DiffUtil import dotty.tools.io.Path import scala.io.Source -import scala.tasty.file._ import scala.tasty.interpreter.TastyInterpreter object Test { @@ -103,7 +102,7 @@ object Test { def interpret(classpath: String*)(interpretedClasses: String*): String = { val ps = new ByteArrayOutputStream() try scala.Console.withOut(ps) { - ConsumeTasty(classpath.mkString(java.io.File.pathSeparatorChar.toString), interpretedClasses.toList, new TastyInterpreter) + new TastyInterpreter().inspect(classpath.mkString(java.io.File.pathSeparatorChar.toString), interpretedClasses.toList) } catch { case e: Throwable => throw new Exception(ps.toString, e) } diff --git a/tests/run-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala b/tests/run-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala index e73846f25780..7c627eba989a 100644 --- a/tests/run-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala +++ b/tests/run-custom-args/tasty-interpreter/interpreter/TastyInterpreter.scala @@ -1,11 +1,11 @@ package scala.tasty.interpreter import scala.tasty.Reflection -import scala.tasty.file.TastyConsumer +import scala.tasty.inspector.TastyInspector -class TastyInterpreter extends TastyConsumer { +class TastyInterpreter extends TastyInspector { - final def apply(reflect: Reflection)(root: reflect.Tree): Unit = { + protected def processCompilationUnit(reflect: Reflection)(root: reflect.Tree): Unit = { import reflect.{_, given _} object Traverser extends TreeTraverser {