diff options
Diffstat (limited to 'test/junit/scala/issues/BytecodeTest.scala')
-rw-r--r-- | test/junit/scala/issues/BytecodeTest.scala | 478 |
1 files changed, 0 insertions, 478 deletions
diff --git a/test/junit/scala/issues/BytecodeTest.scala b/test/junit/scala/issues/BytecodeTest.scala deleted file mode 100644 index cf5c7f9ec3..0000000000 --- a/test/junit/scala/issues/BytecodeTest.scala +++ /dev/null @@ -1,478 +0,0 @@ -package scala.issues - -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import org.junit.Test - -import scala.tools.asm.Opcodes._ -import scala.tools.nsc.backend.jvm.AsmUtils -import scala.tools.nsc.backend.jvm.CodeGenTools._ -import org.junit.Assert._ - -import scala.collection.JavaConverters._ -import scala.tools.asm.Opcodes -import scala.tools.asm.tree.ClassNode -import scala.tools.partest.ASMConverters._ -import scala.tools.testing.ClearAfterClass - -object BytecodeTest extends ClearAfterClass.Clearable { - var compiler = newCompiler() - def clear(): Unit = { compiler = null } -} - -@RunWith(classOf[JUnit4]) -class BytecodeTest extends ClearAfterClass { - ClearAfterClass.stateToClear = BytecodeTest - val compiler = BytecodeTest.compiler - - @Test - def t8731(): Unit = { - val code = - """class C { - | def f(x: Int) = (x: @annotation.switch) match { - | case 1 => 0 - | case 2 => 1 - | case 3 => 2 - | } - | final val K = 10 - | def g(x: Int) = (x: @annotation.switch) match { - | case K => 0 - | case 1 => 10 - | case 2 => 20 - | } - |} - """.stripMargin - - val List(c) = compileClasses(compiler)(code) - - assertTrue(getSingleMethod(c, "f").instructions.count(_.isInstanceOf[TableSwitch]) == 1) - assertTrue(getSingleMethod(c, "g").instructions.count(_.isInstanceOf[LookupSwitch]) == 1) - } - - @Test - def t8926(): Unit = { - import scala.reflect.internal.util.BatchSourceFile - - // this test cannot be implemented using partest because of its mixed-mode compilation strategy: - // partest first compiles all files with scalac, then the java files, and then again the scala - // using the output classpath. this shadows the bug SI-8926. - - val annotA = - """import java.lang.annotation.Retention; - |import java.lang.annotation.RetentionPolicy; - |@Retention(RetentionPolicy.RUNTIME) - |public @interface AnnotA { } - """.stripMargin - val annotB = "public @interface AnnotB { }" - - val scalaSrc = - """@AnnotA class A - |@AnnotB class B - """.stripMargin - - val run = new compiler.Run() - run.compileSources(List(new BatchSourceFile("AnnotA.java", annotA), new BatchSourceFile("AnnotB.java", annotB), new BatchSourceFile("Test.scala", scalaSrc))) - val outDir = compiler.settings.outputDirs.getSingleOutput.get - val outfiles = (for (f <- outDir.iterator if !f.isDirectory) yield (f.name, f.toByteArray)).toList - - def check(classfile: String, annotName: String) = { - val f = (outfiles collect { case (`classfile`, bytes) => AsmUtils.readClass(bytes) }).head - val descs = f.visibleAnnotations.asScala.map(_.desc).toList - assertTrue(descs.toString, descs exists (_ contains annotName)) - } - - check("A.class", "AnnotA") - - // known issue SI-8928: the visibility of AnnotB should be CLASS, but annotation classes without - // a @Retention annotation are currently emitted as RUNTIME. - check("B.class", "AnnotB") - } - - @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(compiler)(code) - - val unapplyLineNumbers = getSingleMethod(module, "unapply").instructions.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 = getSingleMethod(module, "main").instructions 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 List(c) = compileClasses(compiler)(code) - - // t1: no unnecessary GOTOs - assertSameCode(getSingleMethod(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(getSingleMethod(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(getSingleMethod(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(getSingleMethod(c, "t4"), t4t5) - - // t5: one side known null, so just a null check on the other - assertSameCode(getSingleMethod(c, "t5"), t4t5) - - // t6: no unnecessary GOTOs - assertSameCode(getSingleMethod(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(getSingleMethod(c, "t7"), "scala/runtime/BoxesRunTime", "equals") - - // t8: no null checks invoking equals on modules and constants - assertSameCode(getSingleMethod(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))) - } - - object forwarderTestUtils { - def findMethods(cls: ClassNode, name: String): List[Method] = cls.methods.iterator.asScala.find(_.name == name).map(convertMethod).toList - - import language.implicitConversions - implicit def s2c(s: Symbol)(implicit classes: Map[String, ClassNode]): ClassNode = classes(s.name) - - def checkForwarder(c: ClassNode, target: String) = { - val List(f) = findMethods(c, "f") - assertSameCode(f, List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, target, "f", "()I", false), Op(IRETURN))) - } - } - - @Test - def traitMethodForwarders(): Unit = { - import forwarderTestUtils._ - val code = - """trait T1 { def f = 1 } - |trait T2 extends T1 { override def f = 2 } - |trait T3 { self: T1 => override def f = 3 } - | - |abstract class A1 { def f: Int } - |class A2 { def f: Int = 4 } - | - |trait T4 extends A1 { def f = 5 } - |trait T5 extends A2 { override def f = 6 } - | - |trait T6 { def f: Int } - |trait T7 extends T6 { abstract override def f = super.f + 1 } - | - |trait T8 { override def clone() = super.clone() } - | - |class A3 extends T1 { override def f = 7 } - | - |class C1 extends T1 - |class C2 extends T2 - |class C3 extends T1 with T2 - |class C4 extends T2 with T1 - |class C5 extends T1 with T3 - | - |// traits extending a class that defines f - |class C6 extends T4 - |class C7 extends T5 - |class C8 extends A1 with T4 - |class C9 extends A2 with T5 - | - |// T6: abstract f in trait - |class C10 extends T6 with T1 - |class C11 extends T6 with T2 - |abstract class C12 extends A1 with T6 - |class C13 extends A2 with T6 - |class C14 extends T4 with T6 - |class C15 extends T5 with T6 - | - |// superclass overrides a trait method - |class C16 extends A3 - |class C17 extends A3 with T1 - | - |// abstract override - |class C18 extends T6 { def f = 22 } - |class C19 extends C18 with T7 - | - |class C20 extends T8 - """.stripMargin - - implicit val classes = compileClasses(compiler)(code).map(c => (c.name, c)).toMap - - val noForwarder = List('C1, 'C2, 'C3, 'C4, 'C10, 'C11, 'C12, 'C13, 'C16, 'C17) - for (c <- noForwarder) assertEquals(findMethods(c, "f"), Nil) - - checkForwarder('C5, "T3") - checkForwarder('C6, "T4") - checkForwarder('C7, "T5") - checkForwarder('C8, "T4") - checkForwarder('C9, "T5") - checkForwarder('C14, "T4") - checkForwarder('C15, "T5") - assertSameSummary(getSingleMethod('C18, "f"), List(BIPUSH, IRETURN)) - checkForwarder('C19, "T7") - assertSameCode(getSingleMethod('C19, "T7$$super$f"), List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, "C18", "f", "()I", false), Op(IRETURN))) - assertInvoke(getSingleMethod('C20, "clone"), "T8", "clone") // mixin forwarder - } - - @Test - def noTraitMethodForwardersForOverloads(): Unit = { - import forwarderTestUtils._ - val code = - """trait T1 { def f(x: Int) = 0 } - |trait T2 { def f(x: String) = 1 } - |class C extends T1 with T2 - """.stripMargin - val List(c, t1, t2) = compileClasses(compiler)(code) - assertEquals(findMethods(c, "f"), Nil) - } - - @Test - def traitMethodForwardersForJavaDefaultMethods(): Unit = { - import forwarderTestUtils._ - val j1 = ("interface J1 { int f(); }", "J1.java") - val j2 = ("interface J2 { default int f() { return 1; } }", "J2.java") - val j3 = ("interface J3 extends J1 { default int f() { return 2; } }", "J3.java") - val j4 = ("interface J4 extends J2 { default int f() { return 3; } }", "J4.java") - val code = - """trait T1 extends J2 { override def f = 4 } - |trait T2 { self: J2 => override def f = 5 } - | - |class K1 extends J2 - |class K2 extends J1 with J2 - |class K3 extends J2 with J1 - | - |class K4 extends J3 - |class K5 extends J3 with J1 - |class K6 extends J1 with J3 - | - |class K7 extends J4 - |class K8 extends J4 with J2 - |class K9 extends J2 with J4 - | - |class K10 extends T1 with J2 - |class K11 extends J2 with T1 - | - |class K12 extends J2 with T2 - """.stripMargin - implicit val classes = compileClasses(compiler)(code, List(j1, j2, j3, j4)).map(c => (c.name, c)).toMap - - val noForwarder = List('K1, 'K2, 'K3, 'K4, 'K5, 'K6, 'K7, 'K8, 'K9, 'K10, 'K11) - for (c <- noForwarder) assertEquals(findMethods(c, "f"), Nil) - - checkForwarder('K12, "T2") - } - - @Test - def invocationReceivers(): Unit = { - val List(c1, c2, t, u) = compileClasses(compiler)(invocationReceiversTestCode.definitions("Object")) - // mixin forwarder in C1 - assertSameCode(getSingleMethod(c1, "clone"), List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, "T", "clone", "()Ljava/lang/Object;", false), Op(ARETURN))) - assertInvoke(getSingleMethod(c1, "f1"), "T", "clone") - assertInvoke(getSingleMethod(c1, "f2"), "T", "clone") - assertInvoke(getSingleMethod(c1, "f3"), "C1", "clone") - assertInvoke(getSingleMethod(c2, "f1"), "T", "clone") - assertInvoke(getSingleMethod(c2, "f2"), "T", "clone") - assertInvoke(getSingleMethod(c2, "f3"), "C1", "clone") - - val List(c1b, c2b, tb, ub) = compileClasses(compiler)(invocationReceiversTestCode.definitions("String")) - def ms(c: ClassNode, n: String) = c.methods.asScala.toList.filter(_.name == n) - assert(ms(tb, "clone").length == 1) - assert(ms(ub, "clone").isEmpty) - val List(c1Clone) = ms(c1b, "clone") - assertEquals(c1Clone.desc, "()Ljava/lang/Object;") - assert((c1Clone.access | Opcodes.ACC_BRIDGE) != 0) - assertSameCode(convertMethod(c1Clone), List(VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "C1", "clone", "()Ljava/lang/String;", false), Op(ARETURN))) - - def iv(m: Method) = getSingleMethod(c1b, "f1").instructions.collect({case i: Invoke => i}) - assertSameCode(iv(getSingleMethod(c1b, "f1")), List(Invoke(INVOKEINTERFACE, "T", "clone", "()Ljava/lang/String;", true))) - assertSameCode(iv(getSingleMethod(c1b, "f2")), List(Invoke(INVOKEINTERFACE, "T", "clone", "()Ljava/lang/String;", true))) - // invokeinterface T.clone in C1 is OK here because it is not an override of Object.clone (different siganture) - assertSameCode(iv(getSingleMethod(c1b, "f3")), List(Invoke(INVOKEINTERFACE, "T", "clone", "()Ljava/lang/String;", true))) - } - - @Test - def invocationReceiversProtected(): Unit = { - // http://lrytz.github.io/scala-aladdin-bugtracker/displayItem.do%3Fid=455.html / 9954eaf - // also https://issues.scala-lang.org/browse/SI-1430 / 0bea2ab (same but with interfaces) - val aC = - """package a; - |/*package private*/ abstract class A { - | public int f() { return 1; } - | public int t; - |} - """.stripMargin - val bC = - """package a; - |public class B extends A { } - """.stripMargin - val iC = - """package a; - |/*package private*/ interface I { int f(); } - """.stripMargin - val jC = - """package a; - |public interface J extends I { } - """.stripMargin - val cC = - """package b - |class C { - | def f1(b: a.B) = b.f - | def f2(b: a.B) = { b.t = b.t + 1 } - | def f3(j: a.J) = j.f - |} - """.stripMargin - val List(c) = compileClasses(compiler)(cC, javaCode = List((aC, "A.java"), (bC, "B.java"), (iC, "I.java"), (jC, "J.java"))) - assertInvoke(getSingleMethod(c, "f1"), "a/B", "f") // receiver needs to be B (A is not accessible in class C, package b) - println(getSingleMethod(c, "f2").instructions.stringLines) - assertInvoke(getSingleMethod(c, "f3"), "a/J", "f") // receiver needs to be J - } - - @Test - def specialInvocationReceivers(): Unit = { - val code = - """class C { - | def f1(a: Array[String]) = a.clone() - | def f2(a: Array[Int]) = a.hashCode() - | def f3(n: Nothing) = n.hashCode() - | def f4(n: Null) = n.toString() - | - |} - """.stripMargin - val List(c) = compileClasses(compiler)(code) - assertInvoke(getSingleMethod(c, "f1"), "[Ljava/lang/String;", "clone") // array descriptor as receiver - assertInvoke(getSingleMethod(c, "f2"), "java/lang/Object", "hashCode") // object receiver - assertInvoke(getSingleMethod(c, "f3"), "java/lang/Object", "hashCode") - assertInvoke(getSingleMethod(c, "f4"), "java/lang/Object", "toString") - } -} - -object invocationReceiversTestCode { - // if cloneType is more specific than Object (e.g., String), a bridge method is generated. - def definitions(cloneType: String) = - s"""trait T { override def clone(): $cloneType = "hi" } - |trait U extends T - |class C1 extends U with Cloneable { - | // The comments below are true when $cloneType is Object. - | // C1 gets a forwarder for clone that invokes T.clone. this is needed because JVM method - | // resolution always prefers class members, so it would resolve to Object.clone, even if - | // C1 is a subtype of the interface T which has an overriding default method for clone. - | - | // invokeinterface T.clone - | def f1 = (this: T).clone() - | - | // cannot invokeinterface U.clone (NoSuchMethodError). Object.clone would work here, but - | // not in the example in C2 (illegal access to protected). T.clone works in all cases and - | // resolves correctly. - | def f2 = (this: U).clone() - | - | // invokevirtual C1.clone() - | def f3 = (this: C1).clone() - |} - | - |class C2 { - | def f1(t: T) = t.clone() // invokeinterface T.clone - | def f2(t: U) = t.clone() // invokeinterface T.clone -- Object.clone would be illegal (protected, explained in C1) - | def f3(t: C1) = t.clone() // invokevirtual C1.clone -- Object.clone would be illegal - |} - """.stripMargin - - val runCode = - """ - |val r = new StringBuffer() - |val c1 = new C1 - |r.append(c1.f1) - |r.append(c1.f2) - |r.append(c1.f3) - |val t = new T { } - |val u = new U { } - |val c2 = new C2 - |r.append(c2.f1(t)) - |r.append(c2.f1(u)) - |r.append(c2.f1(c1)) - |r.append(c2.f2(u)) - |r.append(c2.f2(c1)) - |r.append(c2.f3(c1)) - |r.toString - """.stripMargin -} |