From 29e1f8a0ae0c01040aea216f97a6e38f49a89a60 Mon Sep 17 00:00:00 2001
From: "Paolo G. Giarrusso"
Date: Fri, 18 Jan 2019 22:18:19 +0100
Subject: [PATCH] Test `final object` is indeed redundant: objects must be
JVM-final
Scalac sometimes *forgets* the final bytecode flag, and sometimes one cares, so
people even *recommend* always writing *final object* instead of object. That's
bad, since we started warning about it in #4973.
https://github.com/scala/bug/issues/11094#issuecomment-455653908
https://nrinaudo.github.io/scala-best-practices/adts/final_case_objects.html
Why care?
=========
If we forget the final flag in bytecode, Java code might then extend the class.
Performance might or might not be affected - some in scala/contributors
debated this for ~20 minutes (starting at
https://gitter.im/scala/contributors?at=5c423c449bfa375aab21f2af).
---
.../backend/jvm/DottyBytecodeTests.scala | 58 +++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala
index 2c666a8d598f..b74e6dc63b13 100644
--- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala
+++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala
@@ -565,4 +565,62 @@ class TestBCode extends DottyBytecodeTest {
assertEquals(14, instructionsFromMethod(method).size)
}
}
+
+ /* Test that objects compile to *final* classes. */
+
+ def checkFinalClass(outputClassName: String, source: String) = {
+ checkBCode(source) {
+ dir =>
+ val moduleIn = dir.lookupName(outputClassName, directory = false)
+ val moduleNode = loadClassNode(moduleIn.input)
+ assert((moduleNode.access & Opcodes.ACC_FINAL) != 0)
+ }
+ }
+
+ @Test def objectsAreFinal =
+ checkFinalClass("Foo$.class", "object Foo")
+
+ @Test def objectsInClassAreFinal =
+ checkFinalClass("Test$Foo$.class",
+ """class Test {
+ | object Foo
+ |}
+ """.stripMargin)
+
+ @Test def objectsInObjsAreFinal =
+ checkFinalClass("Test$Foo$.class",
+ """object Test {
+ | object Foo
+ |}
+ """.stripMargin)
+
+ @Test def objectsInObjDefAreFinal =
+ checkFinalClass("Test$Foo$1$.class",
+ """
+ |object Test {
+ | def bar() = {
+ | object Foo
+ | }
+ |}
+ """.stripMargin)
+
+ @Test def objectsInClassDefAreFinal =
+ checkFinalClass("Test$Foo$1$.class",
+ """
+ |class Test {
+ | def bar() = {
+ | object Foo
+ | }
+ |}
+ """.stripMargin)
+
+ @Test def objectsInObjValAreFinal =
+ checkFinalClass("Test$Foo$1$.class",
+ """
+ |class Test {
+ | val bar = {
+ | object Foo
+ | }
+ |}
+ """.stripMargin)
}