Skip to content

Print annotation in decompiler #4676

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 3 commits into from
Jun 18, 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
1 change: 0 additions & 1 deletion compiler/test/dotty/tools/dotc/FromTastyTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ class FromTastyTests extends ParallelTesting {
"t3612.scala",
),
recompilationBlacklist = Set(
"simpleCaseObject",
"annot-bootstrap.scala",
)
).checkCompile()
Expand Down
120 changes: 105 additions & 15 deletions library/src/scala/tasty/util/ShowSourceCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
printImportSelectors(selectors)

case cdef @ ClassDef(name, DefDef(_, targs, argss, _, _), parents, self, stats) =>
printDefAnnotations(cdef)

val flags = cdef.flags
if (flags.isFinal && !flags.isObject) this += "final "
if (flags.isCase) this += "case "
Expand Down Expand Up @@ -146,11 +148,14 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
}
this

case tdef@TypeDef(name, rhs) =>
case tdef @ TypeDef(name, rhs) =>
printDefAnnotations(tdef)
this += "type "
printTargDef(tdef)

case vdef@ValDef(name, tpt, rhs) =>
case vdef @ ValDef(name, tpt, rhs) =>
printDefAnnotations(vdef)

val flags = vdef.flags
if (flags.isOverride) this += "override "

Expand Down Expand Up @@ -201,7 +206,9 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
printTree(cond)
this += ")"

case ddef@DefDef(name, targs, argss, tpt, rhs) =>
case ddef @ DefDef(name, targs, argss, tpt, rhs) =>
printDefAnnotations(ddef)

val flags = ddef.flags
if (flags.isOverride) sb.append("override ")

Expand All @@ -220,8 +227,16 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
}
this

case [email protected](name) =>
printType(tree.tpe)
case tree @ Term.Ident(name) =>
tree.tpe match {
case Type.SymRef(_, Types.EmptyPrefix()) | Type.TermRef(_, Types.EmptyPrefix()) => this += name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems SymRef subsumes TermRef?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, both are core.Types.NamedTypes but SymRef as a symbol as designator and TermRef has a TermName as designator.

case Type.SymRef(_, prefix) =>
printTypeOrBound(prefix)
this += "." += name
case Type.TermRef(_, prefix) =>
printTypeOrBound(prefix)
this += "." += name
}

case Term.Select(qual, name, sig) =>
printTree(qual)
Expand Down Expand Up @@ -272,7 +287,17 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
this += "("
printTree(term)
this += ": "
printTypeTree(tpt)
def printTypeOrAnnots(tpe: Type): Unit = tpe match {
case Type.AnnotatedType(tp, annot) if tp == term.tpe =>
printAnnotation(annot)
case Type.AnnotatedType(tp, annot) =>
printTypeOrAnnots(tp)
this += " "
printAnnotation(annot)
case tpe =>
printType(tpe)
}
printTypeOrAnnots(tpt.tpe)
this += ")"
}

Expand Down Expand Up @@ -516,6 +541,19 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
this += ")"
}

def printAnnotations(trees: List[Term]): Buffer = {
def printSeparated(list: List[Term]): Unit = list match {
case Nil =>
case x :: Nil => printAnnotation(x)
case x :: xs =>
printAnnotation(x)
this += " "
printSeparated(xs)
}
printSeparated(trees)
this
}

def printArgDef(arg: ValDef): Unit = {
val ValDef(name, tpt, rhs) = arg
this += name += ": "
Expand Down Expand Up @@ -616,11 +654,23 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty

def printTypeTree(tree: TypeTree): Buffer = tree match {
case TypeTree.Synthetic() =>
printType(tree.tpe)
tree.tpe match {
case tpe @ Type.TypeRef(name, _) if name.endsWith("$") => this += ".type"
case tpe => this
}
def printTypeAndAnnots(tpe: Type): Buffer = tpe match {
case Type.AnnotatedType(tp, annot) =>
printTypeAndAnnots(tp)
this += " "
printAnnotation(annot)
case tpe @ Type.TypeRef(name, _) if name.endsWith("$") =>
printType(tpe)
this += ".type"
case Type.SymRef(ClassDef("Null$" | "Nothing$", _, _, _, _), Type.ThisType(Type.SymRef(PackageDef("runtime", _), NoPrefix()))) =>
// scala.runtime.Null$ and scala.runtime.Nothing$ are not modules, those are their actual names
printType(tpe)
case tpe @ Type.SymRef(ClassDef(name, _, _, _, _), _) if name.endsWith("$") =>
printType(tpe)
this += ".type"
case tpe => printType(tpe)
}
printTypeAndAnnots(tree.tpe)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests/run-with-compiler/i3947c.scala failed due to the change, I cannot figure out why.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was because scala.runtime.Null$ and scala.runtime.Nothing$ where handled as modules. Those are now special cases.


case TypeTree.TypeIdent(name) =>
printType(tree.tpe)
Expand Down Expand Up @@ -654,9 +704,11 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
printTypeTrees(args, ", ")
this += "]"

case TypeTree.Annotated(tpt, annots) =>
case TypeTree.Annotated(tpt, annot) =>
val Annotation(ref, args) = annot
printTypeTree(tpt)
// TODO print annots
this += " "
printAnnotation(annot)

case TypeTree.And(left, right) =>
printTypeTree(left)
Expand Down Expand Up @@ -692,14 +744,13 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty

case Type.SymRef(sym, prefix) =>
prefix match {
case Type.ThisType(Types.EmptyPackage() | Types.RootPackage()) =>
case Types.EmptyPrefix() =>
case [email protected](ClassDef(_, _, _, _, _), _) =>
printType(prefix)
this += "#"
case prefix@Type() =>
printType(prefix)
this += "."
case prefix@NoPrefix() =>
}
printDefinitionName(sym)

Expand Down Expand Up @@ -734,7 +785,10 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
this += "]"

case Type.AnnotatedType(tp, annot) =>
val Annotation(ref, args) = annot
printType(tp)
this += " "
printAnnotation(annot)

case Type.AndType(left, right) =>
printType(left)
Expand Down Expand Up @@ -775,6 +829,28 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
case PackageDef(name, _) => this += name
}

def printAnnotation(annot: Term): Buffer = {
val Annotation(ref, args) = annot
this += "@"
printTypeTree(ref)
this += "("
printTrees(args, ", ")
this += ")"
}

def printDefAnnotations(definition: Definition): Buffer = {
val annots = definition.annots.filter {
case Annotation(annot, _) =>
annot.tpe match {
case Type.TypeRef(_, Type.SymRef(PackageDef("internal", _), Type.ThisType(Type.SymRef(PackageDef("annotation", _), NoPrefix())))) => false
case _ => true
}
}
printAnnotations(annots)
if (annots.nonEmpty) this += " "
else this
}

def +=(x: Boolean): this.type = { sb.append(x); this }
def +=(x: Byte): this.type = { sb.append(x); this }
def +=(x: Short): this.type = { sb.append(x); this }
Expand Down Expand Up @@ -829,6 +905,13 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
}
}

private object Annotation {
def unapply(arg: Tree)(implicit ctx: Context): Option[(TypeTree, List[Term])] = arg match {
case Term.Apply(Term.Select(Term.New(annot), "<init>", _), args) => Some((annot, args))
case _ => None
}
}

// TODO Provide some of these in scala.tasty.Tasty.scala and implement them using checks on symbols for performance
private object Types {

Expand Down Expand Up @@ -866,6 +949,13 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
case _ => false
}
}

object EmptyPrefix {
def unapply(tpe: TypeOrBounds)(implicit ctx: Context): Boolean = tpe match {
case NoPrefix() | Type.ThisType(Types.EmptyPackage() | Types.RootPackage()) => true
case _ => false
}
}
}


Expand Down
4 changes: 2 additions & 2 deletions tests/pos/i2104b.decompiled
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ case class Pair[A, B](_1: A, _2: B) {
scala.runtime.Statics.finalizeHash(acc, 2)
}
override def equals(x$0: scala.Any): scala.Boolean = this.eq(x$0.asInstanceOf[java.lang.Object]).||(x$0 match {
case x$0: Pair[Pair.this.A, Pair.this.B] =>
case x$0: Pair[Pair.this.A, Pair.this.B] @scala.unchecked() =>
this._1.==(x$0._1).&&(this._2.==(x$0._2))
case _ =>
false
})
override def toString(): java.lang.String = scala.runtime.ScalaRunTime._toString(this)
override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[Pair[Pair.this.A, Pair.this.B]]
override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[Pair[Pair.this.A, Pair.this.B] @scala.unchecked()]
override def productArity: scala.Int = 2
override def productPrefix: java.lang.String = "Pair"
override def productElement(n: scala.Int): scala.Any = n match {
Expand Down
9 changes: 9 additions & 0 deletions tests/pos/simpleAnnot.decompiled
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/** Decompiled from out/posTestFromTasty/pos/simpleAnnot/Foo.class */
class Foo() {
@annot() type A = scala.Int
@annot() val a: scala.Int = scala.Predef.???
val b: scala.Int @annot() = scala.Predef.???
def c(x: scala.Int): scala.Int = (x: @annot())
}
/** Decompiled from out/posTestFromTasty/pos/simpleAnnot/annot.class */
class annot() extends scala.annotation.Annotation
9 changes: 9 additions & 0 deletions tests/pos/simpleAnnot.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

class Foo {
@annot type A = Int
@annot val a: Int = ???
val b: Int @annot = ???
def c(x: Int) = (x : @annot)
}

class annot extends scala.annotation.Annotation
4 changes: 2 additions & 2 deletions tests/pos/simpleCaseClass-1.decompiled
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
case class A() {
override def hashCode(): scala.Int = 1914112431
override def equals(x$0: scala.Any): scala.Boolean = this.eq(x$0.asInstanceOf[java.lang.Object]).||(x$0 match {
case x$0: A =>
case x$0: A @scala.unchecked() =>
true
case _ =>
false
})
override def toString(): java.lang.String = scala.runtime.ScalaRunTime._toString(this)
override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[A]
override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[A @scala.unchecked()]
override def productArity: scala.Int = 0
override def productPrefix: java.lang.String = "A"
override def productElement(n: scala.Int): scala.Any = n match {
Expand Down
4 changes: 2 additions & 2 deletions tests/pos/simpleCaseClass-2.decompiled
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ case class A(x: scala.Int) {
scala.runtime.Statics.finalizeHash(acc, 1)
}
override def equals(x$0: scala.Any): scala.Boolean = this.eq(x$0.asInstanceOf[java.lang.Object]).||(x$0 match {
case x$0: A =>
case x$0: A @scala.unchecked() =>
this.x.==(x$0.x)
case _ =>
false
})
override def toString(): java.lang.String = scala.runtime.ScalaRunTime._toString(this)
override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[A]
override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[A @scala.unchecked()]
override def productArity: scala.Int = 1
override def productPrefix: java.lang.String = "A"
override def productElement(n: scala.Int): scala.Any = n match {
Expand Down
4 changes: 2 additions & 2 deletions tests/pos/simpleCaseClass-3.decompiled
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ case class A[T](x: T) {
scala.runtime.Statics.finalizeHash(acc, 1)
}
override def equals(x$0: scala.Any): scala.Boolean = this.eq(x$0.asInstanceOf[java.lang.Object]).||(x$0 match {
case x$0: A[A.this.T] =>
case x$0: A[A.this.T] @scala.unchecked() =>
this.x.==(x$0.x)
case _ =>
false
})
override def toString(): java.lang.String = scala.runtime.ScalaRunTime._toString(this)
override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[A[A.this.T]]
override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[A[A.this.T] @scala.unchecked()]
override def productArity: scala.Int = 1
override def productPrefix: java.lang.String = "A"
override def productElement(n: scala.Int): scala.Any = n match {
Expand Down
2 changes: 1 addition & 1 deletion tests/pos/simpleCaseObject.decompiled
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package foo {
case object Foo {
override def hashCode(): scala.Int = 1045991777
override def toString(): java.lang.String = "Foo"
override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[foo.Foo]
override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[foo.Foo.type @scala.unchecked()]
override def productArity: scala.Int = 0
override def productPrefix: java.lang.String = "Foo"
override def productElement(n: scala.Int): scala.Any = n match {
Expand Down