Skip to content

Commit a9f9cbe

Browse files
committed
Stabilize order of annotations in the class file
Regressed in scala#6846, which added support for encoding repeated annotations. Test failure before replacing `groupBy` with `LinkedHashMap`: ``` $ sbt junit/testOnly scala.tools.nsc.DeterminismTest ... java.lang.AssertionError: assertion failed: Difference detected between recompiling List(b.scala, Annot1.java) Run: jardiff -r /var/folders/tz/p8vd07wn7wxck3b9v54grlzw0000gp/T/reference814657788418452571 /var/folders/tz/p8vd07wn7wxck3b9v54grlzw0000gp/T/recompileOutput4882243280168823330 $ jardiff -r /var/folders/tz/p8vd07wn7wxck3b9v54grlzw0000gp/T/reference814657788418452571 /var/folders/tz/p8vd07wn7wxck3b9v54grlzw0000gp/T/recompileOutput4882243280168823330 diff --git a/Test.class.asm b/Test.class.asm index 98bfd80..a056f9a 100644 --- a/Test.class.asm +++ b/Test.class.asm @@ -4,10 +4,10 @@ // compiled from: b.scala - @LAnnot2;(value=java.lang.Object.class) - @LAnnot1;(value="foo") + @LAnnot2;(value=java.lang.Object.class) + @Lscala/reflect/ScalaSignature;(bytes="\u0006\u0001u1AAA\u0002\u0001\r!)Q\u0002\u0001C\u0001\u001d\u0009!A+Z:u\u0015\u0005!\u0011a\u0002\u001ff[B$\u0018PP\u0002\u0001'\u0009\u0001q\u0001\u0005\u0002\u0009\u00175\u0009\u0011BC\u0001\u000b\u0003\u0015\u00198-\u00197b\u0013\u0009a\u0011B\u0001\u0004B]f\u0014VMZ\u0001\u0007y%t\u0017\u000e\u001e \u0015\u0003=\u0001\"\u0001\u0005\u0001\u000e\u0003\rAC\u0001\u0001\n\u0016-A\u0011\u0001cE\u0005\u0003)\r\u0011a!\u00118o_R\u0014\u0014!\u0002<bYV,7%A\u0004)\u0009\u0001ARc\u0007\u0009\u0003!eI!AG\u0002\u0003\r\u0005sgn\u001c;2C\u0005a\u0012a\u00014p_\u0002") ``` WIP stabilize annotation ordering
1 parent 1e1a162 commit a9f9cbe

File tree

2 files changed

+69
-6
lines changed

2 files changed

+69
-6
lines changed

src/compiler/scala/tools/nsc/typechecker/RefChecks.scala

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1436,11 +1436,14 @@ abstract class RefChecks extends Transform {
14361436
checkTypeRefBounds(ann.tpe, tree)
14371437
}
14381438

1439-
annots
1440-
.map(_.transformArgs(transformTrees))
1441-
.groupBy(_.symbol)
1442-
.flatMap((groupRepeatableAnnotations _).tupled)
1443-
.toList
1439+
val annotsBySymbol = new mutable.LinkedHashMap[Symbol, ListBuffer[AnnotationInfo]]()
1440+
val transformedAnnots = annots.map(_.transformArgs(transformTrees))
1441+
for (transformedAnnot <- transformedAnnots) {
1442+
val buffer = annotsBySymbol.getOrElseUpdate(transformedAnnot.symbol, new ListBuffer)
1443+
buffer += transformedAnnot
1444+
}
1445+
val result = annotsBySymbol.iterator.flatMap(x => groupRepeatableAnnotations(x._1, x._2.toList)).toList
1446+
result
14441447
}
14451448

14461449
// assumes non-empty `anns`

test/junit/scala/tools/nsc/DeterminismTest.scala

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package scala.tools.nsc
22

3+
import java.io.{File, OutputStreamWriter}
4+
import java.nio.charset.Charset
35
import java.nio.file.attribute.BasicFileAttributes
46
import java.nio.file.{FileVisitResult, Files, Path, SimpleFileVisitor}
57
import java.util
68

9+
import javax.tools.ToolProvider
710
import org.junit.Test
811

9-
import scala.collection.JavaConverters.asScalaIteratorConverter
12+
import scala.collection.JavaConverters.{asScalaIteratorConverter, seqAsJavaListConverter}
1013
import scala.language.implicitConversions
1114
import scala.reflect.internal.util.{BatchSourceFile, SourceFile}
1215
import scala.reflect.io.PlainNioFile
@@ -187,6 +190,48 @@ class DeterminismTest {
187190
test(List(code))
188191
}
189192

193+
@Test def testAnnotations1(): Unit = {
194+
def code = List[SourceFile](
195+
source("a.scala",
196+
"""
197+
|class Annot1(s: String) extends scala.annotation.StaticAnnotation
198+
|class Annot2(s: Class[_]) extends scala.annotation.StaticAnnotation
199+
|
200+
""".stripMargin),
201+
source("b.scala",
202+
"""
203+
|@Annot1("foo")
204+
|@Annot2(classOf[AnyRef])
205+
|class Test
206+
""".stripMargin)
207+
)
208+
test(List(code))
209+
}
210+
211+
@Test def testAnnotationsJava(): Unit = {
212+
def code = List[SourceFile](
213+
source("Annot1.java",
214+
"""
215+
|import java.lang.annotation.*;
216+
|@Retention(RetentionPolicy.RUNTIME)
217+
|@Target(ElementType.TYPE)
218+
|@Inherited
219+
|@interface Annot1 { String value() default ""; }
220+
|
221+
|@Retention(RetentionPolicy.RUNTIME)
222+
|@Target(ElementType.TYPE)
223+
|@Inherited
224+
|@interface Annot2 { Class value(); }
225+
|
226+
""".stripMargin),
227+
source("b.scala",
228+
"""
229+
|@Annot1("foo") @Annot2(classOf[AnyRef]) class Test
230+
""".stripMargin)
231+
)
232+
test(List(code))
233+
}
234+
190235
def source(name: String, code: String): SourceFile = new BatchSourceFile(name, code)
191236
private def test(groups: List[List[SourceFile]]): Unit = {
192237
val referenceOutput = Files.createTempDirectory("reference")
@@ -203,6 +248,21 @@ class DeterminismTest {
203248
// println("scalac " + files.mkString(" "))
204249
r.compileSources(files)
205250
assert(!storeReporter.hasErrors, storeReporter.infos.mkString("\n"))
251+
files.filter(_.file.name.endsWith(".java")) match {
252+
case Nil =>
253+
case javaSources =>
254+
def tempFileFor(s: SourceFile): Path = {
255+
val f = output.resolve(s.file.name)
256+
Files.write(f, new String(s.content).getBytes(Charset.defaultCharset()))
257+
}
258+
val options = List("-d", output.toString)
259+
val javac = ToolProvider.getSystemJavaCompiler
260+
val fileMan = javac.getStandardFileManager(null, null, null)
261+
val javaFileObjects = fileMan.getJavaFileObjects(javaSources.map(s => tempFileFor(s).toAbsolutePath.toString): _*)
262+
val task = javac.getTask(new OutputStreamWriter(System.out), fileMan, null, options.asJava, Nil.asJava, javaFileObjects)
263+
val result = task.call()
264+
assert(result)
265+
}
206266
}
207267

208268
for (group <- groups.init) {

0 commit comments

Comments
 (0)