Skip to content

Commit 6d1138e

Browse files
committed
Merge pull request #498 from dotty-staging/save-tasty
Save TASTY in attribute of classfiles.
2 parents d55b719 + 3639cf8 commit 6d1138e

File tree

14 files changed

+154
-33
lines changed

14 files changed

+154
-33
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ classes/
2828

2929
# Partest
3030
tests/partest-generated/
31+
/test-classes/

src/dotty/tools/backend/jvm/GenBCode.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import dotty.tools.dotc.ast.tpd
66
import dotty.tools.dotc.core.Phases.Phase
77

88
import scala.collection.mutable
9-
import scala.tools.asm.{ClassVisitor, MethodVisitor, FieldVisitor}
9+
import scala.tools.asm.{CustomAttr, ClassVisitor, MethodVisitor, FieldVisitor}
1010
import scala.tools.nsc.Settings
1111
import scala.tools.nsc.backend.jvm._
1212
import dotty.tools.dotc
@@ -27,6 +27,7 @@ import scala.tools.asm
2727
import scala.tools.asm.tree._
2828
import dotty.tools.dotc.util.{Positions, DotClass}
2929
import tpd._
30+
import StdNames._
3031

3132
import scala.tools.nsc.backend.jvm.opt.LocalOpt
3233

@@ -174,6 +175,13 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
174175
val outF = if (needsOutFolder) getOutFolder(claszSymbol, pcb.thisName) else null;
175176
val plainC = pcb.cnode
176177

178+
if (claszSymbol.isClass) // @DarkDimius is this test needed here?
179+
for (pickler <- ctx.compilationUnit.picklers.get(claszSymbol.asClass)) {
180+
val binary = pickler.assembleParts()
181+
val dataAttr = new CustomAttr(nme.TASTYATTR.toString, binary)
182+
plainC.visitAttribute(dataAttr)
183+
}
184+
177185
// -------------- bean info class, if needed --------------
178186
val beanC =
179187
if (claszSymbol hasAnnotation int.BeanInfoAttr) {

src/dotty/tools/dotc/CompilationUnit.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ class CompilationUnit(val source: SourceFile) {
1919
def isJava = source.file.name.endsWith(".java")
2020

2121
/**
22-
* Pickler used to create TASTY sections.
22+
* Picklers used to create TASTY sections, indexed by toplevel class to which they belong.
2323
* Sections: Header, ASTs and Positions are populated by `pickler` phase.
2424
* Subsequent phases can add new sections.
2525
*/
26-
lazy val pickler: TastyPickler = new TastyPickler()
26+
var picklers: Map[ClassSymbol, TastyPickler] = Map()
2727

2828
/**
2929
* Addresses in TASTY file of trees, stored by pickling.

src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,34 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
461461
accum(Nil, root)
462462
}
463463

464+
465+
/** The top level classes in this tree, including only those module classes that
466+
* are not a linked class of some other class in the result.
467+
*/
468+
def topLevelClasses(tree: Tree)(implicit ctx: Context): List[ClassSymbol] = tree match {
469+
case PackageDef(_, stats) => stats.flatMap(topLevelClasses)
470+
case tdef: TypeDef if tdef.symbol.isClass => tdef.symbol.asClass :: Nil
471+
case _ => Nil
472+
}
473+
474+
/** The tree containing only the top-level classes and objects matching either `cls` or its companion object */
475+
def sliceTopLevel(tree: Tree, cls: ClassSymbol)(implicit ctx: Context): List[Tree] = tree match {
476+
case PackageDef(pid, stats) =>
477+
cpy.PackageDef(tree)(pid, stats.flatMap(sliceTopLevel(_, cls))) :: Nil
478+
case tdef: TypeDef =>
479+
val sym = tdef.symbol
480+
assert(sym.isClass)
481+
if (cls == sym || cls == sym.linkedClass) tdef :: Nil
482+
else Nil
483+
case vdef: ValDef =>
484+
val sym = vdef.symbol
485+
assert(sym is Module)
486+
if (cls == sym.companionClass || cls == sym.moduleClass) vdef :: Nil
487+
else Nil
488+
case tree =>
489+
tree :: Nil
490+
}
491+
464492
/** The statement sequence that contains a definition of `sym`, or Nil
465493
* if none was found.
466494
* For a tree to be found, The symbol must have a position and its definition

src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
383383
else if (tpw isRef defn.ShortClass) Literal(Constant(0.toShort))
384384
else Literal(Constant(null)).select(defn.Any_asInstanceOf).appliedToType(tpe)
385385
}
386+
386387
private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] {
387388
def apply(sym: Symbol, tree: Tree)(implicit ctx: Context) =
388389
if (sym.exists) sym

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ class Definitions {
335335
lazy val ContravariantBetweenClass = ctx.requiredClass("dotty.annotation.internal.ContravariantBetween")
336336
lazy val ScalaSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaSignature")
337337
lazy val ScalaLongSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaLongSignature")
338+
lazy val TASTYSignatureAnnot = ctx.requiredClass("scala.annotation.internal.TASTYSignature")
339+
lazy val TASTYLongSignatureAnnot = ctx.requiredClass("scala.annotation.internal.TASTYLongSignature")
338340
lazy val DeprecatedAnnot = ctx.requiredClass("scala.deprecated")
339341
lazy val MigrationAnnot = ctx.requiredClass("scala.annotation.migration")
340342
lazy val AnnotationDefaultAnnot = ctx.requiredClass("dotty.annotation.internal.AnnotationDefault")

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ object StdNames {
215215
final val RuntimeParamAnnotationATTR: N = "RuntimeVisibleParameterAnnotations" // RetentionPolicy.RUNTIME (annotations on parameters)
216216
final val ScalaATTR: N = "Scala"
217217
final val ScalaSignatureATTR: N = "ScalaSig"
218+
final val TASTYATTR: N = "TASTY"
218219
final val SignatureATTR: N = "Signature"
219220
final val SourceFileATTR: N = "SourceFile"
220221
final val SyntheticATTR: N = "Synthetic"

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

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -664,11 +664,17 @@ class ClassfileParser(
664664
i < attrs
665665
}
666666

667-
def unpickle(bytes: Array[Byte]): Boolean = {
667+
def unpickleScala(bytes: Array[Byte]): Boolean = {
668668
new UnPickler(bytes, classRoot, moduleRoot)(ctx).run()
669669
true
670670
}
671671

672+
def unpickleTASTY(bytes: Array[Byte]): Boolean = {
673+
new DottyUnpickler(bytes)
674+
.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))
675+
true
676+
}
677+
672678
def parseScalaSigBytes: Array[Byte] = {
673679
val tag = in.nextByte.toChar
674680
assert(tag == STRING_TAG, tag)
@@ -688,6 +694,11 @@ class ClassfileParser(
688694
pool.getBytes(entries.toList)
689695
}
690696

697+
if (scan(tpnme.TASTYATTR)) {
698+
val attrLen = in.nextInt
699+
return unpickleTASTY(in.nextBytes(attrLen))
700+
}
701+
691702
if (scan(tpnme.RuntimeAnnotationATTR)) {
692703
val attrLen = in.nextInt
693704
val nAnnots = in.nextChar
@@ -698,12 +709,16 @@ class ClassfileParser(
698709
var j = 0
699710
while (j < nArgs) {
700711
val argName = pool.getName(in.nextChar)
701-
if (attrClass == defn.ScalaSignatureAnnot && argName == nme.bytes)
702-
return unpickle(parseScalaSigBytes)
703-
else if (attrClass == defn.ScalaLongSignatureAnnot && argName == nme.bytes)
704-
return unpickle(parseScalaLongSigBytes)
705-
else
706-
parseAnnotArg(skip = true)
712+
if (argName == nme.bytes)
713+
if (attrClass == defn.ScalaSignatureAnnot)
714+
return unpickleScala(parseScalaSigBytes)
715+
else if (attrClass == defn.ScalaLongSignatureAnnot)
716+
return unpickleScala(parseScalaLongSigBytes)
717+
else if (attrClass == defn.TASTYSignatureAnnot)
718+
return unpickleTASTY(parseScalaSigBytes)
719+
else if (attrClass == defn.TASTYLongSignatureAnnot)
720+
return unpickleTASTY(parseScalaLongSigBytes)
721+
parseAnnotArg(skip = true)
707722
j += 1
708723
}
709724
i += 1

src/dotty/tools/dotc/transform/Pickler.scala

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import config.Printers.{noPrinter, pickling}
99
import java.io.PrintStream
1010
import Periods._
1111
import Phases._
12+
import Symbols._
13+
import Flags.Module
1214
import collection.mutable
1315

1416
/** This phase pickles trees */
@@ -23,28 +25,41 @@ class Pickler extends Phase {
2325
s.close
2426
}
2527

26-
private val beforePickling = new mutable.HashMap[CompilationUnit, String]
28+
private val beforePickling = new mutable.HashMap[ClassSymbol, String]
29+
30+
/** Drop any elements of this list that are linked module classes of other elements in the list */
31+
private def dropCompanionModuleClasses(clss: List[ClassSymbol])(implicit ctx: Context): List[ClassSymbol] = {
32+
val companionModuleClasses =
33+
clss.filterNot(_ is Module).map(_.linkedClass).filterNot(_.isAbsent)
34+
clss.filterNot(companionModuleClasses.contains)
35+
}
2736

2837
override def run(implicit ctx: Context): Unit = {
2938
val unit = ctx.compilationUnit
30-
val tree = unit.tpdTree
3139
pickling.println(i"unpickling in run ${ctx.runId}")
32-
if (ctx.settings.YtestPickler.value) beforePickling(unit) = tree.show
3340

34-
val pickler = unit.pickler
35-
val treePkl = new TreePickler(pickler)
36-
treePkl.pickle(tree :: Nil)
37-
unit.addrOfTree = treePkl.buf.addrOfTree
38-
unit.addrOfSym = treePkl.addrOfSym
39-
if (tree.pos.exists)
40-
new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil, tree.pos)
41+
for { cls <- dropCompanionModuleClasses(topLevelClasses(unit.tpdTree))
42+
tree <- sliceTopLevel(unit.tpdTree, cls) } {
43+
if (ctx.settings.YtestPickler.value) beforePickling(cls) = tree.show
44+
val pickler = new TastyPickler()
45+
unit.picklers += (cls -> pickler)
46+
val treePkl = new TreePickler(pickler)
47+
treePkl.pickle(tree :: Nil)
48+
unit.addrOfTree = treePkl.buf.addrOfTree
49+
unit.addrOfSym = treePkl.addrOfSym
50+
if (tree.pos.exists)
51+
new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil, tree.pos)
4152

42-
def rawBytes = // not needed right now, but useful to print raw format.
43-
unit.pickler.assembleParts().iterator.grouped(10).toList.zipWithIndex.map {
44-
case (row, i) => s"${i}0: ${row.mkString(" ")}"
53+
def rawBytes = // not needed right now, but useful to print raw format.
54+
pickler.assembleParts().iterator.grouped(10).toList.zipWithIndex.map {
55+
case (row, i) => s"${i}0: ${row.mkString(" ")}"
56+
}
57+
// println(i"rawBytes = \n$rawBytes%\n%") // DEBUG
58+
if (pickling ne noPrinter) {
59+
println(i"**** pickled info of $cls")
60+
new TastyPrinter(pickler.assembleParts()).printContents()
4561
}
46-
// println(i"rawBytes = \n$rawBytes%\n%") // DEBUG
47-
if (pickling ne noPrinter) new TastyPrinter(pickler.assembleParts()).printContents()
62+
}
4863
}
4964

5065
override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = {
@@ -58,23 +73,23 @@ class Pickler extends Phase {
5873
pickling.println(i"testing unpickler at run ${ctx.runId}")
5974
ctx.definitions.init
6075
val unpicklers =
61-
for (unit <- units) yield {
62-
val unpickler = new DottyUnpickler(unit.pickler.assembleParts())
76+
for (unit <- units; (cls, pickler) <- unit.picklers) yield {
77+
val unpickler = new DottyUnpickler(pickler.assembleParts())
6378
unpickler.enter(roots = Set())
64-
unpickler
79+
cls -> unpickler
6580
}
6681
pickling.println("************* entered toplevel ***********")
67-
for ((unpickler, unit) <- unpicklers zip units) {
82+
for ((cls, unpickler) <- unpicklers) {
6883
val unpickled = unpickler.body(readPositions = false)
69-
testSame(i"$unpickled%\n%", beforePickling(unit), unit)
84+
testSame(i"$unpickled%\n%", beforePickling(cls), cls)
7085
}
7186
}
7287

73-
private def testSame(unpickled: String, previous: String, unit: CompilationUnit)(implicit ctx: Context) =
88+
private def testSame(unpickled: String, previous: String, cls: ClassSymbol)(implicit ctx: Context) =
7489
if (previous != unpickled) {
7590
output("before-pickling.txt", previous)
7691
output("after-pickling.txt", unpickled)
77-
ctx.error(s"""pickling difference for $unit, for details:
92+
ctx.error(s"""pickling difference for ${cls.fullName}, for details:
7893
|
7994
| diff before-pickling.txt after-pickling.txt""".stripMargin)
8095
}

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ trait Applications extends Compatibility { self: Typer =>
192192
def success = ok
193193

194194
protected def methodType = methType.asInstanceOf[MethodType]
195-
private def methString: String = s"method ${methRef.name}: ${methType.show}"
195+
private def methString: String = i"${methRef.symbol}: ${methType.show}"
196196

197197
/** Re-order arguments to correctly align named arguments */
198198
def reorder[T >: Untyped](args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package scala.annotation.internal;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@Target(ElementType.TYPE)
10+
public @interface TASTYLongSignature {
11+
public String[] bytes();
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package scala.annotation.internal;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@Target(ElementType.TYPE)
10+
public @interface TASTYSignature {
11+
public String bytes();
12+
}

tests/pos/sepComp/A_1.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package sepComp
2+
3+
class A(y: Int) {
4+
5+
val x: Int = y
6+
7+
}
8+
9+
object A {
10+
11+
def apply(x: Int) = new A(22)
12+
13+
}

tests/pos/sepComp/B_2.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package sepComp
2+
3+
class B extends A(22) {
4+
5+
val y: Int = this.x
6+
7+
val a = A(33)
8+
9+
println(a.x)
10+
11+
}
12+
13+

0 commit comments

Comments
 (0)