Skip to content

Compile from Tasty #507

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 10 commits into from
May 2, 2015
14 changes: 0 additions & 14 deletions src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,4 @@ class CompilationUnit(val source: SourceFile) {
* Subsequent phases can add new sections.
*/
var picklers: Map[ClassSymbol, TastyPickler] = Map()

/**
* Addresses in TASTY file of trees, stored by pickling.
* Note that trees are checked for reference equality,
* so one can reliably use this function only dirrectly after `pickler`
*/
var addrOfTree: tpd.Tree => Option[Addr] = (_ => None)

/**
* Addresses in TASTY file of symbols, stored by pickling.
* Note that trees are checked for reference equality,
* so one can reliably use this function only dirrectly after `pickler`
*/
var addrOfSym: Symbol => Option[Addr] = (_ => None)
}
8 changes: 6 additions & 2 deletions src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Symbols._
import Scopes._
import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks}
import reporting.ConsoleReporter
import dotty.tools.dotc.core.Phases.Phase
import Phases.Phase
import dotty.tools.dotc.transform._
import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, TreeTransformer}
import dotty.tools.dotc.core.DenotTransformers.DenotTransformer
Expand Down Expand Up @@ -101,9 +101,13 @@ class Compiler {
(start.setRunInfo(new RunInfo(start)) /: defn.RootImports)(addImport)
}

def newRun(implicit ctx: Context): Run = {
def reset()(implicit ctx: Context): Unit = {
ctx.base.reset()
ctx.runInfo.clear()
}

def newRun(implicit ctx: Context): Run = {
reset()
new Run(this)(rootContext)
}
}
102 changes: 102 additions & 0 deletions src/dotty/tools/dotc/FromTasty.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* dotc
* Copyright 2005-2015 LAMP/EPFL
* @author Martin Odersky
*/
package dotty.tools
package dotc

import core._
import Contexts._
import Symbols._
import SymDenotations._
import typer.FrontEnd
import Phases.Phase
import util._
import Decorators._
import dotty.tools.dotc.transform.Pickler
import pickling.DottyUnpickler
import ast.tpd._

/** Compiler for TASTY files.
* Usage:
*
* scala dotty.tools.dotc.FromTasty (option | classname)*
*
* Options are as for dotc.
* Classnames are fully qualified names of top-level classes that need to have a TASTY attribute.
* Example:
*
* scala dotty.tools.dotc.FromTasty -Xprint:front extMethods.T
*/
object FromTasty extends Driver {
override def newCompiler(): Compiler = new TASTYCompiler

class TASTYCompiler extends Compiler {

override def phases: List[List[Phase]] = {
val backendPhases = super.phases.dropWhile {
case List(_: Pickler) => false
case _ => true
}.tail
List(new ReadTastyTreesFromClasses) :: backendPhases
}

override def newRun(implicit ctx: Context): Run = {
reset()
new TASTYRun(this)(rootContext)
}
}

class TASTYRun(comp: Compiler)(implicit ctx: Context) extends Run(comp) {
override def compile(classNames: List[String]) = {
units = classNames.map(new TASTYCompilationUnit(_))
compileUnits()
}
}

class TASTYCompilationUnit(val className: String) extends CompilationUnit(NoSource) {
override def toString = s"class file $className"
}

object force extends TreeTraverser {
def traverse(tree: Tree)(implicit ctx: Context): Unit = traverseChildren(tree)
}

class ReadTastyTreesFromClasses extends FrontEnd {
override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] =
units.map(readTASTY)

def readTASTY(unit: CompilationUnit)(implicit ctx: Context): CompilationUnit = unit match {
case unit: TASTYCompilationUnit =>
val className = unit.className.toTypeName
val clsd =
if (className.contains('.')) ctx.base.staticRef(className)
else defn.EmptyPackageClass.info.decl(className)
def cannotUnpickle(reason: String) = {
ctx.error(s"class $className cannot be unpickled because $reason")
unit
}
clsd match {
case clsd: ClassDenotation =>
clsd.infoOrCompleter match {
case info: ClassfileLoader =>
info.load(clsd) match {
case Some(unpickler: DottyUnpickler) =>
val (List(unpickled), source) = unpickler.body(readPositions = true)
val unit1 = new CompilationUnit(source)
unit1.tpdTree = unpickled
force.traverse(unit1.tpdTree)
unit1
case _ =>
cannotUnpickle(s"its class file ${info.classfile} does not have a TASTY attribute")
}
case info =>
cannotUnpickle(s"its info of type ${info.getClass} is not a ClassfileLoader")
}
case _ =>
ctx.error(s"class not found: $className")
unit
}
}
}
}
30 changes: 16 additions & 14 deletions src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,25 @@ class Run(comp: Compiler)(implicit ctx: Context) {
* or we need to assmeble phases on each run, and take -Yskip, -Ystop into
* account. I think the latter would be preferable.
*/
def compileSources(sources: List[SourceFile]) = Stats.monitorHeartBeat {
def compileSources(sources: List[SourceFile]) =
if (sources forall (_.exists)) {
val phases = ctx.squashPhases(ctx.phasePlan,
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.YstopAfter.value, ctx.settings.Ycheck.value)
ctx.usePhases(phases)
units = sources map (new CompilationUnit(_))
for (phase <- ctx.allPhases)
if (!ctx.reporter.hasErrors) {
if (ctx.settings.verbose.value) println(s"[$phase]")
units = phase.runOn(units)
def foreachUnit(op: Context => Unit)(implicit ctx: Context): Unit =
for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
if (ctx.settings.Xprint.value.containsPhase(phase))
foreachUnit(printTree)

}
compileUnits()
}

protected def compileUnits() = Stats.monitorHeartBeat {
val phases = ctx.squashPhases(ctx.phasePlan,
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.YstopAfter.value, ctx.settings.Ycheck.value)
ctx.usePhases(phases)
for (phase <- ctx.allPhases)
if (!ctx.reporter.hasErrors) {
if (ctx.settings.verbose.value) println(s"[$phase]")
units = phase.runOn(units)
def foreachUnit(op: Context => Unit)(implicit ctx: Context): Unit =
for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
if (ctx.settings.Xprint.value.containsPhase(phase))
foreachUnit(printTree)
}
}

private def printTree(ctx: Context) = {
Expand Down
5 changes: 3 additions & 2 deletions src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package core

import Types._, Contexts._, Symbols._, Denotations._, SymDenotations._, StdNames._, Names._
import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._
import pickling.UnPickler.ensureConstructor
import pickling.Scala2Unpickler.ensureConstructor
import scala.annotation.{ switch, meta }
import scala.collection.{ mutable, immutable }
import PartialFunction._
Expand Down Expand Up @@ -99,7 +99,8 @@ class Definitions {
lazy val RootPackage: TermSymbol = ctx.newSymbol(
NoSymbol, nme.ROOTPKG, PackageCreationFlags, TypeRef(NoPrefix, RootClass))

lazy val EmptyPackageVal = ctx.newCompletePackageSymbol(RootClass, nme.EMPTY_PACKAGE).entered
lazy val EmptyPackageVal = ctx.newPackageSymbol(
RootClass, nme.EMPTY_PACKAGE, (emptypkg, emptycls) => ctx.rootLoader(emptypkg)).entered
lazy val EmptyPackageClass = EmptyPackageVal.moduleClass.asClass

/** A package in which we can place all methods that are interpreted specially by the compiler */
Expand Down
5 changes: 4 additions & 1 deletion src/dotty/tools/dotc/core/SymbolLoaders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,10 @@ class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {
else (rootDenot, linkedDenot)
}

def doComplete(root: SymDenotation)(implicit ctx: Context): Unit = {
override def doComplete(root: SymDenotation)(implicit ctx: Context): Unit =
load(root)

def load(root: SymDenotation)(implicit ctx: Context): Option[ClassfileParser.Embedded] = {
val (classRoot, moduleRoot) = rootDenots(root.asClass)
new ClassfileParser(classfile, classRoot, moduleRoot)(ctx).run()
}
Expand Down
74 changes: 41 additions & 33 deletions src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package core
package pickling

import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._
import SymDenotations._, UnPickler._, Constants._, Annotations._, util.Positions._
import SymDenotations._, Scala2Unpickler._, Constants._, Annotations._, util.Positions._
import ast.tpd._
import java.io.{ File, IOException }
import java.lang.Integer.toHexString
Expand All @@ -15,12 +15,18 @@ import typer.Checking.checkNonCyclic
import io.AbstractFile
import scala.util.control.NonFatal

object ClassfileParser {
/** Marker trait for unpicklers that can be embedded in classfiles. */
trait Embedded
}

class ClassfileParser(
classfile: AbstractFile,
classRoot: ClassDenotation,
moduleRoot: ClassDenotation)(ictx: Context) {

import ClassfileConstants._
import ClassfileParser._

protected val in = new AbstractFileReader(classfile)

Expand All @@ -41,7 +47,7 @@ class ClassfileParser(
private def mismatchError(c: Symbol) =
throw new IOException(s"class file '${in.file}' has location not matching its contents: contains $c")

def run()(implicit ctx: Context): Unit = try {
def run()(implicit ctx: Context): Option[Embedded] = try {
ctx.debuglog("[class] >> " + classRoot.fullName)
parseHeader
this.pool = new ConstantPool
Expand Down Expand Up @@ -80,7 +86,7 @@ class ClassfileParser(

var sawPrivateConstructor = false

def parseClass()(implicit ctx: Context): Unit = {
def parseClass()(implicit ctx: Context): Option[Embedded] = {
val jflags = in.nextChar
val isAnnotation = hasAnnotation(jflags)
val sflags = FlagTranslation.classFlags(jflags)
Expand All @@ -94,8 +100,6 @@ class ClassfileParser(

addEnclosingTParams()

if (unpickleOrParseInnerClasses()) return

/** Parse parents for Java classes. For Scala, return AnyRef, since the real type will be unpickled.
* Updates the read pointer of 'in'. */
def parseParents: List[Type] = {
Expand All @@ -114,33 +118,36 @@ class ClassfileParser(
superType :: ifaces
}

var classInfo: Type = TempClassInfoType(parseParents, instanceScope, classRoot.symbol)
val result = unpickleOrParseInnerClasses()
if (!result.isDefined) {
var classInfo: Type = TempClassInfoType(parseParents, instanceScope, classRoot.symbol)
// might be reassigned by later parseAttributes
val staticInfo = TempClassInfoType(List(), staticScope, moduleRoot.symbol)
val staticInfo = TempClassInfoType(List(), staticScope, moduleRoot.symbol)

enterOwnInnerClasses
enterOwnInnerClasses

classRoot.setFlag(sflags)
moduleRoot.setFlag(Flags.JavaDefined | Flags.ModuleClassCreationFlags)
setPrivateWithin(classRoot, jflags)
setPrivateWithin(moduleRoot, jflags)
setPrivateWithin(moduleRoot.sourceModule, jflags)
classRoot.setFlag(sflags)
moduleRoot.setFlag(Flags.JavaDefined | Flags.ModuleClassCreationFlags)
setPrivateWithin(classRoot, jflags)
setPrivateWithin(moduleRoot, jflags)
setPrivateWithin(moduleRoot.sourceModule, jflags)

for (i <- 0 until in.nextChar) parseMember(method = false)
for (i <- 0 until in.nextChar) parseMember(method = true)
classInfo = parseAttributes(classRoot.symbol, classInfo)
if (isAnnotation) addAnnotationConstructor(classInfo)
for (i <- 0 until in.nextChar) parseMember(method = false)
for (i <- 0 until in.nextChar) parseMember(method = true)
classInfo = parseAttributes(classRoot.symbol, classInfo)
if (isAnnotation) addAnnotationConstructor(classInfo)

val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, classRoot, moduleRoot)
if (companionClassMethod.exists) companionClassMethod.entered
val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, moduleRoot, classRoot)
if (companionModuleMethod.exists) companionModuleMethod.entered
val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, classRoot, moduleRoot)
if (companionClassMethod.exists) companionClassMethod.entered
val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, moduleRoot, classRoot)
if (companionModuleMethod.exists) companionModuleMethod.entered

setClassInfo(classRoot, classInfo)
setClassInfo(moduleRoot, staticInfo)
setClassInfo(classRoot, classInfo)
setClassInfo(moduleRoot, staticInfo)
}
result
}


/** Add type parameters of enclosing classes */
def addEnclosingTParams()(implicit ctx: Context): Unit = {
var sym = classRoot.owner
Expand Down Expand Up @@ -644,7 +651,7 @@ class ClassfileParser(
* Restores the old `bp`.
* @return true iff classfile is from Scala, so no Java info needs to be read.
*/
def unpickleOrParseInnerClasses()(implicit ctx: Context): Boolean = {
def unpickleOrParseInnerClasses()(implicit ctx: Context): Option[Embedded] = {
val oldbp = in.bp
try {
skipSuperclasses()
Expand All @@ -664,15 +671,16 @@ class ClassfileParser(
i < attrs
}

def unpickleScala(bytes: Array[Byte]): Boolean = {
new UnPickler(bytes, classRoot, moduleRoot)(ctx).run()
true
def unpickleScala(bytes: Array[Byte]): Some[Embedded] = {
val unpickler = new Scala2Unpickler(bytes, classRoot, moduleRoot)(ctx)
unpickler.run()
Some(unpickler)
}

def unpickleTASTY(bytes: Array[Byte]): Boolean = {
new DottyUnpickler(bytes)
.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))
true
def unpickleTASTY(bytes: Array[Byte]): Some[Embedded] = {
val unpickler = new DottyUnpickler(bytes)
unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))
Some(unpickler)
}

def parseScalaSigBytes: Array[Byte] = {
Expand Down Expand Up @@ -739,7 +747,7 @@ class ClassfileParser(
}
}
}
false
None
} finally in.bp = oldbp
}

Expand Down
Loading