From 2537027195fd1702bbd12ba8e9d6cb3262b03482 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 20 May 2016 15:19:50 +0200 Subject: Split RunTest and BytecodeTest into parts, put in matching packages. --- test/junit/scala/BoxUnboxTest.scala | 119 ------ .../scala/PartialFunctionSerializationTest.scala | 16 +- test/junit/scala/PredefAutoboxingTest.scala | 35 -- test/junit/scala/StringContextTest.scala | 266 ------------ test/junit/scala/issues/BytecodeTest.scala | 470 --------------------- .../junit/scala/issues/OptimizedBytecodeTest.scala | 367 ---------------- test/junit/scala/issues/RunTest.scala | 247 ----------- .../scala/lang/annotations/BytecodeTest.scala | 80 ++++ test/junit/scala/lang/annotations/RunTest.scala | 32 ++ .../junit/scala/lang/primitives/BoxUnboxTest.scala | 222 ++++++++++ .../lang/primitives/PredefAutoboxingTest.scala | 33 ++ .../lang/stringinterpol/StringContextTest.scala | 265 ++++++++++++ test/junit/scala/lang/traits/BytecodeTest.scala | 282 +++++++++++++ test/junit/scala/lang/traits/RunTest.scala | 20 + test/junit/scala/reflect/ClassOfTest.scala | 124 ++++++ .../scala/tools/nsc/backend/jvm/BytecodeTest.scala | 140 ++++++ .../nsc/backend/jvm/OptimizedBytecodeTest.scala | 362 ++++++++++++++++ 17 files changed, 1565 insertions(+), 1515 deletions(-) delete mode 100644 test/junit/scala/BoxUnboxTest.scala delete mode 100644 test/junit/scala/PredefAutoboxingTest.scala delete mode 100644 test/junit/scala/StringContextTest.scala delete mode 100644 test/junit/scala/issues/BytecodeTest.scala delete mode 100644 test/junit/scala/issues/OptimizedBytecodeTest.scala delete mode 100644 test/junit/scala/issues/RunTest.scala create mode 100644 test/junit/scala/lang/annotations/BytecodeTest.scala create mode 100644 test/junit/scala/lang/annotations/RunTest.scala create mode 100644 test/junit/scala/lang/primitives/BoxUnboxTest.scala create mode 100644 test/junit/scala/lang/primitives/PredefAutoboxingTest.scala create mode 100644 test/junit/scala/lang/stringinterpol/StringContextTest.scala create mode 100644 test/junit/scala/lang/traits/BytecodeTest.scala create mode 100644 test/junit/scala/lang/traits/RunTest.scala create mode 100644 test/junit/scala/reflect/ClassOfTest.scala create mode 100644 test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala create mode 100644 test/junit/scala/tools/nsc/backend/jvm/OptimizedBytecodeTest.scala diff --git a/test/junit/scala/BoxUnboxTest.scala b/test/junit/scala/BoxUnboxTest.scala deleted file mode 100644 index 88b3037e69..0000000000 --- a/test/junit/scala/BoxUnboxTest.scala +++ /dev/null @@ -1,119 +0,0 @@ -package scala - -import org.junit.Test -import org.junit.Assert._ -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -import scala.tools.testing.AssertUtil._ - -@RunWith(classOf[JUnit4]) -class BoxUnboxTest { - def genericNull[T] = null.asInstanceOf[T] // allowed, see SI-4437, point 2 - - @Test - def boxUnboxInt(): Unit = { - val b = new Integer(1) - val u = 1 - - assertEquals(1.toInt, u) - - assertEquals(Predef.int2Integer(1), b) - assertEquals(1: Integer, b) - assertEquals(Int.box(1), b) - assertEquals(1.asInstanceOf[Object], b) - - assertThrows[ClassCastException]("".asInstanceOf[Integer]) - - assertEquals(Predef.Integer2int(b), u) - assertEquals(b: Int, u) - assertEquals(Int.unbox(b), u) - assertEquals(b.asInstanceOf[Int], u) - assertEquals(b.intValue, u) - assertEquals(b.toInt, u) - intWrapper(b).toInt - - assertThrows[ClassCastException](Int.unbox("")) - assertThrows[ClassCastException]("".asInstanceOf[Int]) - - // null unboxing in various positions - - val n1 = Int.unbox(null) - assertEquals(n1, 0) - val n2 = Predef.Integer2int(null) - assertEquals(n2, 0) - val n3 = (null: Integer): Int - assertEquals(n3, 0) - val n4 = null.asInstanceOf[Int] - assertEquals(n4, 0) - val n5 = null.asInstanceOf[Int] == 0 - assertTrue(n5) - val n6 = null.asInstanceOf[Int] == null - assertFalse(n6) - val n7 = null.asInstanceOf[Int] != 0 - assertFalse(n7) - val n8 = null.asInstanceOf[Int] != null - assertTrue(n8) - - val mp = new java.util.HashMap[Int, Int] - val n9 = mp.get(0) - assertEquals(n9, 0) - val n10 = mp.get(0) == null // SI-602 - assertThrows[AssertionError](assertFalse(n10)) // should not throw - - def f(a: Any) = "" + a - val n11 = f(null.asInstanceOf[Int]) - assertEquals(n11, "0") - - def n12 = genericNull[Int] - assertEquals(n12, 0) - } - - @Test - def numericConversions(): Unit = { - val i1 = 1L.asInstanceOf[Int] - assertEquals(i1, 1) - assertThrows[ClassCastException] { - val i2 = (1L: Any).asInstanceOf[Int] // SI-1448, should not throw. see also SI-4437 point 1. - assertEquals(i2, 1) - } - } - - @Test - def boxUnboxBoolean(): Unit = { - val n1 = Option(null.asInstanceOf[Boolean]) - assertEquals(n1, Some(false)) - } - - @Test - def boxUnboxUnit(): Unit = { - // should not use assertEquals in this test: it takes two Object parameters. normally, Unit does - // not conform to Object, but for Java-defined methods scalac makes an exception and treats them - // as Any. passing a Unit as Any makes the compiler go through another layer of boxing, so it - // can hide some bugs (where we actually have a null, but the compiler makes it a ()). - - var v = 0 - def eff() = { v = 1 } - def chk() = { assert(v == 1); v = 0 } - - val b = runtime.BoxedUnit.UNIT - - assert(eff() == b); chk() - assert(Unit.box(eff()) == b); chk() - assert(().asInstanceOf[Object] == b) - - Unit.unbox({eff(); b}); chk() - Unit.unbox({eff(); null}); chk() - assertThrows[ClassCastException](Unit.unbox({eff(); ""})); chk() - - val n1 = null.asInstanceOf[Unit] - assert(n1 == b) - - val n2 = null.asInstanceOf[Unit] == b - assert(n2) - - def f(a: Any) = "" + a - val n3 = f(null.asInstanceOf[Unit]) - assertEquals(n3, "()") - } -} diff --git a/test/junit/scala/PartialFunctionSerializationTest.scala b/test/junit/scala/PartialFunctionSerializationTest.scala index d525b045cd..2019e3a425 100644 --- a/test/junit/scala/PartialFunctionSerializationTest.scala +++ b/test/junit/scala/PartialFunctionSerializationTest.scala @@ -7,24 +7,18 @@ import org.junit.runners.JUnit4 @RunWith(classOf[JUnit4]) class PartialFunctionSerializationTest { - val pf1: PartialFunction[Int, Int] = { - case n if n > 0 => 1 - } - - val pf2: PartialFunction[Int, Int] = { - case n if n <= 0 => 2 - } + val pf1: PartialFunction[Int, Int] = { case n if n > 0 => 1 } + val pf2: PartialFunction[Int, Int] = { case n if n <= 0 => 2 } - private def assertSerializable[A,B](fn: A => B) = { + private def assertSerializable[A,B](fn: A => B): Unit = { import java.io._ - new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(fn) } - @Test def canSerializeLiteral= assertSerializable(pf1) + @Test def canSerializeLiteral = assertSerializable(pf1) - @Test def canSerializeLifted= assertSerializable(pf1.lift) + @Test def canSerializeLifted = assertSerializable(pf1.lift) @Test def canSerializeOrElse = assertSerializable(pf1 orElse pf2) diff --git a/test/junit/scala/PredefAutoboxingTest.scala b/test/junit/scala/PredefAutoboxingTest.scala deleted file mode 100644 index e5d8ded5d4..0000000000 --- a/test/junit/scala/PredefAutoboxingTest.scala +++ /dev/null @@ -1,35 +0,0 @@ -package scala - -import org.junit.Test -import org.junit.Assert._ -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -import scala.tools.testing.AssertUtil._ - -@RunWith(classOf[JUnit4]) -class PredefAutoboxingTest { - @Test def unboxNullByte() = - assertEquals(Predef.Byte2byte(null), 0.toByte) - - @Test def unboxNullShort() = - assertEquals(Predef.Short2short(null), 0.toShort) - - @Test def unboxNullCharacter() = - assertEquals(Predef.Character2char(null), 0.toChar) - - @Test def unboxNullInteger() = - assertEquals(Predef.Integer2int(null), 0) - - @Test def unboxNullLong() = - assertEquals(Predef.Long2long(null), 0L) - - @Test def unboxNullFloat() = - assertEquals(Predef.Float2float(null), 0F, 0F) - - @Test def unboxNullDouble() = - assertEquals(Predef.Double2double(null), 0D, 0D) - - @Test def unboxNullBoolean() = - assertEquals(Predef.Boolean2boolean(null), false) -} diff --git a/test/junit/scala/StringContextTest.scala b/test/junit/scala/StringContextTest.scala deleted file mode 100644 index b5af6de7eb..0000000000 --- a/test/junit/scala/StringContextTest.scala +++ /dev/null @@ -1,266 +0,0 @@ - -package scala - -import java.text.DecimalFormat - -import language.implicitConversions - -import org.junit.Test -import org.junit.Assert._ -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -import scala.tools.testing.AssertUtil._ - -object StringContextTestUtils { - private val decimalSeparator: Char = new DecimalFormat().getDecimalFormatSymbols().getDecimalSeparator() - private val numberPattern = """(\d+)\.(\d+.*)""".r - - implicit class StringContextOps(val sc: StringContext) extends AnyVal { - // Use this String interpolator to avoid problems with a locale-dependent decimal mark. - def locally(numbers: String*): String = { - val numbersWithCorrectLocale = numbers.map(applyProperLocale) - sc.s(numbersWithCorrectLocale: _*) - } - - // Handles cases like locally"3.14" - it's prettier than locally"${"3.14"}". - def locally(): String = sc.parts.map(applyProperLocale).mkString - - private def applyProperLocale(number: String): String = { - val numberPattern(intPart, fractionalPartAndSuffix) = number - s"$intPart$decimalSeparator$fractionalPartAndSuffix" - } - } -} - -@RunWith(classOf[JUnit4]) -class StringContextTest { - - import StringContext._ - import StringContextTestUtils.StringContextOps - - @Test def noEscape() = { - val s = "string" - val res = processEscapes(s) - assertEquals(s, res) - } - @Test def tabbed() = { - val s = """a\tb""" - val res = processEscapes(s) - assertEquals("a\tb", res) - } - @Test def quoted() = { - val s = """hello, \"world\"""" - val res = processEscapes(s) - assertEquals("""hello, "world"""", res) - } - @Test def octal() = { - val s = """\123cala""" - val res = treatEscapes(s) - assertEquals("Scala", res) - } - @Test def doubled() = { - val s = """\123cala\123yntax""" - val res = treatEscapes(s) - assertEquals("ScalaSyntax", res) - } - @Test def badly() = assertThrows[InvalidEscapeException] { - val s = """Scala\""" - val res = treatEscapes(s) - assertEquals("Scala", res) - } - @Test def noOctal() = assertThrows[InvalidEscapeException] { - val s = """\123cala""" - val res = processEscapes(s) - assertEquals("Scala", res) - } - - @Test def t6631_baseline() = assertEquals("\f\r\n\t", s"""\f\r\n\t""") - - @Test def t6631_badEscape() = assertThrows[InvalidEscapeException] { - s"""\x""" - } - - // verifying that the standard interpolators can be supplanted - @Test def antiHijack_?() = { - object AllYourStringsAreBelongToMe { case class StringContext(args: Any*) { def s(args: Any) = "!!!!" } } - import AllYourStringsAreBelongToMe._ - //assertEquals("????", s"????") - assertEquals("!!!!", s"????") // OK to hijack core interpolator ids - } - - @Test def fIf() = { - val res = f"${if (true) 2.5 else 2.5}%.2f" - val expected = locally"2.50" - assertEquals(expected, res) - } - - @Test def fIfNot() = { - val res = f"${if (false) 2.5 else 3.5}%.2f" - val expected = locally"3.50" - assertEquals(expected, res) - } - - @Test def fHeteroArgs() = { - val res = f"${3.14}%.2f rounds to ${3}%d" - val expected = locally"${"3.14"} rounds to 3" - assertEquals(expected, res) - } - - @Test def `f interpolator baseline`(): Unit = { - - implicit def stringToBoolean(s: String): Boolean = java.lang.Boolean.parseBoolean(s) - implicit def stringToChar(s: String): Char = s(0) - implicit def str2fmt(s: String): java.util.Formattable = new java.util.Formattable { - def formatTo(f: java.util.Formatter, g: Int, w: Int, p: Int) = f.format("%s", s) - } - - val b_true = true - val b_false = false - - val i = 42 - - val f_zero = 0.0 - val f_zero_- = -0.0 - - val s = "Scala" - - val fff = new java.util.Formattable { - def formatTo(f: java.util.Formatter, g: Int, w: Int, p: Int) = f.format("4") - } - import java.util.{ Calendar, Locale } - val c = Calendar.getInstance(Locale.US) - c.set(2012, Calendar.MAY, 26) - implicit def strToDate(x: String): Calendar = c - - val ss = List[(String, String)] ( - // 'b' / 'B' (category: general) - // ----------------------------- - f"${b_false}%b" -> "false", - f"${b_true}%b" -> "true", - - f"${null}%b" -> "false", - f"${false}%b" -> "false", - f"${true}%b" -> "true", - f"${true && false}%b" -> "false", - f"${new java.lang.Boolean(false)}%b" -> "false", - f"${new java.lang.Boolean(true)}%b" -> "true", - - f"${null}%B" -> "FALSE", - f"${false}%B" -> "FALSE", - f"${true}%B" -> "TRUE", - f"${new java.lang.Boolean(false)}%B" -> "FALSE", - f"${new java.lang.Boolean(true)}%B" -> "TRUE", - - f"${"true"}%b" -> "true", - f"${"false"}%b"-> "false", - - // 'h' | 'H' (category: general) - // ----------------------------- - f"${null}%h" -> "null", - f"${f_zero}%h" -> "0", - f"${f_zero_-}%h" -> "80000000", - f"${s}%h" -> "4c01926", - - f"${null}%H" -> "NULL", - f"${s}%H" -> "4C01926", - - // 's' | 'S' (category: general) - // ----------------------------- - f"${null}%s" -> "null", - f"${null}%S" -> "NULL", - f"${s}%s" -> "Scala", - f"${s}%S" -> "SCALA", - f"${5}" -> "5", - f"${i}" -> "42", - f"${'foo}" -> "'foo", - - f"${Thread.State.NEW}" -> "NEW", - - // 'c' | 'C' (category: character) - // ------------------------------- - f"${120:Char}%c" -> "x", - f"${120:Byte}%c" -> "x", - f"${120:Short}%c" -> "x", - f"${120:Int}%c" -> "x", - f"${new java.lang.Character('x')}%c" -> "x", - f"${new java.lang.Byte(120:Byte)}%c" -> "x", - f"${new java.lang.Short(120:Short)}%c" -> "x", - f"${new java.lang.Integer(120)}%c" -> "x", - - f"${'x' : java.lang.Character}%c" -> "x", - f"${(120:Byte) : java.lang.Byte}%c" -> "x", - f"${(120:Short) : java.lang.Short}%c" -> "x", - f"${120 : java.lang.Integer}%c" -> "x", - - f"${"Scala"}%c" -> "S", - - // 'd' | 'o' | 'x' | 'X' (category: integral) - // ------------------------------------------ - f"${120:Byte}%d" -> "120", - f"${120:Short}%d" -> "120", - f"${120:Int}%d" -> "120", - f"${120:Long}%d" -> "120", - f"${60 * 2}%d" -> "120", - f"${new java.lang.Byte(120:Byte)}%d" -> "120", - f"${new java.lang.Short(120:Short)}%d" -> "120", - f"${new java.lang.Integer(120)}%d" -> "120", - f"${new java.lang.Long(120)}%d" -> "120", - f"${120 : java.lang.Integer}%d" -> "120", - f"${120 : java.lang.Long}%d" -> "120", - f"${BigInt(120)}%d" -> "120", - - f"${new java.math.BigInteger("120")}%d" -> "120", - - f"${4}%#10X" -> " 0X4", - - f"She is ${fff}%#s feet tall." -> "She is 4 feet tall.", - - f"Just want to say ${"hello, world"}%#s..." -> "Just want to say hello, world...", - - { implicit val strToShort = (s: String) => java.lang.Short.parseShort(s) ; f"${"120"}%d" } -> "120", - { implicit val strToInt = (s: String) => 42 ; f"${"120"}%d" } -> "42", - - // 'e' | 'E' | 'g' | 'G' | 'f' | 'a' | 'A' (category: floating point) - // ------------------------------------------------------------------ - f"${3.4f}%e" -> locally"3.400000e+00", - f"${3.4}%e" -> locally"3.400000e+00", - f"${3.4f : java.lang.Float}%e" -> locally"3.400000e+00", - f"${3.4 : java.lang.Double}%e" -> locally"3.400000e+00", - - f"${BigDecimal(3.4)}%e" -> locally"3.400000e+00", - - f"${new java.math.BigDecimal(3.4)}%e" -> locally"3.400000e+00", - - f"${3}%e" -> locally"3.000000e+00", - f"${3L}%e" -> locally"3.000000e+00", - - // 't' | 'T' (category: date/time) - // ------------------------------- - f"${c}%TD" -> "05/26/12", - f"${c.getTime}%TD" -> "05/26/12", - f"${c.getTime.getTime}%TD" -> "05/26/12", - f"""${"1234"}%TD""" -> "05/26/12", - - // literals and arg indexes - f"%%" -> "%", - f" mind%n------%nmatter" -> - """| mind - |------ - |matter""".stripMargin.lines.mkString(compat.Platform.EOL), - f"${i}%d % "42 42 9", - f"${7}%d % "7 7 9", - f"${7}%d %2$$d ${9}%d" -> "7 9 9", - - f"${null}%d % "null FALSE", - - f"${5: Any}" -> "5", - f"${5}%s% "55", - f"${3.14}%s,% locally"3.14,${"3.140000"}", - - f"z" -> "z" - ) - - for ((f, s) <- ss) assertEquals(s, f) - } -} diff --git a/test/junit/scala/issues/BytecodeTest.scala b/test/junit/scala/issues/BytecodeTest.scala deleted file mode 100644 index 125024f746..0000000000 --- a/test/junit/scala/issues/BytecodeTest.scala +++ /dev/null @@ -1,470 +0,0 @@ -package scala.issues - -import org.junit.Assert._ -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -import scala.collection.JavaConverters._ -import scala.tools.asm.Opcodes -import scala.tools.asm.Opcodes._ -import scala.tools.asm.tree.ClassNode -import scala.tools.nsc.backend.jvm.AsmUtils -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 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 c = compileClass(code) - - assertTrue(getInstructions(c, "f").count(_.isInstanceOf[TableSwitch]) == 1) - assertTrue(getInstructions(c, "g").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 global.Run() - run.compileSources(List(new BatchSourceFile("AnnotA.java", annotA), new BatchSourceFile("AnnotB.java", annotB), new BatchSourceFile("Test.scala", scalaSrc))) - val outDir = global.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(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))) - } - - def checkForwarder(classes: Map[String, ClassNode], clsName: Symbol, target: String) = { - val List(f) = getMethods(classes(clsName.name), "f") - assertSameCode(f, List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, target, "f", "()I", false), Op(IRETURN))) - } - - @Test - def traitMethodForwarders(): Unit = { - 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 - - val c = compileClasses(code).map(c => (c.name, c)).toMap - - val noForwarder = List('C1, 'C2, 'C3, 'C4, 'C10, 'C11, 'C12, 'C13, 'C16, 'C17) - for (cn <- noForwarder) assertEquals(getMethods(c(cn.name), "f"), Nil) - - checkForwarder(c, 'C5, "T3") - checkForwarder(c, 'C6, "T4") - checkForwarder(c, 'C7, "T5") - checkForwarder(c, 'C8, "T4") - checkForwarder(c, 'C9, "T5") - checkForwarder(c, 'C14, "T4") - checkForwarder(c, 'C15, "T5") - assertSameSummary(getMethod(c("C18"), "f"), List(BIPUSH, IRETURN)) - checkForwarder(c, 'C19, "T7") - assertSameCode(getMethod(c("C19"), "T7$$super$f"), List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, "C18", "f", "()I", false), Op(IRETURN))) - assertInvoke(getMethod(c("C20"), "clone"), "T8", "clone") // mixin forwarder - } - - @Test - def noTraitMethodForwardersForOverloads(): Unit = { - 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(code) - assertEquals(getMethods(c, "f"), Nil) - } - - @Test - def traitMethodForwardersForJavaDefaultMethods(): Unit = { - 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 - val c = compileClasses(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 (cn <- noForwarder) assertEquals(getMethods(c(cn.name), "f"), Nil) - - checkForwarder(c, 'K12, "T2") - } - - @Test - def invocationReceivers(): Unit = { - val List(c1, c2, t, u) = compileClasses(invocationReceiversTestCode.definitions("Object")) - // mixin forwarder in C1 - assertSameCode(getMethod(c1, "clone"), List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, "T", "clone", "()Ljava/lang/Object;", false), Op(ARETURN))) - assertInvoke(getMethod(c1, "f1"), "T", "clone") - assertInvoke(getMethod(c1, "f2"), "T", "clone") - assertInvoke(getMethod(c1, "f3"), "C1", "clone") - assertInvoke(getMethod(c2, "f1"), "T", "clone") - assertInvoke(getMethod(c2, "f2"), "T", "clone") - assertInvoke(getMethod(c2, "f3"), "C1", "clone") - - val List(c1b, c2b, tb, ub) = compileClasses(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) = getInstructions(c1b, "f1").collect({case i: Invoke => i}) - assertSameCode(iv(getMethod(c1b, "f1")), List(Invoke(INVOKEINTERFACE, "T", "clone", "()Ljava/lang/String;", true))) - assertSameCode(iv(getMethod(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(getMethod(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 c = compileClass(cC, javaCode = List((aC, "A.java"), (bC, "B.java"), (iC, "I.java"), (jC, "J.java"))) - assertInvoke(getMethod(c, "f1"), "a/B", "f") // receiver needs to be B (A is not accessible in class C, package b) - assertInvoke(getMethod(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 c = compileClass(code) - assertInvoke(getMethod(c, "f1"), "[Ljava/lang/String;", "clone") // array descriptor as receiver - assertInvoke(getMethod(c, "f2"), "java/lang/Object", "hashCode") // object receiver - assertInvoke(getMethod(c, "f3"), "java/lang/Object", "hashCode") - assertInvoke(getMethod(c, "f4"), "java/lang/Object", "toString") - } - - @Test - def superConstructorArgumentInSpecializedClass(): Unit = { - // see comment in SpecializeTypes.forwardCtorCall - val code = "case class C[@specialized(Int) T](_1: T)" - val List(c, cMod, cSpec) = compileClasses(code) - assertSameSummary(getMethod(cSpec, ""), - // pass `null` to super constructor, no box-unbox, no Integer created - List(ALOAD, ILOAD, PUTFIELD, ALOAD, ACONST_NULL, "", RETURN)) - } -} - -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 -} diff --git a/test/junit/scala/issues/OptimizedBytecodeTest.scala b/test/junit/scala/issues/OptimizedBytecodeTest.scala deleted file mode 100644 index af1c50acac..0000000000 --- a/test/junit/scala/issues/OptimizedBytecodeTest.scala +++ /dev/null @@ -1,367 +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 org.junit.Assert._ - -import scala.tools.nsc.backend.jvm.AsmUtils -import scala.tools.testing.BytecodeTesting._ -import scala.tools.partest.ASMConverters -import ASMConverters._ -import AsmUtils._ -import scala.tools.testing.{BytecodeTesting, ClearAfterClass} - -@RunWith(classOf[JUnit4]) -class OptimizedBytecodeTest extends BytecodeTesting { - override def compilerArgs = "-Yopt:l:classpath -Yopt-warnings" - import compiler._ - - @Test - def t2171(): Unit = { - val code = - """class C { - | final def m(msg: => String) = try 0 catch { case ex: Throwable => println(msg) } - | def t(): Unit = while (true) m("...") - |} - """.stripMargin - val c = compileClass(code) - assertSameCode(getMethod(c, "t"), List(Label(0), Jump(GOTO, Label(0)))) - } - - @Test - def t3430(): Unit = { - val code = - """class C { - | final def m(f: String => Boolean) = f("a") - | def t(): Boolean = - | m { s1 => - | m { s2 => - | while (true) { } - | true - | } - | } - |} - """.stripMargin - val c = compileClass(code) - - assertSameSummary(getMethod(c, "t"), List( - LDC, ASTORE, ALOAD /*0*/, ALOAD /*1*/, "C$$$anonfun$1", IRETURN)) - assertSameSummary(getMethod(c, "C$$$anonfun$1"), List(LDC, "C$$$anonfun$2", IRETURN)) - assertSameSummary(getMethod(c, "C$$$anonfun$2"), List(-1 /*A*/, GOTO /*A*/)) - } - - @Test - def t3252(): Unit = { - val code = - """class C { - | def t(x: Boolean): Thread = { - | g { - | x match { - | case false => Tat.h { } - | } - | } - | } - | - | private def g[T](block: => T) = ??? - |} - |object Tat { - | def h(block: => Unit): Nothing = ??? - |} - """.stripMargin - val List(c, t, tMod) = compileClasses(code, allowMessage = _.msg.contains("not be exhaustive")) - assertSameSummary(getMethod(c, "t"), List(GETSTATIC, "$qmark$qmark$qmark", ATHROW)) - } - - @Test - def t6157(): Unit = { - val code = - """class C { - | def t = println(ErrorHandler.defaultIfIOException("String")("String")) - |} - |object ErrorHandler { - | import java.io.IOException - | @inline - | def defaultIfIOException[T](default: => T)(closure: => T): T = try closure catch { - | case e: IOException => default - | } - |} - """.stripMargin - - val msg = - """ErrorHandler$::defaultIfIOException(Lscala/Function0;Lscala/Function0;)Ljava/lang/Object; is annotated @inline but could not be inlined: - |The operand stack at the callsite in C::t()V contains more values than the - |arguments expected by the callee ErrorHandler$::defaultIfIOException(Lscala/Function0;Lscala/Function0;)Ljava/lang/Object;. These values would be discarded - |when entering an exception handler declared in the inlined method.""".stripMargin - - compileClasses(code, allowMessage = _.msg == msg) - } - - @Test - def t6547(): Unit = { // "pos" test -- check that it compiles - val code = - """trait ConfigurableDefault[@specialized V] { - | def fillArray(arr: Array[V], v: V) = (arr: Any) match { - | case x: Array[Int] => null - | case x: Array[Long] => v.asInstanceOf[Long] - | } - |} - """.stripMargin - compileToBytes(code) - } - - @Test - def t8062(): Unit = { - val c1 = - """package warmup - |object Warmup { def filter[A](p: Any => Boolean): Any = filter[Any](p) } - """.stripMargin - val c2 = "class C { def t = warmup.Warmup.filter[Any](x => false) }" - val List(c, _, _) = compileClassesSeparately(List(c1, c2), extraArgs = compilerArgs) - assertInvoke(getMethod(c, "t"), "warmup/Warmup$", "filter") - } - - @Test - def t8306(): Unit = { // "pos" test - val code = - """class C { - | def foo: Int = 123 - | lazy val extension: Int = foo match { - | case idx if idx != -1 => 15 - | case _ => 17 - | } - |} - """.stripMargin - compileToBytes(code) - } - - @Test - def t8359(): Unit = { // "pos" test - // This is a minimization of code that crashed the compiler during bootstrapping - // in the first iteration of https://github.com/scala/scala/pull/4373, the PR - // that adjusted the order of free and declared params in LambdaLift. - - // Was: - // java.lang.AssertionError: assertion failed: - // Record Record(<$anon: Function1>,Map(value a$1 -> Deref(LocalVar(value b)))) does not contain a field value b$1 - // at scala.tools.nsc.Global.assert(Global.scala:262) - // at scala.tools.nsc.backend.icode.analysis.CopyPropagation$copyLattice$State.getFieldNonRecordValue(CopyPropagation.scala:113) - // at scala.tools.nsc.backend.icode.analysis.CopyPropagation$copyLattice$State.getFieldNonRecordValue(CopyPropagation.scala:122) - // at scala.tools.nsc.backend.opt.ClosureElimination$ClosureElim$$anonfun$analyzeMethod$1$$anonfun$apply$2.replaceFieldAccess$1(ClosureElimination.scala:124) - val code = - """package test - |class Typer { - | def bar(a: Boolean, b: Boolean): Unit = { - | @inline - | def baz(): Unit = { - | ((_: Any) => (Typer.this, a, b)).apply("") - | } - | ((_: Any) => baz()).apply("") - | } - |} - """.stripMargin - compileToBytes(code) - } - - @Test - def t9123(): Unit = { // "pos" test - val code = - """trait Setting { - | type T - | def value: T - |} - |object Test { - | def test(x: Some[Setting]) = x match { - | case Some(dep) => Some(dep.value) map (_ => true) - | } - |} - """.stripMargin - compileToBytes(code) - } - - @Test - def traitForceInfo(): Unit = { - // This did NOT crash unless it's in the interactive package. - // error: java.lang.AssertionError: assertion failed: trait Contexts.NoContext$ linkedModule: List() - // at scala.Predef$.assert(Predef.scala:160) - // at scala.tools.nsc.symtab.classfile.ClassfileParser$innerClasses$.innerSymbol$1(ClassfileParser.scala:1211) - // at scala.tools.nsc.symtab.classfile.ClassfileParser$innerClasses$.classSymbol(ClassfileParser.scala:1223) - // at scala.tools.nsc.symtab.classfile.ClassfileParser.classNameToSymbol(ClassfileParser.scala:489) - // at scala.tools.nsc.symtab.classfile.ClassfileParser.sig2type$1(ClassfileParser.scala:757) - // at scala.tools.nsc.symtab.classfile.ClassfileParser.sig2type$1(ClassfileParser.scala:789) - val code = - """package scala.tools.nsc - |package interactive - | - |trait MyContextTrees { - | val self: Global - | val NoContext = self.analyzer.NoContext - |} - """.stripMargin - compileClasses(code) - } - - @Test - def t9160(): Unit = { - val code = - """class C { - | def getInt: Int = 0 - | def t(trees: Object): Int = { - | trees match { - | case Some(elems) => - | case tree => getInt - | } - | 55 - | } - |} - """.stripMargin - val c = compileClass(code) - assertSameSummary(getMethod(c, "t"), List( - ALOAD /*1*/, INSTANCEOF /*Some*/, IFNE /*A*/, - ALOAD /*0*/, "getInt", POP, - -1 /*A*/, BIPUSH, IRETURN)) - } - - @Test - def t8796(): Unit = { - val code = - """final class C { - | def pr(): Unit = () - | def t(index: Int): Unit = index match { - | case 0 => pr() - | case 1 => pr() - | case _ => t(index - 2) - | } - |} - """.stripMargin - val c = compileClass(code) - assertSameSummary(getMethod(c, "t"), List( - -1 /*A*/, ILOAD /*1*/, TABLESWITCH, - -1, ALOAD, "pr", RETURN, - -1, ALOAD, "pr", RETURN, - -1, ILOAD, ICONST_2, ISUB, ISTORE, GOTO /*A*/)) - } - - @Test - def t8524(): Unit = { - val c1 = - """package library - |object Library { - | @inline def pleaseInlineMe() = 1 - | object Nested { @inline def pleaseInlineMe() = 2 } - |} - """.stripMargin - - val c2 = - """class C { - | def t = library.Library.pleaseInlineMe() + library.Library.Nested.pleaseInlineMe() - |} - """.stripMargin - - val cls = compileClassesSeparately(List(c1, c2), extraArgs = compilerArgs) - val c = findClass(cls, "C") - assertSameSummary(getMethod(c, "t"), List( - GETSTATIC, IFNONNULL, ACONST_NULL, ATHROW, // module load and null checks not yet eliminated - -1, ICONST_1, GETSTATIC, IFNONNULL, ACONST_NULL, ATHROW, - -1, ICONST_2, IADD, IRETURN)) - } - - @Test - def privateInline(): Unit = { - val code = - """final class C { - | private var x1 = false - | var x2 = false - | - | @inline private def wrapper1[T](body: => T): T = { - | val saved = x1 - | x1 = true - | try body - | finally x1 = saved - | } - | - | @inline private def wrapper2[T](body: => T): T = { - | val saved = x2 - | x2 = true - | try body - | finally x2 = saved - | } - | // inlined - | def f1a() = wrapper1(5) - | // not inlined: even after inlining `identity`, the Predef module is already on the stack for the - | // subsequent null check (the receiver of an inlined method, in this case Predef, is checked for - | // nullness, to ensure an NPE is thrown) - | def f1b() = identity(wrapper1(5)) - | - | def f2a() = wrapper2(5) // inlined - | def f2b() = identity(wrapper2(5)) // not inlined - |} - """.stripMargin - val c = compileClass(code, allowMessage = _.msg.contains("exception handler declared in the inlined method")) - assertInvoke(getMethod(c, "f1a"), "C", "C$$$anonfun$1") - assertInvoke(getMethod(c, "f1b"), "C", "wrapper1") - assertInvoke(getMethod(c, "f2a"), "C", "C$$$anonfun$3") - assertInvoke(getMethod(c, "f2b"), "C", "wrapper2") - } - - @Test - def t7060(): Unit = { - val code = - """class C { - | @inline final def mbarray_apply_minibox(array: Any, tag: Byte): Long = - | if (tag == 0) array.asInstanceOf[Array[Long]](0) - | else array.asInstanceOf[Array[Byte]](0).toLong - | - | def t = mbarray_apply_minibox(null, 0) - |} - """.stripMargin - val c = compileClass(code) - assertNoInvoke(getMethod(c, "t")) - } - - @Test - def t8315(): Unit = { - val code = - """class C { - | def t(as: Listt): Unit = { - | map(as, (_: Any) => return) - | } - | final def map(x: Listt, f: Any => Any): Any = { - | if (x eq Nill) "" else f("") - | } - |} - |object Nill extends Listt - |class Listt - """.stripMargin - val List(c, nil, nilMod, listt) = compileClasses(code) - assertInvoke(getMethod(c, "t"), "C", "C$$$anonfun$1") - } - - @Test - def t8315b(): Unit = { - val code = - """class C { - | def crash: Unit = { - | val key = "" - | try map(new F(key)) - | catch { case _: Throwable => } - | } - | final def map(f: F): Any = f.apply("") - |} - |final class F(key: String) { - | final def apply(a: Any): Any = throw new RuntimeException(key) - |} - """.stripMargin - val List(c, f) = compileClasses(code) - assertInvoke(getMethod(c, "crash"), "C", "map") - } - - @Test - def optimiseEnablesNewOpt(): Unit = { - val code = """class C { def t = (1 to 10) foreach println }""" - val List(c) = readAsmClasses(newCompiler(extraArgs = "-optimise -deprecation").compileToBytes(code, allowMessage = _.msg.contains("is deprecated"))) - assertInvoke(getMethod(c, "t"), "C", "C$$$anonfun$1") // range-foreach inlined from classpath - } -} diff --git a/test/junit/scala/issues/RunTest.scala b/test/junit/scala/issues/RunTest.scala deleted file mode 100644 index 0686d73d9b..0000000000 --- a/test/junit/scala/issues/RunTest.scala +++ /dev/null @@ -1,247 +0,0 @@ -package scala.issues - -import org.junit.Assert._ -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -import scala.tools.testing.RunTesting - -object RunTest { - class VC(val x: Any) extends AnyVal - class VCI(val x: Int) extends AnyVal { override def toString = "" + x } -} - -@RunWith(classOf[JUnit4]) -class RunTest extends RunTesting { - import runner._ - - @Test - def classOfValueClassAlias(): Unit = { - val code = - """import scala.issues.RunTest.VC - |type aVC = VC - |type aInt = Int - |type aInteger = Integer - |classOf[VC] == classOf[aVC] && - | classOf[aInt] == classOf[Int] && - | classOf[aInteger] == classOf[Integer] && - | classOf[aInt] != classOf[aInteger] - """.stripMargin - assertTrue(run[Boolean](code)) - } - - @Test - def classOfFinalVal(): Unit = { - val code = - """class C { - | final val a1 = classOf[Int] - | final val b1 = classOf[List[_]] - | final val c1 = classOf[List[String]] - | final val d1 = classOf[Array[Int]] - | final val e1 = classOf[Array[List[_]]] - | final val f1 = classOf[Array[_]] - | - | val a2 = classOf[Int] - | val b2 = classOf[List[_]] - | val c2 = classOf[List[String]] - | val d2 = classOf[Array[Int]] - | val e2 = classOf[Array[List[_]]] - | val f2 = classOf[Array[_]] - | - | val listC = Class.forName("scala.collection.immutable.List") - | - | val compare = List( - | (a1, a2, Integer.TYPE), - | (b1, b2, listC), - | (c1, c2, listC), - | (d1, d2, Array(1).getClass), - | (e1, e2, Array(List()).getClass), - | (f1, f2, new Object().getClass)) - |} - |(new C).compare - """.stripMargin - type K = Class[_] - val cs = run[List[(K, K, K)]](code) - for ((x, y, z) <- cs) { - assertEquals(x, y) - assertEquals(x, z) - } - } - - @Test - def t9702(): Unit = { - val code = - """import javax.annotation.Resource - |import scala.issues.RunTest.VC - |class C { - | type aList[K] = List[K] - | type aVC = VC - | type aInt = Int - | type aInteger = Integer - | @Resource(`type` = classOf[List[Int]]) def a = 0 - | @Resource(`type` = classOf[List[_]]) def b = 0 - | @Resource(`type` = classOf[aList[_]]) def c = 0 - | @Resource(`type` = classOf[Int]) def d = 0 - | @Resource(`type` = classOf[aInt]) def e = 0 - | @Resource(`type` = classOf[Integer]) def f = 0 - | @Resource(`type` = classOf[aInteger]) def g = 0 - | @Resource(`type` = classOf[VC]) def h = 0 - | @Resource(`type` = classOf[aVC]) def i = 0 - | @Resource(`type` = classOf[Array[Int]]) def j = 0 - | @Resource(`type` = classOf[Array[List[_]]]) def k = 0 - |} - |val c = classOf[C] - |def typeArg(meth: String) = c.getDeclaredMethod(meth).getDeclaredAnnotation(classOf[Resource]).`type` - |('a' to 'k').toList.map(_.toString).map(typeArg) - """.stripMargin - - val l = Class.forName("scala.collection.immutable.List") - val i = Integer.TYPE - val ig = new Integer(1).getClass - val v = new RunTest.VC(1).getClass - val ai = Array(1).getClass - val al = Array(List()).getClass - - // sanity checks - assertEquals(i, classOf[Int]) - assertNotEquals(i, ig) - - assertEquals(run[List[Class[_]]](code), - List(l, l, l, i, i, ig, ig, v, v, ai, al)) - } - - @Test - def annotationInfoNotErased(): Unit = { - val code = - """import javax.annotation.Resource - |import scala.annotation.meta.getter - |class C { - | type Rg = Resource @getter - | @(Resource @getter)(`type` = classOf[Int]) def a = 0 - | @Rg(`type` = classOf[Int]) def b = 0 - |} - |val c = classOf[C] - |def typeArg(meth: String) = c.getDeclaredMethod(meth).getDeclaredAnnotation(classOf[Resource]).`type` - |List("a", "b") map typeArg - |""".stripMargin - - val i = Integer.TYPE - assertEquals(run[List[Class[_]]](code), List(i, i)) - } - - @Test - def invocationReceivers(): Unit = { - import invocationReceiversTestCode._ - assertEquals(run[String](definitions("Object") + runCode), "hi" * 9) - assertEquals(run[String](definitions("String") + runCode), "hi" * 9) // bridge method for clone generated - } - - @Test - def classOfUnitConstant(): Unit = { - val code = - """abstract class A { def f: Class[_] } - |class C extends A { final val f = classOf[Unit] } - |val c = new C - |(c.f, (c: A).f) - """.stripMargin - val u = Void.TYPE - assertEquals(run[(Class[_], Class[_])](code), (u, u)) - } - - @Test - def t9671(): Unit = { - val code = - """import scala.issues.RunTest.VCI - | - |def f1(a: Any) = "" + a - |def f2(a: AnyVal) = "" + a - |def f3[T](a: T) = "" + a - |def f4(a: Int) = "" + a - |def f5(a: VCI) = "" + a - |def f6(u: Unit) = "" + u - | - |def n1: AnyRef = null - |def n2: Null = null - |def n3: Any = null - |def n4[T]: T = null.asInstanceOf[T] - | - |def npe(s: => String) = try { s; throw new Error() } catch { case _: NullPointerException => "npe" } - | - | f1(null.asInstanceOf[Int]) + - | f1( n1.asInstanceOf[Int]) + - | f1( n2.asInstanceOf[Int]) + - | f1( n3.asInstanceOf[Int]) + - | f1( n4[Int]) + // "null" - |"-" + - | f1(null.asInstanceOf[VCI]) + - |npe(f1( n1.asInstanceOf[VCI])) + // SI-8097 - | f1( n2.asInstanceOf[VCI]) + - |npe(f1( n3.asInstanceOf[VCI])) + // SI-8097 - | f1( n4[VCI]) + // "null" - |"-" + - | f1(null.asInstanceOf[Unit]) + - | f1( n1.asInstanceOf[Unit]) + - | f1( n2.asInstanceOf[Unit]) + - | f1( n3.asInstanceOf[Unit]) + - | f1( n4[Unit]) + // "null" - |"-" + - | f2(null.asInstanceOf[Int]) + - | f2( n1.asInstanceOf[Int]) + - | f2( n2.asInstanceOf[Int]) + - | f2( n3.asInstanceOf[Int]) + - | f2( n4[Int]) + // "null" - |"-" + - | f2(null.asInstanceOf[VCI]) + - |npe(f2( n1.asInstanceOf[VCI])) + // SI-8097 - | f2( n2.asInstanceOf[VCI]) + - |npe(f2( n3.asInstanceOf[VCI])) + // SI-8097 - | f2( n4[VCI]) + // "null" - |"-" + - | f2(null.asInstanceOf[Unit]) + - | f2( n1.asInstanceOf[Unit]) + - | f2( n2.asInstanceOf[Unit]) + - | f2( n3.asInstanceOf[Unit]) + - | f2( n4[Unit]) + // "null" - |"-" + - | f3(null.asInstanceOf[Int]) + - | f3( n1.asInstanceOf[Int]) + - | f3( n2.asInstanceOf[Int]) + - | f3( n3.asInstanceOf[Int]) + - | f3( n4[Int]) + // "null" - |"-" + - | f3(null.asInstanceOf[VCI]) + - |npe(f3( n1.asInstanceOf[VCI])) + // SI-8097 - | f3( n2.asInstanceOf[VCI]) + - |npe(f3( n3.asInstanceOf[VCI])) + // SI-8097 - | f3( n4[VCI]) + // "null" - |"-" + - | f3(null.asInstanceOf[Unit]) + - | f3( n1.asInstanceOf[Unit]) + - | f3( n2.asInstanceOf[Unit]) + - | f3( n3.asInstanceOf[Unit]) + - | f3( n4[Unit]) + // "null" - |"-" + - | f4(null.asInstanceOf[Int]) + - | f4( n1.asInstanceOf[Int]) + - | f4( n2.asInstanceOf[Int]) + - | f4( n3.asInstanceOf[Int]) + - | f4( n4[Int]) + - |"-" + - | f5(null.asInstanceOf[VCI]) + - |npe(f5( n1.asInstanceOf[VCI])) + // SI-8097 - | f5( n2.asInstanceOf[VCI]) + - |npe(f5( n3.asInstanceOf[VCI])) + // SI-8097 - |npe(f5( n4[VCI])) + // SI-8097 - |"-" + - | f6(null.asInstanceOf[Unit]) + - | f6( n1.asInstanceOf[Unit]) + - | f6( n2.asInstanceOf[Unit]) + - | f6( n3.asInstanceOf[Unit]) + - | f6( n4[Unit]) // "null" - """.stripMargin - - assertEquals(run[String](code), - "0000null-0npe0npenull-()()()()null-0000null-0npe0npenull-()()()()null-0000null-0npe0npenull-()()()()null-00000-0npe0npenpe-()()()()null") - } -} diff --git a/test/junit/scala/lang/annotations/BytecodeTest.scala b/test/junit/scala/lang/annotations/BytecodeTest.scala new file mode 100644 index 0000000000..09fc1d3572 --- /dev/null +++ b/test/junit/scala/lang/annotations/BytecodeTest.scala @@ -0,0 +1,80 @@ +package scala.lang.annotations + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.collection.JavaConverters._ +import scala.tools.nsc.backend.jvm.AsmUtils +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 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 c = compileClass(code) + + assertTrue(getInstructions(c, "f").count(_.isInstanceOf[TableSwitch]) == 1) + assertTrue(getInstructions(c, "g").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 global.Run() + run.compileSources(List(new BatchSourceFile("AnnotA.java", annotA), new BatchSourceFile("AnnotB.java", annotB), new BatchSourceFile("Test.scala", scalaSrc))) + val outDir = global.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") + } +} \ No newline at end of file diff --git a/test/junit/scala/lang/annotations/RunTest.scala b/test/junit/scala/lang/annotations/RunTest.scala new file mode 100644 index 0000000000..0d9c0c4713 --- /dev/null +++ b/test/junit/scala/lang/annotations/RunTest.scala @@ -0,0 +1,32 @@ +package scala.lang.annotations + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testing.RunTesting + +@RunWith(classOf[JUnit4]) +class RunTest extends RunTesting { + import runner._ + + @Test + def annotationInfoNotErased(): Unit = { + val code = + """import javax.annotation.Resource + |import scala.annotation.meta.getter + |class C { + | type Rg = Resource @getter + | @(Resource @getter)(`type` = classOf[Int]) def a = 0 + | @Rg(`type` = classOf[Int]) def b = 0 + |} + |val c = classOf[C] + |def typeArg(meth: String) = c.getDeclaredMethod(meth).getDeclaredAnnotation(classOf[Resource]).`type` + |List("a", "b") map typeArg + |""".stripMargin + + val i = Integer.TYPE + assertEquals(run[List[Class[_]]](code), List(i, i)) + } +} diff --git a/test/junit/scala/lang/primitives/BoxUnboxTest.scala b/test/junit/scala/lang/primitives/BoxUnboxTest.scala new file mode 100644 index 0000000000..23c9326989 --- /dev/null +++ b/test/junit/scala/lang/primitives/BoxUnboxTest.scala @@ -0,0 +1,222 @@ +package scala.lang.primitives + +import org.junit.Test +import org.junit.Assert._ +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testing.AssertUtil._ +import scala.tools.testing.RunTesting + +object BoxUnboxTest { + class VCI(val x: Int) extends AnyVal { override def toString = "" + x } +} + +@RunWith(classOf[JUnit4]) +class BoxUnboxTest extends RunTesting { + import runner._ + + def genericNull[T] = null.asInstanceOf[T] // allowed, see SI-4437, point 2 + + @Test + def boxUnboxInt(): Unit = { + val b = new Integer(1) + val u = 1 + + assertEquals(1.toInt, u) + + assertEquals(Predef.int2Integer(1), b) + assertEquals(1: Integer, b) + assertEquals(Int.box(1), b) + assertEquals(1.asInstanceOf[Object], b) + + assertThrows[ClassCastException]("".asInstanceOf[Integer]) + + assertEquals(Predef.Integer2int(b), u) + assertEquals(b: Int, u) + assertEquals(Int.unbox(b), u) + assertEquals(b.asInstanceOf[Int], u) + assertEquals(b.intValue, u) + assertEquals(b.toInt, u) + intWrapper(b).toInt + + assertThrows[ClassCastException](Int.unbox("")) + assertThrows[ClassCastException]("".asInstanceOf[Int]) + + // null unboxing in various positions + + val n1 = Int.unbox(null) + assertEquals(n1, 0) + val n2 = Predef.Integer2int(null) + assertEquals(n2, 0) + val n3 = (null: Integer): Int + assertEquals(n3, 0) + val n4 = null.asInstanceOf[Int] + assertEquals(n4, 0) + val n5 = null.asInstanceOf[Int] == 0 + assertTrue(n5) + val n6 = null.asInstanceOf[Int] == null + assertFalse(n6) + val n7 = null.asInstanceOf[Int] != 0 + assertFalse(n7) + val n8 = null.asInstanceOf[Int] != null + assertTrue(n8) + + val mp = new java.util.HashMap[Int, Int] + val n9 = mp.get(0) + assertEquals(n9, 0) + val n10 = mp.get(0) == null // SI-602 + assertThrows[AssertionError](assertFalse(n10)) // should not throw + + def f(a: Any) = "" + a + val n11 = f(null.asInstanceOf[Int]) + assertEquals(n11, "0") + + def n12 = genericNull[Int] + assertEquals(n12, 0) + } + + @Test + def numericConversions(): Unit = { + val i1 = 1L.asInstanceOf[Int] + assertEquals(i1, 1) + assertThrows[ClassCastException] { + val i2 = (1L: Any).asInstanceOf[Int] // SI-1448, should not throw. see also SI-4437 point 1. + assertEquals(i2, 1) + } + } + + @Test + def boxUnboxBoolean(): Unit = { + val n1 = Option(null.asInstanceOf[Boolean]) + assertEquals(n1, Some(false)) + } + + @Test + def boxUnboxUnit(): Unit = { + // should not use assertEquals in this test: it takes two Object parameters. normally, Unit does + // not conform to Object, but for Java-defined methods scalac makes an exception and treats them + // as Any. passing a Unit as Any makes the compiler go through another layer of boxing, so it + // can hide some bugs (where we actually have a null, but the compiler makes it a ()). + + var v = 0 + def eff() = { v = 1 } + def chk() = { assert(v == 1); v = 0 } + + val b = runtime.BoxedUnit.UNIT + + assert(eff() == b); chk() + assert(Unit.box(eff()) == b); chk() + assert(().asInstanceOf[Object] == b) + + Unit.unbox({eff(); b}); chk() + Unit.unbox({eff(); null}); chk() + assertThrows[ClassCastException](Unit.unbox({eff(); ""})); chk() + + val n1 = null.asInstanceOf[Unit] + assert(n1 == b) + + val n2 = null.asInstanceOf[Unit] == b + assert(n2) + + def f(a: Any) = "" + a + val n3 = f(null.asInstanceOf[Unit]) + assertEquals(n3, "()") + } + + @Test + def t9671(): Unit = { + val code = + """import scala.lang.primitives.BoxUnboxTest.VCI + | + |def f1(a: Any) = "" + a + |def f2(a: AnyVal) = "" + a + |def f3[T](a: T) = "" + a + |def f4(a: Int) = "" + a + |def f5(a: VCI) = "" + a + |def f6(u: Unit) = "" + u + | + |def n1: AnyRef = null + |def n2: Null = null + |def n3: Any = null + |def n4[T]: T = null.asInstanceOf[T] + | + |def npe(s: => String) = try { s; throw new Error() } catch { case _: NullPointerException => "npe" } + | + | f1(null.asInstanceOf[Int]) + + | f1( n1.asInstanceOf[Int]) + + | f1( n2.asInstanceOf[Int]) + + | f1( n3.asInstanceOf[Int]) + + | f1( n4[Int]) + // "null" + |"-" + + | f1(null.asInstanceOf[VCI]) + + |npe(f1( n1.asInstanceOf[VCI])) + // SI-8097 + | f1( n2.asInstanceOf[VCI]) + + |npe(f1( n3.asInstanceOf[VCI])) + // SI-8097 + | f1( n4[VCI]) + // "null" + |"-" + + | f1(null.asInstanceOf[Unit]) + + | f1( n1.asInstanceOf[Unit]) + + | f1( n2.asInstanceOf[Unit]) + + | f1( n3.asInstanceOf[Unit]) + + | f1( n4[Unit]) + // "null" + |"-" + + | f2(null.asInstanceOf[Int]) + + | f2( n1.asInstanceOf[Int]) + + | f2( n2.asInstanceOf[Int]) + + | f2( n3.asInstanceOf[Int]) + + | f2( n4[Int]) + // "null" + |"-" + + | f2(null.asInstanceOf[VCI]) + + |npe(f2( n1.asInstanceOf[VCI])) + // SI-8097 + | f2( n2.asInstanceOf[VCI]) + + |npe(f2( n3.asInstanceOf[VCI])) + // SI-8097 + | f2( n4[VCI]) + // "null" + |"-" + + | f2(null.asInstanceOf[Unit]) + + | f2( n1.asInstanceOf[Unit]) + + | f2( n2.asInstanceOf[Unit]) + + | f2( n3.asInstanceOf[Unit]) + + | f2( n4[Unit]) + // "null" + |"-" + + | f3(null.asInstanceOf[Int]) + + | f3( n1.asInstanceOf[Int]) + + | f3( n2.asInstanceOf[Int]) + + | f3( n3.asInstanceOf[Int]) + + | f3( n4[Int]) + // "null" + |"-" + + | f3(null.asInstanceOf[VCI]) + + |npe(f3( n1.asInstanceOf[VCI])) + // SI-8097 + | f3( n2.asInstanceOf[VCI]) + + |npe(f3( n3.asInstanceOf[VCI])) + // SI-8097 + | f3( n4[VCI]) + // "null" + |"-" + + | f3(null.asInstanceOf[Unit]) + + | f3( n1.asInstanceOf[Unit]) + + | f3( n2.asInstanceOf[Unit]) + + | f3( n3.asInstanceOf[Unit]) + + | f3( n4[Unit]) + // "null" + |"-" + + | f4(null.asInstanceOf[Int]) + + | f4( n1.asInstanceOf[Int]) + + | f4( n2.asInstanceOf[Int]) + + | f4( n3.asInstanceOf[Int]) + + | f4( n4[Int]) + + |"-" + + | f5(null.asInstanceOf[VCI]) + + |npe(f5( n1.asInstanceOf[VCI])) + // SI-8097 + | f5( n2.asInstanceOf[VCI]) + + |npe(f5( n3.asInstanceOf[VCI])) + // SI-8097 + |npe(f5( n4[VCI])) + // SI-8097 + |"-" + + | f6(null.asInstanceOf[Unit]) + + | f6( n1.asInstanceOf[Unit]) + + | f6( n2.asInstanceOf[Unit]) + + | f6( n3.asInstanceOf[Unit]) + + | f6( n4[Unit]) // "null" + """.stripMargin + + assertEquals(run[String](code), + "0000null-0npe0npenull-()()()()null-0000null-0npe0npenull-()()()()null-0000null-0npe0npenull-()()()()null-00000-0npe0npenpe-()()()()null") + } +} diff --git a/test/junit/scala/lang/primitives/PredefAutoboxingTest.scala b/test/junit/scala/lang/primitives/PredefAutoboxingTest.scala new file mode 100644 index 0000000000..ab31a9e8f1 --- /dev/null +++ b/test/junit/scala/lang/primitives/PredefAutoboxingTest.scala @@ -0,0 +1,33 @@ +package scala.lang.primitives + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(classOf[JUnit4]) +class PredefAutoboxingTest { + @Test def unboxNullByte() = + assertEquals(Predef.Byte2byte(null), 0.toByte) + + @Test def unboxNullShort() = + assertEquals(Predef.Short2short(null), 0.toShort) + + @Test def unboxNullCharacter() = + assertEquals(Predef.Character2char(null), 0.toChar) + + @Test def unboxNullInteger() = + assertEquals(Predef.Integer2int(null), 0) + + @Test def unboxNullLong() = + assertEquals(Predef.Long2long(null), 0L) + + @Test def unboxNullFloat() = + assertEquals(Predef.Float2float(null), 0F, 0F) + + @Test def unboxNullDouble() = + assertEquals(Predef.Double2double(null), 0D, 0D) + + @Test def unboxNullBoolean() = + assertEquals(Predef.Boolean2boolean(null), false) +} diff --git a/test/junit/scala/lang/stringinterpol/StringContextTest.scala b/test/junit/scala/lang/stringinterpol/StringContextTest.scala new file mode 100644 index 0000000000..d2cb8149d7 --- /dev/null +++ b/test/junit/scala/lang/stringinterpol/StringContextTest.scala @@ -0,0 +1,265 @@ + +package scala.lang.stringinterpol + +import java.text.DecimalFormat + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.language.implicitConversions +import scala.tools.testing.AssertUtil._ + +object StringContextTestUtils { + private val decimalSeparator: Char = new DecimalFormat().getDecimalFormatSymbols().getDecimalSeparator() + private val numberPattern = """(\d+)\.(\d+.*)""".r + + implicit class StringContextOps(val sc: StringContext) extends AnyVal { + // Use this String interpolator to avoid problems with a locale-dependent decimal mark. + def locally(numbers: String*): String = { + val numbersWithCorrectLocale = numbers.map(applyProperLocale) + sc.s(numbersWithCorrectLocale: _*) + } + + // Handles cases like locally"3.14" - it's prettier than locally"${"3.14"}". + def locally(): String = sc.parts.map(applyProperLocale).mkString + + private def applyProperLocale(number: String): String = { + val numberPattern(intPart, fractionalPartAndSuffix) = number + s"$intPart$decimalSeparator$fractionalPartAndSuffix" + } + } +} + +@RunWith(classOf[JUnit4]) +class StringContextTest { + + import StringContext._ + import StringContextTestUtils.StringContextOps + + @Test def noEscape() = { + val s = "string" + val res = processEscapes(s) + assertEquals(s, res) + } + @Test def tabbed() = { + val s = """a\tb""" + val res = processEscapes(s) + assertEquals("a\tb", res) + } + @Test def quoted() = { + val s = """hello, \"world\"""" + val res = processEscapes(s) + assertEquals("""hello, "world"""", res) + } + @Test def octal() = { + val s = """\123cala""" + val res = treatEscapes(s) + assertEquals("Scala", res) + } + @Test def doubled() = { + val s = """\123cala\123yntax""" + val res = treatEscapes(s) + assertEquals("ScalaSyntax", res) + } + @Test def badly() = assertThrows[InvalidEscapeException] { + val s = """Scala\""" + val res = treatEscapes(s) + assertEquals("Scala", res) + } + @Test def noOctal() = assertThrows[InvalidEscapeException] { + val s = """\123cala""" + val res = processEscapes(s) + assertEquals("Scala", res) + } + + @Test def t6631_baseline() = assertEquals("\f\r\n\t", s"""\f\r\n\t""") + + @Test def t6631_badEscape() = assertThrows[InvalidEscapeException] { + s"""\x""" + } + + // verifying that the standard interpolators can be supplanted + @Test def antiHijack_?() = { + object AllYourStringsAreBelongToMe { case class StringContext(args: Any*) { def s(args: Any) = "!!!!" } } + import AllYourStringsAreBelongToMe._ + //assertEquals("????", s"????") + assertEquals("!!!!", s"????") // OK to hijack core interpolator ids + } + + @Test def fIf() = { + val res = f"${if (true) 2.5 else 2.5}%.2f" + val expected = locally"2.50" + assertEquals(expected, res) + } + + @Test def fIfNot() = { + val res = f"${if (false) 2.5 else 3.5}%.2f" + val expected = locally"3.50" + assertEquals(expected, res) + } + + @Test def fHeteroArgs() = { + val res = f"${3.14}%.2f rounds to ${3}%d" + val expected = locally"${"3.14"} rounds to 3" + assertEquals(expected, res) + } + + @Test def `f interpolator baseline`(): Unit = { + + implicit def stringToBoolean(s: String): Boolean = java.lang.Boolean.parseBoolean(s) + implicit def stringToChar(s: String): Char = s(0) + implicit def str2fmt(s: String): java.util.Formattable = new java.util.Formattable { + def formatTo(f: java.util.Formatter, g: Int, w: Int, p: Int) = f.format("%s", s) + } + + val b_true = true + val b_false = false + + val i = 42 + + val f_zero = 0.0 + val f_zero_- = -0.0 + + val s = "Scala" + + val fff = new java.util.Formattable { + def formatTo(f: java.util.Formatter, g: Int, w: Int, p: Int) = f.format("4") + } + import java.util.{Calendar, Locale} + val c = Calendar.getInstance(Locale.US) + c.set(2012, Calendar.MAY, 26) + implicit def strToDate(x: String): Calendar = c + + val ss = List[(String, String)] ( + // 'b' / 'B' (category: general) + // ----------------------------- + f"${b_false}%b" -> "false", + f"${b_true}%b" -> "true", + + f"${null}%b" -> "false", + f"${false}%b" -> "false", + f"${true}%b" -> "true", + f"${true && false}%b" -> "false", + f"${new java.lang.Boolean(false)}%b" -> "false", + f"${new java.lang.Boolean(true)}%b" -> "true", + + f"${null}%B" -> "FALSE", + f"${false}%B" -> "FALSE", + f"${true}%B" -> "TRUE", + f"${new java.lang.Boolean(false)}%B" -> "FALSE", + f"${new java.lang.Boolean(true)}%B" -> "TRUE", + + f"${"true"}%b" -> "true", + f"${"false"}%b"-> "false", + + // 'h' | 'H' (category: general) + // ----------------------------- + f"${null}%h" -> "null", + f"${f_zero}%h" -> "0", + f"${f_zero_-}%h" -> "80000000", + f"${s}%h" -> "4c01926", + + f"${null}%H" -> "NULL", + f"${s}%H" -> "4C01926", + + // 's' | 'S' (category: general) + // ----------------------------- + f"${null}%s" -> "null", + f"${null}%S" -> "NULL", + f"${s}%s" -> "Scala", + f"${s}%S" -> "SCALA", + f"${5}" -> "5", + f"${i}" -> "42", + f"${'foo}" -> "'foo", + + f"${Thread.State.NEW}" -> "NEW", + + // 'c' | 'C' (category: character) + // ------------------------------- + f"${120:Char}%c" -> "x", + f"${120:Byte}%c" -> "x", + f"${120:Short}%c" -> "x", + f"${120:Int}%c" -> "x", + f"${new java.lang.Character('x')}%c" -> "x", + f"${new java.lang.Byte(120:Byte)}%c" -> "x", + f"${new java.lang.Short(120:Short)}%c" -> "x", + f"${new java.lang.Integer(120)}%c" -> "x", + + f"${'x' : java.lang.Character}%c" -> "x", + f"${(120:Byte) : java.lang.Byte}%c" -> "x", + f"${(120:Short) : java.lang.Short}%c" -> "x", + f"${120 : java.lang.Integer}%c" -> "x", + + f"${"Scala"}%c" -> "S", + + // 'd' | 'o' | 'x' | 'X' (category: integral) + // ------------------------------------------ + f"${120:Byte}%d" -> "120", + f"${120:Short}%d" -> "120", + f"${120:Int}%d" -> "120", + f"${120:Long}%d" -> "120", + f"${60 * 2}%d" -> "120", + f"${new java.lang.Byte(120:Byte)}%d" -> "120", + f"${new java.lang.Short(120:Short)}%d" -> "120", + f"${new java.lang.Integer(120)}%d" -> "120", + f"${new java.lang.Long(120)}%d" -> "120", + f"${120 : java.lang.Integer}%d" -> "120", + f"${120 : java.lang.Long}%d" -> "120", + f"${BigInt(120)}%d" -> "120", + + f"${new java.math.BigInteger("120")}%d" -> "120", + + f"${4}%#10X" -> " 0X4", + + f"She is ${fff}%#s feet tall." -> "She is 4 feet tall.", + + f"Just want to say ${"hello, world"}%#s..." -> "Just want to say hello, world...", + + { implicit val strToShort = (s: String) => java.lang.Short.parseShort(s) ; f"${"120"}%d" } -> "120", + { implicit val strToInt = (s: String) => 42 ; f"${"120"}%d" } -> "42", + + // 'e' | 'E' | 'g' | 'G' | 'f' | 'a' | 'A' (category: floating point) + // ------------------------------------------------------------------ + f"${3.4f}%e" -> locally"3.400000e+00", + f"${3.4}%e" -> locally"3.400000e+00", + f"${3.4f : java.lang.Float}%e" -> locally"3.400000e+00", + f"${3.4 : java.lang.Double}%e" -> locally"3.400000e+00", + + f"${BigDecimal(3.4)}%e" -> locally"3.400000e+00", + + f"${new java.math.BigDecimal(3.4)}%e" -> locally"3.400000e+00", + + f"${3}%e" -> locally"3.000000e+00", + f"${3L}%e" -> locally"3.000000e+00", + + // 't' | 'T' (category: date/time) + // ------------------------------- + f"${c}%TD" -> "05/26/12", + f"${c.getTime}%TD" -> "05/26/12", + f"${c.getTime.getTime}%TD" -> "05/26/12", + f"""${"1234"}%TD""" -> "05/26/12", + + // literals and arg indexes + f"%%" -> "%", + f" mind%n------%nmatter" -> + """| mind + |------ + |matter""".stripMargin.lines.mkString(compat.Platform.EOL), + f"${i}%d % "42 42 9", + f"${7}%d % "7 7 9", + f"${7}%d %2$$d ${9}%d" -> "7 9 9", + + f"${null}%d % "null FALSE", + + f"${5: Any}" -> "5", + f"${5}%s% "55", + f"${3.14}%s,% locally"3.14,${"3.140000"}", + + f"z" -> "z" + ) + + for ((f, s) <- ss) assertEquals(s, f) + } +} diff --git a/test/junit/scala/lang/traits/BytecodeTest.scala b/test/junit/scala/lang/traits/BytecodeTest.scala new file mode 100644 index 0000000000..f47fc9c127 --- /dev/null +++ b/test/junit/scala/lang/traits/BytecodeTest.scala @@ -0,0 +1,282 @@ +package scala.lang.traits + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.collection.JavaConverters._ +import scala.tools.asm.Opcodes +import scala.tools.asm.Opcodes._ +import scala.tools.asm.tree.ClassNode +import scala.tools.partest.ASMConverters._ +import scala.tools.testing.BytecodeTesting +import scala.tools.testing.BytecodeTesting._ + +@RunWith(classOf[JUnit4]) +class BytecodeTest extends BytecodeTesting { + import compiler._ + + def checkForwarder(classes: Map[String, ClassNode], clsName: Symbol, target: String) = { + val List(f) = getMethods(classes(clsName.name), "f") + assertSameCode(f, List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, target, "f", "()I", false), Op(IRETURN))) + } + + @Test + def traitMethodForwarders(): Unit = { + 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 + + val c = compileClasses(code).map(c => (c.name, c)).toMap + + val noForwarder = List('C1, 'C2, 'C3, 'C4, 'C10, 'C11, 'C12, 'C13, 'C16, 'C17) + for (cn <- noForwarder) assertEquals(getMethods(c(cn.name), "f"), Nil) + + checkForwarder(c, 'C5, "T3") + checkForwarder(c, 'C6, "T4") + checkForwarder(c, 'C7, "T5") + checkForwarder(c, 'C8, "T4") + checkForwarder(c, 'C9, "T5") + checkForwarder(c, 'C14, "T4") + checkForwarder(c, 'C15, "T5") + assertSameSummary(getMethod(c("C18"), "f"), List(BIPUSH, IRETURN)) + checkForwarder(c, 'C19, "T7") + assertSameCode(getMethod(c("C19"), "T7$$super$f"), List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, "C18", "f", "()I", false), Op(IRETURN))) + assertInvoke(getMethod(c("C20"), "clone"), "T8", "clone") // mixin forwarder + } + + @Test + def noTraitMethodForwardersForOverloads(): Unit = { + 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(code) + assertEquals(getMethods(c, "f"), Nil) + } + + @Test + def traitMethodForwardersForJavaDefaultMethods(): Unit = { + 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 + val c = compileClasses(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 (cn <- noForwarder) assertEquals(getMethods(c(cn.name), "f"), Nil) + + checkForwarder(c, 'K12, "T2") + } + + @Test + def invocationReceivers(): Unit = { + val List(c1, c2, t, u) = compileClasses(invocationReceiversTestCode.definitions("Object")) + // mixin forwarder in C1 + assertSameCode(getMethod(c1, "clone"), List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, "T", "clone", "()Ljava/lang/Object;", false), Op(ARETURN))) + assertInvoke(getMethod(c1, "f1"), "T", "clone") + assertInvoke(getMethod(c1, "f2"), "T", "clone") + assertInvoke(getMethod(c1, "f3"), "C1", "clone") + assertInvoke(getMethod(c2, "f1"), "T", "clone") + assertInvoke(getMethod(c2, "f2"), "T", "clone") + assertInvoke(getMethod(c2, "f3"), "C1", "clone") + + val List(c1b, c2b, tb, ub) = compileClasses(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) = getInstructions(c1b, "f1").collect({case i: Invoke => i}) + assertSameCode(iv(getMethod(c1b, "f1")), List(Invoke(INVOKEINTERFACE, "T", "clone", "()Ljava/lang/String;", true))) + assertSameCode(iv(getMethod(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(getMethod(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 c = compileClass(cC, javaCode = List((aC, "A.java"), (bC, "B.java"), (iC, "I.java"), (jC, "J.java"))) + assertInvoke(getMethod(c, "f1"), "a/B", "f") // receiver needs to be B (A is not accessible in class C, package b) + assertInvoke(getMethod(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 c = compileClass(code) + assertInvoke(getMethod(c, "f1"), "[Ljava/lang/String;", "clone") // array descriptor as receiver + assertInvoke(getMethod(c, "f2"), "java/lang/Object", "hashCode") // object receiver + assertInvoke(getMethod(c, "f3"), "java/lang/Object", "hashCode") + assertInvoke(getMethod(c, "f4"), "java/lang/Object", "toString") + } + + @Test + def superConstructorArgumentInSpecializedClass(): Unit = { + // see comment in SpecializeTypes.forwardCtorCall + val code = "case class C[@specialized(Int) T](_1: T)" + val List(c, cMod, cSpec) = compileClasses(code) + assertSameSummary(getMethod(cSpec, ""), + // pass `null` to super constructor, no box-unbox, no Integer created + List(ALOAD, ILOAD, PUTFIELD, ALOAD, ACONST_NULL, "", RETURN)) + } + +} + +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 +} diff --git a/test/junit/scala/lang/traits/RunTest.scala b/test/junit/scala/lang/traits/RunTest.scala new file mode 100644 index 0000000000..d27dc15e20 --- /dev/null +++ b/test/junit/scala/lang/traits/RunTest.scala @@ -0,0 +1,20 @@ +package scala.lang.traits + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testing.RunTesting + +@RunWith(classOf[JUnit4]) +class RunTest extends RunTesting { + import runner._ + + @Test + def invocationReceivers(): Unit = { + import invocationReceiversTestCode._ + assertEquals(run[String](definitions("Object") + runCode), "hi" * 9) + assertEquals(run[String](definitions("String") + runCode), "hi" * 9) // bridge method for clone generated + } +} diff --git a/test/junit/scala/reflect/ClassOfTest.scala b/test/junit/scala/reflect/ClassOfTest.scala new file mode 100644 index 0000000000..520b14ccd4 --- /dev/null +++ b/test/junit/scala/reflect/ClassOfTest.scala @@ -0,0 +1,124 @@ +package scala.reflect + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testing.RunTesting + +object ClassOfTest { + class VC(val x: Any) extends AnyVal +} + +@RunWith(classOf[JUnit4]) +class ClassOfTest extends RunTesting { + import runner._ + + @Test + def classOfValueClassAlias(): Unit = { + val code = + """import scala.reflect.ClassOfTest.VC + |type aVC = VC + |type aInt = Int + |type aInteger = Integer + |classOf[VC] == classOf[aVC] && + | classOf[aInt] == classOf[Int] && + | classOf[aInteger] == classOf[Integer] && + | classOf[aInt] != classOf[aInteger] + """.stripMargin + assertTrue(run[Boolean](code)) + } + + @Test + def classOfFinalVal(): Unit = { + val code = + """class C { + | final val a1 = classOf[Int] + | final val b1 = classOf[List[_]] + | final val c1 = classOf[List[String]] + | final val d1 = classOf[Array[Int]] + | final val e1 = classOf[Array[List[_]]] + | final val f1 = classOf[Array[_]] + | + | val a2 = classOf[Int] + | val b2 = classOf[List[_]] + | val c2 = classOf[List[String]] + | val d2 = classOf[Array[Int]] + | val e2 = classOf[Array[List[_]]] + | val f2 = classOf[Array[_]] + | + | val listC = Class.forName("scala.collection.immutable.List") + | + | val compare = List( + | (a1, a2, Integer.TYPE), + | (b1, b2, listC), + | (c1, c2, listC), + | (d1, d2, Array(1).getClass), + | (e1, e2, Array(List()).getClass), + | (f1, f2, new Object().getClass)) + |} + |(new C).compare + """.stripMargin + type K = Class[_] + val cs = run[List[(K, K, K)]](code) + for ((x, y, z) <- cs) { + assertEquals(x, y) + assertEquals(x, z) + } + } + + @Test + def t9702(): Unit = { + val code = + """import javax.annotation.Resource + |import scala.reflect.ClassOfTest.VC + |class C { + | type aList[K] = List[K] + | type aVC = VC + | type aInt = Int + | type aInteger = Integer + | @Resource(`type` = classOf[List[Int]]) def a = 0 + | @Resource(`type` = classOf[List[_]]) def b = 0 + | @Resource(`type` = classOf[aList[_]]) def c = 0 + | @Resource(`type` = classOf[Int]) def d = 0 + | @Resource(`type` = classOf[aInt]) def e = 0 + | @Resource(`type` = classOf[Integer]) def f = 0 + | @Resource(`type` = classOf[aInteger]) def g = 0 + | @Resource(`type` = classOf[VC]) def h = 0 + | @Resource(`type` = classOf[aVC]) def i = 0 + | @Resource(`type` = classOf[Array[Int]]) def j = 0 + | @Resource(`type` = classOf[Array[List[_]]]) def k = 0 + |} + |val c = classOf[C] + |def typeArg(meth: String) = c.getDeclaredMethod(meth).getDeclaredAnnotation(classOf[Resource]).`type` + |('a' to 'k').toList.map(_.toString).map(typeArg) + """.stripMargin + + val l = Class.forName("scala.collection.immutable.List") + val i = Integer.TYPE + val ig = new Integer(1).getClass + val v = new ClassOfTest.VC(1).getClass + val ai = Array(1).getClass + val al = Array(List()).getClass + + // sanity checks + assertEquals(i, classOf[Int]) + assertNotEquals(i, ig) + + assertEquals(run[List[Class[_]]](code), + List(l, l, l, i, i, ig, ig, v, v, ai, al)) + } + + @Test + def classOfUnitConstant(): Unit = { + val code = + """abstract class A { def f: Class[_] } + |class C extends A { final val f = classOf[Unit] } + |val c = new C + |(c.f, (c: A).f) + """.stripMargin + val u = Void.TYPE + assertEquals(run[(Class[_], Class[_])](code), (u, u)) + } +} 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..7954fe2360 --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala @@ -0,0 +1,140 @@ +package scala.tools.nsc.backend.jvm + +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))) + } +} diff --git a/test/junit/scala/tools/nsc/backend/jvm/OptimizedBytecodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/OptimizedBytecodeTest.scala new file mode 100644 index 0000000000..003162c1ad --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/OptimizedBytecodeTest.scala @@ -0,0 +1,362 @@ +package scala.tools.nsc.backend.jvm + +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 OptimizedBytecodeTest extends BytecodeTesting { + override def compilerArgs = "-Yopt:l:classpath -Yopt-warnings" + import compiler._ + + @Test + def t2171(): Unit = { + val code = + """class C { + | final def m(msg: => String) = try 0 catch { case ex: Throwable => println(msg) } + | def t(): Unit = while (true) m("...") + |} + """.stripMargin + val c = compileClass(code) + assertSameCode(getMethod(c, "t"), List(Label(0), Jump(GOTO, Label(0)))) + } + + @Test + def t3430(): Unit = { + val code = + """class C { + | final def m(f: String => Boolean) = f("a") + | def t(): Boolean = + | m { s1 => + | m { s2 => + | while (true) { } + | true + | } + | } + |} + """.stripMargin + val c = compileClass(code) + + assertSameSummary(getMethod(c, "t"), List( + LDC, ASTORE, ALOAD /*0*/, ALOAD /*1*/, "C$$$anonfun$1", IRETURN)) + assertSameSummary(getMethod(c, "C$$$anonfun$1"), List(LDC, "C$$$anonfun$2", IRETURN)) + assertSameSummary(getMethod(c, "C$$$anonfun$2"), List(-1 /*A*/, GOTO /*A*/)) + } + + @Test + def t3252(): Unit = { + val code = + """class C { + | def t(x: Boolean): Thread = { + | g { + | x match { + | case false => Tat.h { } + | } + | } + | } + | + | private def g[T](block: => T) = ??? + |} + |object Tat { + | def h(block: => Unit): Nothing = ??? + |} + """.stripMargin + val List(c, t, tMod) = compileClasses(code, allowMessage = _.msg.contains("not be exhaustive")) + assertSameSummary(getMethod(c, "t"), List(GETSTATIC, "$qmark$qmark$qmark", ATHROW)) + } + + @Test + def t6157(): Unit = { + val code = + """class C { + | def t = println(ErrorHandler.defaultIfIOException("String")("String")) + |} + |object ErrorHandler { + | import java.io.IOException + | @inline + | def defaultIfIOException[T](default: => T)(closure: => T): T = try closure catch { + | case e: IOException => default + | } + |} + """.stripMargin + + val msg = + """ErrorHandler$::defaultIfIOException(Lscala/Function0;Lscala/Function0;)Ljava/lang/Object; is annotated @inline but could not be inlined: + |The operand stack at the callsite in C::t()V contains more values than the + |arguments expected by the callee ErrorHandler$::defaultIfIOException(Lscala/Function0;Lscala/Function0;)Ljava/lang/Object;. These values would be discarded + |when entering an exception handler declared in the inlined method.""".stripMargin + + compileClasses(code, allowMessage = _.msg == msg) + } + + @Test + def t6547(): Unit = { // "pos" test -- check that it compiles + val code = + """trait ConfigurableDefault[@specialized V] { + | def fillArray(arr: Array[V], v: V) = (arr: Any) match { + | case x: Array[Int] => null + | case x: Array[Long] => v.asInstanceOf[Long] + | } + |} + """.stripMargin + compileToBytes(code) + } + + @Test + def t8062(): Unit = { + val c1 = + """package warmup + |object Warmup { def filter[A](p: Any => Boolean): Any = filter[Any](p) } + """.stripMargin + val c2 = "class C { def t = warmup.Warmup.filter[Any](x => false) }" + val List(c, _, _) = compileClassesSeparately(List(c1, c2), extraArgs = compilerArgs) + assertInvoke(getMethod(c, "t"), "warmup/Warmup$", "filter") + } + + @Test + def t8306(): Unit = { // "pos" test + val code = + """class C { + | def foo: Int = 123 + | lazy val extension: Int = foo match { + | case idx if idx != -1 => 15 + | case _ => 17 + | } + |} + """.stripMargin + compileToBytes(code) + } + + @Test + def t8359(): Unit = { // "pos" test + // This is a minimization of code that crashed the compiler during bootstrapping + // in the first iteration of https://github.com/scala/scala/pull/4373, the PR + // that adjusted the order of free and declared params in LambdaLift. + + // Was: + // java.lang.AssertionError: assertion failed: + // Record Record(<$anon: Function1>,Map(value a$1 -> Deref(LocalVar(value b)))) does not contain a field value b$1 + // at scala.tools.nsc.Global.assert(Global.scala:262) + // at scala.tools.nsc.backend.icode.analysis.CopyPropagation$copyLattice$State.getFieldNonRecordValue(CopyPropagation.scala:113) + // at scala.tools.nsc.backend.icode.analysis.CopyPropagation$copyLattice$State.getFieldNonRecordValue(CopyPropagation.scala:122) + // at scala.tools.nsc.backend.opt.ClosureElimination$ClosureElim$$anonfun$analyzeMethod$1$$anonfun$apply$2.replaceFieldAccess$1(ClosureElimination.scala:124) + val code = + """package test + |class Typer { + | def bar(a: Boolean, b: Boolean): Unit = { + | @inline + | def baz(): Unit = { + | ((_: Any) => (Typer.this, a, b)).apply("") + | } + | ((_: Any) => baz()).apply("") + | } + |} + """.stripMargin + compileToBytes(code) + } + + @Test + def t9123(): Unit = { // "pos" test + val code = + """trait Setting { + | type T + | def value: T + |} + |object Test { + | def test(x: Some[Setting]) = x match { + | case Some(dep) => Some(dep.value) map (_ => true) + | } + |} + """.stripMargin + compileToBytes(code) + } + + @Test + def traitForceInfo(): Unit = { + // This did NOT crash unless it's in the interactive package. + // error: java.lang.AssertionError: assertion failed: trait Contexts.NoContext$ linkedModule: List() + // at scala.Predef$.assert(Predef.scala:160) + // at scala.tools.nsc.symtab.classfile.ClassfileParser$innerClasses$.innerSymbol$1(ClassfileParser.scala:1211) + // at scala.tools.nsc.symtab.classfile.ClassfileParser$innerClasses$.classSymbol(ClassfileParser.scala:1223) + // at scala.tools.nsc.symtab.classfile.ClassfileParser.classNameToSymbol(ClassfileParser.scala:489) + // at scala.tools.nsc.symtab.classfile.ClassfileParser.sig2type$1(ClassfileParser.scala:757) + // at scala.tools.nsc.symtab.classfile.ClassfileParser.sig2type$1(ClassfileParser.scala:789) + val code = + """package scala.tools.nsc + |package interactive + | + |trait MyContextTrees { + | val self: Global + | val NoContext = self.analyzer.NoContext + |} + """.stripMargin + compileClasses(code) + } + + @Test + def t9160(): Unit = { + val code = + """class C { + | def getInt: Int = 0 + | def t(trees: Object): Int = { + | trees match { + | case Some(elems) => + | case tree => getInt + | } + | 55 + | } + |} + """.stripMargin + val c = compileClass(code) + assertSameSummary(getMethod(c, "t"), List( + ALOAD /*1*/, INSTANCEOF /*Some*/, IFNE /*A*/, + ALOAD /*0*/, "getInt", POP, + -1 /*A*/, BIPUSH, IRETURN)) + } + + @Test + def t8796(): Unit = { + val code = + """final class C { + | def pr(): Unit = () + | def t(index: Int): Unit = index match { + | case 0 => pr() + | case 1 => pr() + | case _ => t(index - 2) + | } + |} + """.stripMargin + val c = compileClass(code) + assertSameSummary(getMethod(c, "t"), List( + -1 /*A*/, ILOAD /*1*/, TABLESWITCH, + -1, ALOAD, "pr", RETURN, + -1, ALOAD, "pr", RETURN, + -1, ILOAD, ICONST_2, ISUB, ISTORE, GOTO /*A*/)) + } + + @Test + def t8524(): Unit = { + val c1 = + """package library + |object Library { + | @inline def pleaseInlineMe() = 1 + | object Nested { @inline def pleaseInlineMe() = 2 } + |} + """.stripMargin + + val c2 = + """class C { + | def t = library.Library.pleaseInlineMe() + library.Library.Nested.pleaseInlineMe() + |} + """.stripMargin + + val cls = compileClassesSeparately(List(c1, c2), extraArgs = compilerArgs) + val c = findClass(cls, "C") + assertSameSummary(getMethod(c, "t"), List( + GETSTATIC, IFNONNULL, ACONST_NULL, ATHROW, // module load and null checks not yet eliminated + -1, ICONST_1, GETSTATIC, IFNONNULL, ACONST_NULL, ATHROW, + -1, ICONST_2, IADD, IRETURN)) + } + + @Test + def privateInline(): Unit = { + val code = + """final class C { + | private var x1 = false + | var x2 = false + | + | @inline private def wrapper1[T](body: => T): T = { + | val saved = x1 + | x1 = true + | try body + | finally x1 = saved + | } + | + | @inline private def wrapper2[T](body: => T): T = { + | val saved = x2 + | x2 = true + | try body + | finally x2 = saved + | } + | // inlined + | def f1a() = wrapper1(5) + | // not inlined: even after inlining `identity`, the Predef module is already on the stack for the + | // subsequent null check (the receiver of an inlined method, in this case Predef, is checked for + | // nullness, to ensure an NPE is thrown) + | def f1b() = identity(wrapper1(5)) + | + | def f2a() = wrapper2(5) // inlined + | def f2b() = identity(wrapper2(5)) // not inlined + |} + """.stripMargin + val c = compileClass(code, allowMessage = _.msg.contains("exception handler declared in the inlined method")) + assertInvoke(getMethod(c, "f1a"), "C", "C$$$anonfun$1") + assertInvoke(getMethod(c, "f1b"), "C", "wrapper1") + assertInvoke(getMethod(c, "f2a"), "C", "C$$$anonfun$3") + assertInvoke(getMethod(c, "f2b"), "C", "wrapper2") + } + + @Test + def t7060(): Unit = { + val code = + """class C { + | @inline final def mbarray_apply_minibox(array: Any, tag: Byte): Long = + | if (tag == 0) array.asInstanceOf[Array[Long]](0) + | else array.asInstanceOf[Array[Byte]](0).toLong + | + | def t = mbarray_apply_minibox(null, 0) + |} + """.stripMargin + val c = compileClass(code) + assertNoInvoke(getMethod(c, "t")) + } + + @Test + def t8315(): Unit = { + val code = + """class C { + | def t(as: Listt): Unit = { + | map(as, (_: Any) => return) + | } + | final def map(x: Listt, f: Any => Any): Any = { + | if (x eq Nill) "" else f("") + | } + |} + |object Nill extends Listt + |class Listt + """.stripMargin + val List(c, nil, nilMod, listt) = compileClasses(code) + assertInvoke(getMethod(c, "t"), "C", "C$$$anonfun$1") + } + + @Test + def t8315b(): Unit = { + val code = + """class C { + | def crash: Unit = { + | val key = "" + | try map(new F(key)) + | catch { case _: Throwable => } + | } + | final def map(f: F): Any = f.apply("") + |} + |final class F(key: String) { + | final def apply(a: Any): Any = throw new RuntimeException(key) + |} + """.stripMargin + val List(c, f) = compileClasses(code) + assertInvoke(getMethod(c, "crash"), "C", "map") + } + + @Test + def optimiseEnablesNewOpt(): Unit = { + val code = """class C { def t = (1 to 10) foreach println }""" + val List(c) = readAsmClasses(newCompiler(extraArgs = "-optimise -deprecation").compileToBytes(code, allowMessage = _.msg.contains("is deprecated"))) + assertInvoke(getMethod(c, "t"), "C", "C$$$anonfun$1") // range-foreach inlined from classpath + } +} -- cgit v1.2.3