Skip to content

Allow tasty files to be the input of -from-tasty #5325

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions compiler/src/dotty/tools/dotc/Driver.scala
Original file line number Diff line number Diff line change
@@ -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.
*
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
58 changes: 58 additions & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyClassName.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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._
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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, ", ") ~~ "]]"
Expand All @@ -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
Expand Down
38 changes: 38 additions & 0 deletions compiler/src/dotty/tools/dotc/fromtasty/TastyFileUtil.scala
Original file line number Diff line number Diff line change
@@ -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)
}

}

}