diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index f319432199a6..b1c9496e3a8f 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -1,13 +1,16 @@ package dotty.tools.dotc +import java.nio.file.{Files, Paths} + import dotty.tools.FatalError import config.CompilerCommand import core.Comments.{ContextDoc, ContextDocstrings} import core.Contexts.{Context, ContextBase} import core.Mode import reporting._ + import scala.util.control.NonFatal -import fromtasty.TASTYCompiler +import fromtasty.{TASTYCompiler, TastyFileUtil} /** Run the Dotty compiler. * @@ -54,7 +57,34 @@ class Driver { } val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)(ctx) - (fileNames, ctx) + fromTastySetup(fileNames, ctx) + } + + /** Setup extra classpath and figure out class names for tasty file inputs */ + private def fromTastySetup(fileNames0: List[String], ctx0: Context) = { + if (ctx0.settings.fromTasty.value(ctx0)) { + // Resolve classpath and class names of tasty files + val (classPaths, classNames) = fileNames0.map { name => + val path = Paths.get(name) + if (!name.endsWith(".tasty")) ("", name) + else if (Files.exists(path)) { + TastyFileUtil.getClassName(path) match { + case Some(res) => res + case _ => + ctx0.error(s"Could not load classname from $name.") + ("", name) + } + } else { + ctx0.error(s"File $name does not exist.") + ("", name) + } + }.unzip + val ctx1 = ctx0.fresh + val classPaths1 = classPaths.distinct.filter(_ != "") + val fullClassPath = (classPaths1 :+ ctx1.settings.classpath.value(ctx1)).mkString(java.io.File.pathSeparator) + ctx1.setSetting(ctx1.settings.classpath, fullClassPath) + (classNames, ctx1) + } else (fileNames0, ctx0) } /** Entry point to the compiler that can be conveniently used with Java reflection. diff --git a/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala index fdb003a8a181..57125fed3e65 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala @@ -16,7 +16,7 @@ object DottyUnpickler { class BadSignature(msg: String) extends RuntimeException(msg) class TreeSectionUnpickler(posUnpickler: Option[PositionUnpickler], commentUnpickler: Option[CommentUnpickler]) - extends SectionUnpickler[TreeUnpickler]("ASTs") { + extends SectionUnpickler[TreeUnpickler](TreePickler.sectionName) { def unpickle(reader: TastyReader, nameAtRef: NameTable): TreeUnpickler = new TreeUnpickler(reader, nameAtRef, posUnpickler, commentUnpickler, Seq.empty) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyClassName.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyClassName.scala new file mode 100644 index 000000000000..ab7ead709560 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyClassName.scala @@ -0,0 +1,58 @@ +package dotty.tools.dotc +package core +package tasty + +import Contexts._, Decorators._ +import Names.{Name, TermName} +import StdNames.nme +import TastyUnpickler._ +import TastyBuffer.NameRef +import util.Positions.offsetToInt +import printing.Highlighting._ + +/** Reads the package and class name of the class contained in this TASTy */ +class TastyClassName(bytes: Array[Byte]) { + + val unpickler: TastyUnpickler = new TastyUnpickler(bytes) + import unpickler.{nameAtRef, unpickle} + + /** Returns a tuple with the package and class names */ + def readName(): Option[(TermName, TermName)] = unpickle(new TreeSectionUnpickler) + + class TreeSectionUnpickler extends SectionUnpickler[(TermName, TermName)](TreePickler.sectionName) { + import TastyFormat._ + def unpickle(reader: TastyReader, tastyName: NameTable): (TermName, TermName) = { + import reader._ + def readName() = { + val idx = readNat() + nameAtRef(NameRef(idx)) + } + def readNames(packageName: TermName): (TermName, TermName) = { + val tag = readByte() + if (tag >= firstLengthTreeTag) { + val len = readNat() + val end = currentAddr + len + tag match { + case TYPEDEF => + val className = readName() + goto(end) + (packageName, className) + case IMPORT | VALDEF => + goto(end) + readNames(packageName) + case PACKAGE => + readNames(packageName) + } + } + else tag match { + case TERMREFpkg | TYPEREFpkg => + val subPackageName = readName() + readNames(subPackageName) + case _ => + readNames(packageName) + } + } + readNames(nme.EMPTY_PACKAGE) + } + } +} diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index 4bd53cdb8d2f..afaecd8c0ef8 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -34,7 +34,7 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { unpickle(new CommentSectionUnpickler) } - class TreeSectionUnpickler extends SectionUnpickler[Unit]("ASTs") { + class TreeSectionUnpickler extends SectionUnpickler[Unit](TreePickler.sectionName) { import TastyFormat._ def unpickle(reader: TastyReader, tastyName: NameTable): Unit = { import reader._ diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 2960c4ebc3f7..3c76acf4f2e1 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -17,6 +17,8 @@ import printing.Texts._ object TreePickler { + val sectionName = "ASTs" + case class Hole(idx: Int, args: List[tpd.Tree]) extends tpd.Tree { override def fallbackToText(printer: Printer): Text = s"[[$idx|" ~~ printer.toTextGlobal(args, ", ") ~~ "]]" @@ -25,7 +27,7 @@ object TreePickler { class TreePickler(pickler: TastyPickler) { val buf: TreeBuffer = new TreeBuffer - pickler.newSection("ASTs", buf) + pickler.newSection(TreePickler.sectionName, buf) import TreePickler._ import buf._ import pickler.nameBuffer.nameIndex diff --git a/compiler/src/dotty/tools/dotc/fromtasty/TastyFileUtil.scala b/compiler/src/dotty/tools/dotc/fromtasty/TastyFileUtil.scala new file mode 100644 index 000000000000..89da2374276f --- /dev/null +++ b/compiler/src/dotty/tools/dotc/fromtasty/TastyFileUtil.scala @@ -0,0 +1,38 @@ +package dotty.tools.dotc +package fromtasty + +import java.nio.file.{Files, Path, Paths} +import java.io + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.NameKinds +import dotty.tools.dotc.core.Names.SimpleName +import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.core.tasty.{TastyUnpickler, TreePickler} + +object TastyFileUtil { + + /** Get the class path and the class name including packages + * + * If + * ```scala + * package foo + * class Foo + * ``` + * then `getClassName("./out/foo/Foo.tasty") returns `Some(("./out", "foo.Foo"))` + */ + def getClassName(path: Path): Option[(String, String)] = { + assert(path.toString.endsWith(".tasty")) + assert(Files.exists(path)) + val bytes = Files.readAllBytes(path) + val names = new core.tasty.TastyClassName(bytes).readName() + names.map { case (packageName, className) => + val fullName = s"$packageName.${className.lastPart}" + val classInPath = fullName.replace(".", io.File.separator) + ".tasty" + val classpath = path.toString.replace(classInPath, "") + (classpath, fullName) + } + + } + +}