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) }