summaryrefslogtreecommitdiff
path: root/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala
diff options
context:
space:
mode:
Diffstat (limited to 'test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala')
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala198
1 files changed, 198 insertions, 0 deletions
diff --git a/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala
new file mode 100644
index 0000000000..00b6d1cc42
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala
@@ -0,0 +1,198 @@
+package scala.tools.nsc.backend.jvm
+
+import org.junit.Assert._
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+import scala.tools.asm.Opcodes._
+import scala.tools.partest.ASMConverters._
+import scala.tools.testing.BytecodeTesting
+import scala.tools.testing.BytecodeTesting._
+
+@RunWith(classOf[JUnit4])
+class BytecodeTest extends BytecodeTesting {
+ import compiler._
+
+ @Test
+ def t6288bJumpPosition(): Unit = {
+ val code =
+ """object Case3 { // 01
+ | def unapply(z: Any): Option[Int] = Some(-1) // 02
+ | def main(args: Array[String]) { // 03
+ | ("": Any) match { // 04
+ | case x : String => // 05
+ | println("case 0") // 06 println and jump at 6
+ | case _ => // 07
+ | println("default") // 08 println and jump at 8
+ | } // 09
+ | println("done") // 10
+ | }
+ |}
+ """.stripMargin
+ val List(mirror, module) = compileClasses(code)
+
+ val unapplyLineNumbers = getInstructions(module, "unapply").filter(_.isInstanceOf[LineNumber])
+ assert(unapplyLineNumbers == List(LineNumber(2, Label(0))), unapplyLineNumbers)
+
+ val expected = List(
+ LineNumber(4, Label(0)),
+ LineNumber(5, Label(5)),
+ Jump(IFEQ, Label(20)),
+
+ LineNumber(6, Label(11)),
+ Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false),
+ Jump(GOTO, Label(33)),
+
+ LineNumber(5, Label(20)),
+ Jump(GOTO, Label(24)),
+
+ LineNumber(8, Label(24)),
+ Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false),
+ Jump(GOTO, Label(33)),
+
+ LineNumber(10, Label(33)),
+ Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false)
+ )
+
+ val mainIns = getInstructions(module, "main") filter {
+ case _: LineNumber | _: Invoke | _: Jump => true
+ case _ => false
+ }
+ assertSameCode(mainIns, expected)
+ }
+
+ @Test
+ def bytecodeForBranches(): Unit = {
+ val code =
+ """class C {
+ | def t1(b: Boolean) = if (b) 1 else 2
+ | def t2(x: Int) = if (x == 393) 1 else 2
+ | def t3(a: Array[String], b: AnyRef) = a != b && b == a
+ | def t4(a: AnyRef) = a == null || null != a
+ | def t5(a: AnyRef) = (a eq null) || (null ne a)
+ | def t6(a: Int, b: Boolean) = if ((a == 10) && b || a != 1) 1 else 2
+ | def t7(a: AnyRef, b: AnyRef) = a == b
+ | def t8(a: AnyRef) = Nil == a || "" != a
+ |}
+ """.stripMargin
+
+ val c = compileClass(code)
+
+ // t1: no unnecessary GOTOs
+ assertSameCode(getMethod(c, "t1"), List(
+ VarOp(ILOAD, 1), Jump(IFEQ, Label(6)),
+ Op(ICONST_1), Jump(GOTO, Label(9)),
+ Label(6), Op(ICONST_2),
+ Label(9), Op(IRETURN)))
+
+ // t2: no unnecessary GOTOs
+ assertSameCode(getMethod(c, "t2"), List(
+ VarOp(ILOAD, 1), IntOp(SIPUSH, 393), Jump(IF_ICMPNE, Label(7)),
+ Op(ICONST_1), Jump(GOTO, Label(10)),
+ Label(7), Op(ICONST_2),
+ Label(10), Op(IRETURN)))
+
+ // t3: Array == is translated to reference equality, AnyRef == to null checks and equals
+ assertSameCode(getMethod(c, "t3"), List(
+ // Array ==
+ VarOp(ALOAD, 1), VarOp(ALOAD, 2), Jump(IF_ACMPEQ, Label(23)),
+ // AnyRef ==
+ VarOp(ALOAD, 2), VarOp(ALOAD, 1), VarOp(ASTORE, 3), Op(DUP), Jump(IFNONNULL, Label(14)),
+ Op(POP), VarOp(ALOAD, 3), Jump(IFNULL, Label(19)), Jump(GOTO, Label(23)),
+ Label(14), VarOp(ALOAD, 3), Invoke(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false), Jump(IFEQ, Label(23)),
+ Label(19), Op(ICONST_1), Jump(GOTO, Label(26)),
+ Label(23), Op(ICONST_0),
+ Label(26), Op(IRETURN)))
+
+ val t4t5 = List(
+ VarOp(ALOAD, 1), Jump(IFNULL, Label(6)),
+ VarOp(ALOAD, 1), Jump(IFNULL, Label(10)),
+ Label(6), Op(ICONST_1), Jump(GOTO, Label(13)),
+ Label(10), Op(ICONST_0),
+ Label(13), Op(IRETURN))
+
+ // t4: one side is known null, so just a null check on the other
+ assertSameCode(getMethod(c, "t4"), t4t5)
+
+ // t5: one side known null, so just a null check on the other
+ assertSameCode(getMethod(c, "t5"), t4t5)
+
+ // t6: no unnecessary GOTOs
+ assertSameCode(getMethod(c, "t6"), List(
+ VarOp(ILOAD, 1), IntOp(BIPUSH, 10), Jump(IF_ICMPNE, Label(7)),
+ VarOp(ILOAD, 2), Jump(IFNE, Label(12)),
+ Label(7), VarOp(ILOAD, 1), Op(ICONST_1), Jump(IF_ICMPEQ, Label(16)),
+ Label(12), Op(ICONST_1), Jump(GOTO, Label(19)),
+ Label(16), Op(ICONST_2),
+ Label(19), Op(IRETURN)))
+
+ // t7: universal equality
+ assertInvoke(getMethod(c, "t7"), "scala/runtime/BoxesRunTime", "equals")
+
+ // t8: no null checks invoking equals on modules and constants
+ assertSameCode(getMethod(c, "t8"), List(
+ Field(GETSTATIC, "scala/collection/immutable/Nil$", "MODULE$", "Lscala/collection/immutable/Nil$;"), VarOp(ALOAD, 1), Invoke(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false), Jump(IFNE, Label(10)),
+ Ldc(LDC, ""), VarOp(ALOAD, 1), Invoke(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false), Jump(IFNE, Label(14)),
+ Label(10), Op(ICONST_1), Jump(GOTO, Label(17)),
+ Label(14), Op(ICONST_0),
+ Label(17), Op(IRETURN)))
+ }
+
+ @Test // wrong local variable table for methods containing while loops
+ def t9179(): Unit = {
+ val code =
+ """class C {
+ | def t(): Unit = {
+ | var x = ""
+ | while (x != null) {
+ | foo()
+ | x = null
+ | }
+ | bar()
+ | }
+ | def foo(): Unit = ()
+ | def bar(): Unit = ()
+ |}
+ """.stripMargin
+ val c = compileClass(code)
+ val t = getMethod(c, "t")
+ val isFrameLine = (x: Instruction) => x.isInstanceOf[FrameEntry] || x.isInstanceOf[LineNumber]
+ assertSameCode(t.instructions.filterNot(isFrameLine), List(
+ Label(0), Ldc(LDC, ""), Label(3), VarOp(ASTORE, 1),
+ Label(5), VarOp(ALOAD, 1), Jump(IFNULL, Label(21)),
+ Label(10), VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "C", "foo", "()V", false), Label(14), Op(ACONST_NULL), VarOp(ASTORE, 1), Label(18), Jump(GOTO, Label(5)),
+ Label(21), VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "C", "bar", "()V", false), Label(26), Op(RETURN), Label(28)))
+ val labels = t.instructions collect { case l: Label => l }
+ val x = t.localVars.find(_.name == "x").get
+ assertEquals(x.start, labels(1))
+ assertEquals(x.end, labels(7))
+ }
+
+ @Test
+ def sd186_traitLineNumber(): Unit = {
+ val code =
+ """trait T {
+ | def t(): Unit = {
+ | toString
+ | toString
+ | }
+ |}
+ """.stripMargin
+ val t = compileClass(code)
+ val tMethod = getMethod(t, "t$")
+ val invoke = Invoke(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false)
+ // ths static accessor is positioned at the line number of the accessed method.
+ assertSameCode(tMethod.instructions,
+ List(Label(0), LineNumber(2, Label(0)), VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, "T", "t", "()V", true), Op(RETURN), Label(4))
+ )
+ }
+
+ @Test
+ def sd233(): Unit = {
+ val code = "def f = { println(1); synchronized(println(2)) }"
+ val m = compileMethod(code)
+ val List(ExceptionHandler(_, _, _, desc)) = m.handlers
+ assert(desc == None, desc)
+ }
+}