Skip to content

Commit 219e78b

Browse files
committed
Merge pull request scala#4067 from lrytz/t8926
SI-8926 default visbility RUNTIME for java annotations
2 parents 767cc44 + fa40ff3 commit 219e78b

File tree

2 files changed

+52
-4
lines changed

2 files changed

+52
-4
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
1919
val ExcludedForwarderFlags = {
2020
import scala.tools.nsc.symtab.Flags._
2121
// Should include DEFERRED but this breaks findMember.
22-
( SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags | MACRO )
22+
SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags | MACRO
2323
}
2424

2525
/**
@@ -147,9 +147,16 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
147147
annot.args.isEmpty
148148
}
149149

150-
def isRuntimeVisible(annot: AnnotationInfo): Boolean =
151-
annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr)
152-
.exists(_.assocs.contains((nme.value -> LiteralAnnotArg(Constant(AnnotationRetentionPolicyRuntimeValue)))))
150+
def isRuntimeVisible(annot: AnnotationInfo): Boolean = {
151+
annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr) match {
152+
case Some(retentionAnnot) =>
153+
retentionAnnot.assocs.contains(nme.value -> LiteralAnnotArg(Constant(AnnotationRetentionPolicyRuntimeValue)))
154+
case _ =>
155+
// SI-8926: if the annotation class symbol doesn't have a @RetentionPolicy annotation, the
156+
// annotation is emitted with visibility `RUNTIME`
157+
true
158+
}
159+
}
153160

154161
private def retentionPolicyOf(annot: AnnotationInfo): Symbol =
155162
annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr).map(_.assocs).map(assoc =>

test/junit/scala/issues/BytecodeTests.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import org.junit.runner.RunWith
44
import org.junit.runners.JUnit4
55
import org.junit.Test
66
import scala.tools.asm.Opcodes
7+
import scala.tools.nsc.backend.jvm.AsmUtils
78
import scala.tools.nsc.backend.jvm.CodeGenTools._
89
import org.junit.Assert._
910
import scala.collection.JavaConverters._
@@ -36,4 +37,44 @@ class BytecodeTests {
3637
assertTrue(getSingleMethod(c, "f").instructions.count(_.isInstanceOf[TableSwitch]) == 1)
3738
assertTrue(getSingleMethod(c, "g").instructions.count(_.isInstanceOf[LookupSwitch]) == 1)
3839
}
40+
41+
@Test
42+
def t8926(): Unit = {
43+
import scala.reflect.internal.util.BatchSourceFile
44+
45+
// this test cannot be implemented using partest because of its mixed-mode compilation strategy:
46+
// partest first compiles all files with scalac, then the java files, and then again the scala
47+
// using the output classpath. this shadows the bug SI-8926.
48+
49+
val annotA =
50+
"""import java.lang.annotation.Retention;
51+
|import java.lang.annotation.RetentionPolicy;
52+
|@Retention(RetentionPolicy.RUNTIME)
53+
|public @interface AnnotA { }
54+
""".stripMargin
55+
val annotB = "public @interface AnnotB { }"
56+
57+
val scalaSrc =
58+
"""@AnnotA class A
59+
|@AnnotB class B
60+
""".stripMargin
61+
62+
val compiler = newCompiler()
63+
val run = new compiler.Run()
64+
run.compileSources(List(new BatchSourceFile("AnnotA.java", annotA), new BatchSourceFile("AnnotB.java", annotB), new BatchSourceFile("Test.scala", scalaSrc)))
65+
val outDir = compiler.settings.outputDirs.getSingleOutput.get
66+
val outfiles = (for (f <- outDir.iterator if !f.isDirectory) yield (f.name, f.toByteArray)).toList
67+
68+
def check(classfile: String, annotName: String) = {
69+
val f = (outfiles collect { case (`classfile`, bytes) => AsmUtils.readClass(bytes) }).head
70+
val descs = f.visibleAnnotations.asScala.map(_.desc).toList
71+
assertTrue(descs.toString, descs exists (_ contains annotName))
72+
}
73+
74+
check("A.class", "AnnotA")
75+
76+
// known issue SI-8928: the visibility of AnnotB should be CLASS, but annotation classes without
77+
// a @Retention annotation are currently emitted as RUNTIME.
78+
check("B.class", "AnnotB")
79+
}
3980
}

0 commit comments

Comments
 (0)