Skip to content

Save TASTY in attribute of classfiles. #498

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
Apr 28, 2015
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ classes/

# Partest
tests/partest-generated/
/test-classes/
10 changes: 9 additions & 1 deletion src/dotty/tools/backend/jvm/GenBCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Phases.Phase

import scala.collection.mutable
import scala.tools.asm.{ClassVisitor, MethodVisitor, FieldVisitor}
import scala.tools.asm.{CustomAttr, ClassVisitor, MethodVisitor, FieldVisitor}
import scala.tools.nsc.Settings
import scala.tools.nsc.backend.jvm._
import dotty.tools.dotc
Expand All @@ -27,6 +27,7 @@ import scala.tools.asm
import scala.tools.asm.tree._
import dotty.tools.dotc.util.{Positions, DotClass}
import tpd._
import StdNames._

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

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

if (claszSymbol.isClass) // @DarkDimius is this test needed here?
for (pickler <- ctx.compilationUnit.picklers.get(claszSymbol.asClass)) {
val binary = pickler.assembleParts()
val dataAttr = new CustomAttr(nme.TASTYATTR.toString, binary)
plainC.visitAttribute(dataAttr)
}

// -------------- bean info class, if needed --------------
val beanC =
if (claszSymbol hasAnnotation int.BeanInfoAttr) {
Expand Down
4 changes: 2 additions & 2 deletions src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ class CompilationUnit(val source: SourceFile) {
def isJava = source.file.name.endsWith(".java")

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

/**
* Addresses in TASTY file of trees, stored by pickling.
Expand Down
28 changes: 28 additions & 0 deletions src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,34 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
accum(Nil, root)
}


/** The top level classes in this tree, including only those module classes that
* are not a linked class of some other class in the result.
*/
def topLevelClasses(tree: Tree)(implicit ctx: Context): List[ClassSymbol] = tree match {
case PackageDef(_, stats) => stats.flatMap(topLevelClasses)
case tdef: TypeDef if tdef.symbol.isClass => tdef.symbol.asClass :: Nil
case _ => Nil
}

/** The tree containing only the top-level classes and objects matching either `cls` or its companion object */
def sliceTopLevel(tree: Tree, cls: ClassSymbol)(implicit ctx: Context): List[Tree] = tree match {
case PackageDef(pid, stats) =>
cpy.PackageDef(tree)(pid, stats.flatMap(sliceTopLevel(_, cls))) :: Nil
case tdef: TypeDef =>
val sym = tdef.symbol
assert(sym.isClass)
if (cls == sym || cls == sym.linkedClass) tdef :: Nil
else Nil
case vdef: ValDef =>
val sym = vdef.symbol
assert(sym is Module)
if (cls == sym.companionClass || cls == sym.moduleClass) vdef :: Nil
else Nil
case tree =>
tree :: Nil
}

/** The statement sequence that contains a definition of `sym`, or Nil
* if none was found.
* For a tree to be found, The symbol must have a position and its definition
Expand Down
1 change: 1 addition & 0 deletions src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
else if (tpw isRef defn.ShortClass) Literal(Constant(0.toShort))
else Literal(Constant(null)).select(defn.Any_asInstanceOf).appliedToType(tpe)
}

private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] {
def apply(sym: Symbol, tree: Tree)(implicit ctx: Context) =
if (sym.exists) sym
Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ class Definitions {
lazy val ContravariantBetweenClass = ctx.requiredClass("dotty.annotation.internal.ContravariantBetween")
lazy val ScalaSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaSignature")
lazy val ScalaLongSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaLongSignature")
lazy val TASTYSignatureAnnot = ctx.requiredClass("scala.annotation.internal.TASTYSignature")
lazy val TASTYLongSignatureAnnot = ctx.requiredClass("scala.annotation.internal.TASTYLongSignature")
lazy val DeprecatedAnnot = ctx.requiredClass("scala.deprecated")
lazy val MigrationAnnot = ctx.requiredClass("scala.annotation.migration")
lazy val AnnotationDefaultAnnot = ctx.requiredClass("dotty.annotation.internal.AnnotationDefault")
Expand Down
1 change: 1 addition & 0 deletions src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ object StdNames {
final val RuntimeParamAnnotationATTR: N = "RuntimeVisibleParameterAnnotations" // RetentionPolicy.RUNTIME (annotations on parameters)
final val ScalaATTR: N = "Scala"
final val ScalaSignatureATTR: N = "ScalaSig"
final val TASTYATTR: N = "TASTY"
final val SignatureATTR: N = "Signature"
final val SourceFileATTR: N = "SourceFile"
final val SyntheticATTR: N = "Synthetic"
Expand Down
29 changes: 22 additions & 7 deletions src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -664,11 +664,17 @@ class ClassfileParser(
i < attrs
}

def unpickle(bytes: Array[Byte]): Boolean = {
def unpickleScala(bytes: Array[Byte]): Boolean = {
new UnPickler(bytes, classRoot, moduleRoot)(ctx).run()
true
}

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

def parseScalaSigBytes: Array[Byte] = {
val tag = in.nextByte.toChar
assert(tag == STRING_TAG, tag)
Expand All @@ -688,6 +694,11 @@ class ClassfileParser(
pool.getBytes(entries.toList)
}

if (scan(tpnme.TASTYATTR)) {
val attrLen = in.nextInt
return unpickleTASTY(in.nextBytes(attrLen))
}

if (scan(tpnme.RuntimeAnnotationATTR)) {
val attrLen = in.nextInt
val nAnnots = in.nextChar
Expand All @@ -698,12 +709,16 @@ class ClassfileParser(
var j = 0
while (j < nArgs) {
val argName = pool.getName(in.nextChar)
if (attrClass == defn.ScalaSignatureAnnot && argName == nme.bytes)
return unpickle(parseScalaSigBytes)
else if (attrClass == defn.ScalaLongSignatureAnnot && argName == nme.bytes)
return unpickle(parseScalaLongSigBytes)
else
parseAnnotArg(skip = true)
if (argName == nme.bytes)
if (attrClass == defn.ScalaSignatureAnnot)
return unpickleScala(parseScalaSigBytes)
else if (attrClass == defn.ScalaLongSignatureAnnot)
return unpickleScala(parseScalaLongSigBytes)
else if (attrClass == defn.TASTYSignatureAnnot)
return unpickleTASTY(parseScalaSigBytes)
else if (attrClass == defn.TASTYLongSignatureAnnot)
return unpickleTASTY(parseScalaLongSigBytes)
parseAnnotArg(skip = true)
j += 1
}
i += 1
Expand Down
59 changes: 37 additions & 22 deletions src/dotty/tools/dotc/transform/Pickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import config.Printers.{noPrinter, pickling}
import java.io.PrintStream
import Periods._
import Phases._
import Symbols._
import Flags.Module
import collection.mutable

/** This phase pickles trees */
Expand All @@ -23,28 +25,41 @@ class Pickler extends Phase {
s.close
}

private val beforePickling = new mutable.HashMap[CompilationUnit, String]
private val beforePickling = new mutable.HashMap[ClassSymbol, String]

/** Drop any elements of this list that are linked module classes of other elements in the list */
private def dropCompanionModuleClasses(clss: List[ClassSymbol])(implicit ctx: Context): List[ClassSymbol] = {
val companionModuleClasses =
clss.filterNot(_ is Module).map(_.linkedClass).filterNot(_.isAbsent)
clss.filterNot(companionModuleClasses.contains)
}

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

val pickler = unit.pickler
val treePkl = new TreePickler(pickler)
treePkl.pickle(tree :: Nil)
unit.addrOfTree = treePkl.buf.addrOfTree
unit.addrOfSym = treePkl.addrOfSym
if (tree.pos.exists)
new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil, tree.pos)
for { cls <- dropCompanionModuleClasses(topLevelClasses(unit.tpdTree))
tree <- sliceTopLevel(unit.tpdTree, cls) } {
if (ctx.settings.YtestPickler.value) beforePickling(cls) = tree.show
val pickler = new TastyPickler()
unit.picklers += (cls -> pickler)
val treePkl = new TreePickler(pickler)
treePkl.pickle(tree :: Nil)
unit.addrOfTree = treePkl.buf.addrOfTree
unit.addrOfSym = treePkl.addrOfSym
if (tree.pos.exists)
new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil, tree.pos)

def rawBytes = // not needed right now, but useful to print raw format.
unit.pickler.assembleParts().iterator.grouped(10).toList.zipWithIndex.map {
case (row, i) => s"${i}0: ${row.mkString(" ")}"
def rawBytes = // not needed right now, but useful to print raw format.
pickler.assembleParts().iterator.grouped(10).toList.zipWithIndex.map {
case (row, i) => s"${i}0: ${row.mkString(" ")}"
}
// println(i"rawBytes = \n$rawBytes%\n%") // DEBUG
if (pickling ne noPrinter) {
println(i"**** pickled info of $cls")
new TastyPrinter(pickler.assembleParts()).printContents()
}
// println(i"rawBytes = \n$rawBytes%\n%") // DEBUG
if (pickling ne noPrinter) new TastyPrinter(pickler.assembleParts()).printContents()
}
}

override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = {
Expand All @@ -58,23 +73,23 @@ class Pickler extends Phase {
pickling.println(i"testing unpickler at run ${ctx.runId}")
ctx.definitions.init
val unpicklers =
for (unit <- units) yield {
val unpickler = new DottyUnpickler(unit.pickler.assembleParts())
for (unit <- units; (cls, pickler) <- unit.picklers) yield {
val unpickler = new DottyUnpickler(pickler.assembleParts())
unpickler.enter(roots = Set())
unpickler
cls -> unpickler
}
pickling.println("************* entered toplevel ***********")
for ((unpickler, unit) <- unpicklers zip units) {
for ((cls, unpickler) <- unpicklers) {
val unpickled = unpickler.body(readPositions = false)
testSame(i"$unpickled%\n%", beforePickling(unit), unit)
testSame(i"$unpickled%\n%", beforePickling(cls), cls)
}
}

private def testSame(unpickled: String, previous: String, unit: CompilationUnit)(implicit ctx: Context) =
private def testSame(unpickled: String, previous: String, cls: ClassSymbol)(implicit ctx: Context) =
if (previous != unpickled) {
output("before-pickling.txt", previous)
output("after-pickling.txt", unpickled)
ctx.error(s"""pickling difference for $unit, for details:
ctx.error(s"""pickling difference for ${cls.fullName}, for details:
|
| diff before-pickling.txt after-pickling.txt""".stripMargin)
}
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ trait Applications extends Compatibility { self: Typer =>
def success = ok

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

/** Re-order arguments to correctly align named arguments */
def reorder[T >: Untyped](args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = {
Expand Down
12 changes: 12 additions & 0 deletions src/scala/annotation/internal/TASTYLongSignature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package scala.annotation.internal;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TASTYLongSignature {
public String[] bytes();
}
12 changes: 12 additions & 0 deletions src/scala/annotation/internal/TASTYSignature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package scala.annotation.internal;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TASTYSignature {
public String bytes();
}
13 changes: 13 additions & 0 deletions tests/pos/sepComp/A_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package sepComp

class A(y: Int) {

val x: Int = y

}

object A {

def apply(x: Int) = new A(22)

}
13 changes: 13 additions & 0 deletions tests/pos/sepComp/B_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package sepComp

class B extends A(22) {

val y: Int = this.x

val a = A(33)

println(a.x)

}