Skip to content

Commit

Permalink
Require seeing ScalaSig before scanning for Scala pickling annotations
Browse files Browse the repository at this point in the history
An alternative fix for scala#15166, which aligns with the behavior of the
Scala 2 classfile parser.

Before this commit, all classfiles, including those produced by the Java
compiler and compilers of other JVM languages, were scanned for Scala
pickling annotations. In certain situations (as in tests/pos/i15166),
this is problematic, as the denotation of an annotation symbol defined as
a Java inner class may be forced before the inner class table is populated
and setClassInfo is called on the class root.

We avoid this situation by only performing the pickling annotation scan for
those classfiles having the `ScalaSig` attribute, i.e. those produced by
the Scala 2 compiler.

We also drop support for pickling TASTy using the classfile annotations
  `scala.annotation.internal.TASTYSignature` and
  `scala.annotation.internal.TASTYLongSignature`.
These were never used by the compiler, there are no plans for future use,
and preserving support would complicate this fix.
  • Loading branch information
griggt committed May 19, 2022
1 parent e4b1cfe commit bc767cc
Show file tree
Hide file tree
Showing 4 changed files with 9 additions and 15 deletions.
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,8 @@ class Definitions {
@tu lazy val NativeAnnot: ClassSymbol = requiredClass("scala.native")
@tu lazy val RepeatedAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Repeated")
@tu lazy val SourceFileAnnot: ClassSymbol = requiredClass("scala.annotation.internal.SourceFile")
@tu lazy val ScalaSignatureAnnot: ClassSymbol = requiredClass("scala.reflect.ScalaSignature")
@tu lazy val ScalaLongSignatureAnnot: ClassSymbol = requiredClass("scala.reflect.ScalaLongSignature")
@tu lazy val ScalaStrictFPAnnot: ClassSymbol = requiredClass("scala.annotation.strictfp")
@tu lazy val ScalaStaticAnnot: ClassSymbol = requiredClass("scala.annotation.static")
@tu lazy val SerialVersionUIDAnnot: ClassSymbol = requiredClass("scala.SerialVersionUID")
Expand Down
20 changes: 5 additions & 15 deletions compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,6 @@ object ClassfileParser {
mapOver(tp)
}
}

private inline def sigOfClassName(n: String) = s"L$n;"
val ScalaSignatureAnnot: String = sigOfClassName("scala.reflect.ScalaSignature")
val ScalaLongSignatureAnnot: String = sigOfClassName("scala.reflect.ScalaLongSignature")
val TASTYSignatureAnnot: String = sigOfClassName("scala.annotation.internal.TASTYSignature")
val TASTYLongSignatureAnnot: String = sigOfClassName("scala.annotation.internal.TASTYLongSignature")
}

class ClassfileParser(
Expand Down Expand Up @@ -876,7 +870,7 @@ class ClassfileParser(

/** Parse inner classes. Expects `in.bp` to point to the superclass entry.
* Restores the old `bp`.
* @return true iff classfile is from Scala, so no Java info needs to be read.
* @return Some(unpickler) iff classfile is from Scala, so no Java info needs to be read.
*/
def unpickleOrParseInnerClasses()(using ctx: Context, in: DataReader): Option[Embedded] = {
val oldbp = in.bp
Expand Down Expand Up @@ -1005,25 +999,21 @@ class ClassfileParser(
// attribute isn't, this classfile is a compilation artifact.
return Some(NoEmbedded)

if (scan(tpnme.RuntimeVisibleAnnotationATTR) || scan(tpnme.RuntimeInvisibleAnnotationATTR)) {
if (scan(tpnme.ScalaSignatureATTR) && scan(tpnme.RuntimeVisibleAnnotationATTR)) {
val attrLen = in.nextInt
val nAnnots = in.nextChar
var i = 0
while (i < nAnnots) {
val attrSig = pool.getExternalName(in.nextChar).value
val attrClass = pool.getType(in.nextChar).typeSymbol
val nArgs = in.nextChar
var j = 0
while (j < nArgs) {
val argName = pool.getName(in.nextChar)
if (argName.name == nme.bytes) {
if attrSig == ScalaSignatureAnnot then
if attrClass == defn.ScalaSignatureAnnot then
return unpickleScala(parseScalaSigBytes)
else if attrSig == ScalaLongSignatureAnnot then
else if attrClass == defn.ScalaLongSignatureAnnot then
return unpickleScala(parseScalaLongSigBytes)
else if attrSig == TASTYSignatureAnnot then
return unpickleTASTY(parseScalaSigBytes)
else if attrSig == TASTYLongSignatureAnnot then
return unpickleTASTY(parseScalaLongSigBytes)
}
parseAnnotArg(skip = true)
j += 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Deprecated
public @interface TASTYLongSignature {
public String[] bytes();
}
1 change: 1 addition & 0 deletions library/src/scala/annotation/internal/TASTYSignature.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Deprecated
public @interface TASTYSignature {
public String bytes();
}

0 comments on commit bc767cc

Please sign in to comment.