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