Skip to content

Commit ac46a0e

Browse files
committed
Merge pull request #507 from dotty-staging/add/from-tasty
Compile from Tasty
2 parents 2dbabca + 10a5f91 commit ac46a0e

13 files changed

+228
-87
lines changed

src/dotty/tools/dotc/CompilationUnit.scala

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,4 @@ class CompilationUnit(val source: SourceFile) {
2424
* Subsequent phases can add new sections.
2525
*/
2626
var picklers: Map[ClassSymbol, TastyPickler] = Map()
27-
28-
/**
29-
* Addresses in TASTY file of trees, stored by pickling.
30-
* Note that trees are checked for reference equality,
31-
* so one can reliably use this function only dirrectly after `pickler`
32-
*/
33-
var addrOfTree: tpd.Tree => Option[Addr] = (_ => None)
34-
35-
/**
36-
* Addresses in TASTY file of symbols, stored by pickling.
37-
* Note that trees are checked for reference equality,
38-
* so one can reliably use this function only dirrectly after `pickler`
39-
*/
40-
var addrOfSym: Symbol => Option[Addr] = (_ => None)
4127
}

src/dotty/tools/dotc/Compiler.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Symbols._
88
import Scopes._
99
import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks}
1010
import reporting.ConsoleReporter
11-
import dotty.tools.dotc.core.Phases.Phase
11+
import Phases.Phase
1212
import dotty.tools.dotc.transform._
1313
import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, TreeTransformer}
1414
import dotty.tools.dotc.core.DenotTransformers.DenotTransformer
@@ -103,9 +103,13 @@ class Compiler {
103103
(start.setRunInfo(new RunInfo(start)) /: defn.RootImports)(addImport)
104104
}
105105

106-
def newRun(implicit ctx: Context): Run = {
106+
def reset()(implicit ctx: Context): Unit = {
107107
ctx.base.reset()
108108
ctx.runInfo.clear()
109+
}
110+
111+
def newRun(implicit ctx: Context): Run = {
112+
reset()
109113
new Run(this)(rootContext)
110114
}
111115
}

src/dotty/tools/dotc/FromTasty.scala

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/* dotc
2+
* Copyright 2005-2015 LAMP/EPFL
3+
* @author Martin Odersky
4+
*/
5+
package dotty.tools
6+
package dotc
7+
8+
import core._
9+
import Contexts._
10+
import Symbols._
11+
import SymDenotations._
12+
import typer.FrontEnd
13+
import Phases.Phase
14+
import util._
15+
import Decorators._
16+
import dotty.tools.dotc.transform.Pickler
17+
import pickling.DottyUnpickler
18+
import ast.tpd._
19+
20+
/** Compiler for TASTY files.
21+
* Usage:
22+
*
23+
* scala dotty.tools.dotc.FromTasty (option | classname)*
24+
*
25+
* Options are as for dotc.
26+
* Classnames are fully qualified names of top-level classes that need to have a TASTY attribute.
27+
* Example:
28+
*
29+
* scala dotty.tools.dotc.FromTasty -Xprint:front extMethods.T
30+
*/
31+
object FromTasty extends Driver {
32+
override def newCompiler(): Compiler = new TASTYCompiler
33+
34+
class TASTYCompiler extends Compiler {
35+
36+
override def phases: List[List[Phase]] = {
37+
val backendPhases = super.phases.dropWhile {
38+
case List(_: Pickler) => false
39+
case _ => true
40+
}.tail
41+
List(new ReadTastyTreesFromClasses) :: backendPhases
42+
}
43+
44+
override def newRun(implicit ctx: Context): Run = {
45+
reset()
46+
new TASTYRun(this)(rootContext)
47+
}
48+
}
49+
50+
class TASTYRun(comp: Compiler)(implicit ctx: Context) extends Run(comp) {
51+
override def compile(classNames: List[String]) = {
52+
units = classNames.map(new TASTYCompilationUnit(_))
53+
compileUnits()
54+
}
55+
}
56+
57+
class TASTYCompilationUnit(val className: String) extends CompilationUnit(NoSource) {
58+
override def toString = s"class file $className"
59+
}
60+
61+
object force extends TreeTraverser {
62+
def traverse(tree: Tree)(implicit ctx: Context): Unit = traverseChildren(tree)
63+
}
64+
65+
class ReadTastyTreesFromClasses extends FrontEnd {
66+
override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] =
67+
units.map(readTASTY)
68+
69+
def readTASTY(unit: CompilationUnit)(implicit ctx: Context): CompilationUnit = unit match {
70+
case unit: TASTYCompilationUnit =>
71+
val className = unit.className.toTypeName
72+
val clsd =
73+
if (className.contains('.')) ctx.base.staticRef(className)
74+
else defn.EmptyPackageClass.info.decl(className)
75+
def cannotUnpickle(reason: String) = {
76+
ctx.error(s"class $className cannot be unpickled because $reason")
77+
unit
78+
}
79+
clsd match {
80+
case clsd: ClassDenotation =>
81+
clsd.infoOrCompleter match {
82+
case info: ClassfileLoader =>
83+
info.load(clsd) match {
84+
case Some(unpickler: DottyUnpickler) =>
85+
val (List(unpickled), source) = unpickler.body(readPositions = true)
86+
val unit1 = new CompilationUnit(source)
87+
unit1.tpdTree = unpickled
88+
force.traverse(unit1.tpdTree)
89+
unit1
90+
case _ =>
91+
cannotUnpickle(s"its class file ${info.classfile} does not have a TASTY attribute")
92+
}
93+
case info =>
94+
cannotUnpickle(s"its info of type ${info.getClass} is not a ClassfileLoader")
95+
}
96+
case _ =>
97+
ctx.error(s"class not found: $className")
98+
unit
99+
}
100+
}
101+
}
102+
}

src/dotty/tools/dotc/Run.scala

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,25 @@ class Run(comp: Compiler)(implicit ctx: Context) {
4343
* or we need to assmeble phases on each run, and take -Yskip, -Ystop into
4444
* account. I think the latter would be preferable.
4545
*/
46-
def compileSources(sources: List[SourceFile]) = Stats.monitorHeartBeat {
46+
def compileSources(sources: List[SourceFile]) =
4747
if (sources forall (_.exists)) {
48-
val phases = ctx.squashPhases(ctx.phasePlan,
49-
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.YstopAfter.value, ctx.settings.Ycheck.value)
50-
ctx.usePhases(phases)
5148
units = sources map (new CompilationUnit(_))
52-
for (phase <- ctx.allPhases)
53-
if (!ctx.reporter.hasErrors) {
54-
if (ctx.settings.verbose.value) println(s"[$phase]")
55-
units = phase.runOn(units)
56-
def foreachUnit(op: Context => Unit)(implicit ctx: Context): Unit =
57-
for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
58-
if (ctx.settings.Xprint.value.containsPhase(phase))
59-
foreachUnit(printTree)
60-
61-
}
49+
compileUnits()
6250
}
51+
52+
protected def compileUnits() = Stats.monitorHeartBeat {
53+
val phases = ctx.squashPhases(ctx.phasePlan,
54+
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.YstopAfter.value, ctx.settings.Ycheck.value)
55+
ctx.usePhases(phases)
56+
for (phase <- ctx.allPhases)
57+
if (!ctx.reporter.hasErrors) {
58+
if (ctx.settings.verbose.value) println(s"[$phase]")
59+
units = phase.runOn(units)
60+
def foreachUnit(op: Context => Unit)(implicit ctx: Context): Unit =
61+
for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
62+
if (ctx.settings.Xprint.value.containsPhase(phase))
63+
foreachUnit(printTree)
64+
}
6365
}
6466

6567
private def printTree(ctx: Context) = {

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package core
44

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

102-
lazy val EmptyPackageVal = ctx.newCompletePackageSymbol(RootClass, nme.EMPTY_PACKAGE).entered
102+
lazy val EmptyPackageVal = ctx.newPackageSymbol(
103+
RootClass, nme.EMPTY_PACKAGE, (emptypkg, emptycls) => ctx.rootLoader(emptypkg)).entered
103104
lazy val EmptyPackageClass = EmptyPackageVal.moduleClass.asClass
104105

105106
/** A package in which we can place all methods that are interpreted specially by the compiler */

src/dotty/tools/dotc/core/SymbolLoaders.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,10 @@ class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {
251251
else (rootDenot, linkedDenot)
252252
}
253253

254-
def doComplete(root: SymDenotation)(implicit ctx: Context): Unit = {
254+
override def doComplete(root: SymDenotation)(implicit ctx: Context): Unit =
255+
load(root)
256+
257+
def load(root: SymDenotation)(implicit ctx: Context): Option[ClassfileParser.Embedded] = {
255258
val (classRoot, moduleRoot) = rootDenots(root.asClass)
256259
new ClassfileParser(classfile, classRoot, moduleRoot)(ctx).run()
257260
}

src/dotty/tools/dotc/core/pickling/ClassfileParser.scala

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package core
44
package pickling
55

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

18+
object ClassfileParser {
19+
/** Marker trait for unpicklers that can be embedded in classfiles. */
20+
trait Embedded
21+
}
22+
1823
class ClassfileParser(
1924
classfile: AbstractFile,
2025
classRoot: ClassDenotation,
2126
moduleRoot: ClassDenotation)(ictx: Context) {
2227

2328
import ClassfileConstants._
29+
import ClassfileParser._
2430

2531
protected val in = new AbstractFileReader(classfile)
2632

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

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

8187
var sawPrivateConstructor = false
8288

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

95101
addEnclosingTParams()
96102

97-
if (unpickleOrParseInnerClasses()) return
98-
99103
/** Parse parents for Java classes. For Scala, return AnyRef, since the real type will be unpickled.
100104
* Updates the read pointer of 'in'. */
101105
def parseParents: List[Type] = {
@@ -114,33 +118,36 @@ class ClassfileParser(
114118
superType :: ifaces
115119
}
116120

117-
var classInfo: Type = TempClassInfoType(parseParents, instanceScope, classRoot.symbol)
121+
val result = unpickleOrParseInnerClasses()
122+
if (!result.isDefined) {
123+
var classInfo: Type = TempClassInfoType(parseParents, instanceScope, classRoot.symbol)
118124
// might be reassigned by later parseAttributes
119-
val staticInfo = TempClassInfoType(List(), staticScope, moduleRoot.symbol)
125+
val staticInfo = TempClassInfoType(List(), staticScope, moduleRoot.symbol)
120126

121-
enterOwnInnerClasses
127+
enterOwnInnerClasses
122128

123-
classRoot.setFlag(sflags)
124-
moduleRoot.setFlag(Flags.JavaDefined | Flags.ModuleClassCreationFlags)
125-
setPrivateWithin(classRoot, jflags)
126-
setPrivateWithin(moduleRoot, jflags)
127-
setPrivateWithin(moduleRoot.sourceModule, jflags)
129+
classRoot.setFlag(sflags)
130+
moduleRoot.setFlag(Flags.JavaDefined | Flags.ModuleClassCreationFlags)
131+
setPrivateWithin(classRoot, jflags)
132+
setPrivateWithin(moduleRoot, jflags)
133+
setPrivateWithin(moduleRoot.sourceModule, jflags)
128134

129-
for (i <- 0 until in.nextChar) parseMember(method = false)
130-
for (i <- 0 until in.nextChar) parseMember(method = true)
131-
classInfo = parseAttributes(classRoot.symbol, classInfo)
132-
if (isAnnotation) addAnnotationConstructor(classInfo)
135+
for (i <- 0 until in.nextChar) parseMember(method = false)
136+
for (i <- 0 until in.nextChar) parseMember(method = true)
137+
classInfo = parseAttributes(classRoot.symbol, classInfo)
138+
if (isAnnotation) addAnnotationConstructor(classInfo)
133139

134-
val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, classRoot, moduleRoot)
135-
if (companionClassMethod.exists) companionClassMethod.entered
136-
val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, moduleRoot, classRoot)
137-
if (companionModuleMethod.exists) companionModuleMethod.entered
140+
val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, classRoot, moduleRoot)
141+
if (companionClassMethod.exists) companionClassMethod.entered
142+
val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, moduleRoot, classRoot)
143+
if (companionModuleMethod.exists) companionModuleMethod.entered
138144

139-
setClassInfo(classRoot, classInfo)
140-
setClassInfo(moduleRoot, staticInfo)
145+
setClassInfo(classRoot, classInfo)
146+
setClassInfo(moduleRoot, staticInfo)
147+
}
148+
result
141149
}
142150

143-
144151
/** Add type parameters of enclosing classes */
145152
def addEnclosingTParams()(implicit ctx: Context): Unit = {
146153
var sym = classRoot.owner
@@ -644,7 +651,7 @@ class ClassfileParser(
644651
* Restores the old `bp`.
645652
* @return true iff classfile is from Scala, so no Java info needs to be read.
646653
*/
647-
def unpickleOrParseInnerClasses()(implicit ctx: Context): Boolean = {
654+
def unpickleOrParseInnerClasses()(implicit ctx: Context): Option[Embedded] = {
648655
val oldbp = in.bp
649656
try {
650657
skipSuperclasses()
@@ -664,15 +671,16 @@ class ClassfileParser(
664671
i < attrs
665672
}
666673

667-
def unpickleScala(bytes: Array[Byte]): Boolean = {
668-
new UnPickler(bytes, classRoot, moduleRoot)(ctx).run()
669-
true
674+
def unpickleScala(bytes: Array[Byte]): Some[Embedded] = {
675+
val unpickler = new Scala2Unpickler(bytes, classRoot, moduleRoot)(ctx)
676+
unpickler.run()
677+
Some(unpickler)
670678
}
671679

672-
def unpickleTASTY(bytes: Array[Byte]): Boolean = {
673-
new DottyUnpickler(bytes)
674-
.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))
675-
true
680+
def unpickleTASTY(bytes: Array[Byte]): Some[Embedded] = {
681+
val unpickler = new DottyUnpickler(bytes)
682+
unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))
683+
Some(unpickler)
676684
}
677685

678686
def parseScalaSigBytes: Array[Byte] = {
@@ -739,7 +747,7 @@ class ClassfileParser(
739747
}
740748
}
741749
}
742-
false
750+
None
743751
} finally in.bp = oldbp
744752
}
745753

0 commit comments

Comments
 (0)