summaryrefslogtreecommitdiff
path: root/test/junit/scala/lang
diff options
context:
space:
mode:
Diffstat (limited to 'test/junit/scala/lang')
-rw-r--r--test/junit/scala/lang/annotations/BytecodeTest.scala80
-rw-r--r--test/junit/scala/lang/annotations/RunTest.scala32
-rw-r--r--test/junit/scala/lang/primitives/BoxUnboxTest.scala228
-rw-r--r--test/junit/scala/lang/primitives/NaNTest.scala38
-rw-r--r--test/junit/scala/lang/primitives/PredefAutoboxingTest.scala33
-rw-r--r--test/junit/scala/lang/stringinterpol/StringContextTest.scala265
-rw-r--r--test/junit/scala/lang/traits/BytecodeTest.scala612
-rw-r--r--test/junit/scala/lang/traits/RunTest.scala20
8 files changed, 1308 insertions, 0 deletions
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..94413b69b4
--- /dev/null
+++ b/test/junit/scala/lang/primitives/BoxUnboxTest.scala
@@ -0,0 +1,228 @@
+package scala.lang.primitives
+
+import org.junit.Assert._
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+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._
+
+ @Test
+ def boxUnboxInt(): Unit = {
+ import scala.tools.testing.AssertUtil._
+ import org.junit.Assert._
+
+ def genericNull[T] = null.asInstanceOf[T] // allowed, see SI-4437, point 2
+
+ 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 = {
+ import scala.tools.testing.AssertUtil._
+ import org.junit.Assert._
+
+ 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 ()).
+ import scala.tools.testing.AssertUtil._
+ import org.junit.Assert._
+
+ 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 = {
+ 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" }
+
+ val result =
+ 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"
+ assertEquals(result,
+ "0000null-0npe0npenull-()()()()null-0000null-0npe0npenull-()()()()null-0000null-0npe0npenull-()()()()null-00000-0npe0npenpe-()()()()null")
+ }
+
+}
diff --git a/test/junit/scala/lang/primitives/NaNTest.scala b/test/junit/scala/lang/primitives/NaNTest.scala
new file mode 100644
index 0000000000..f4c4258395
--- /dev/null
+++ b/test/junit/scala/lang/primitives/NaNTest.scala
@@ -0,0 +1,38 @@
+package scala.lang.primitives
+
+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 NaNTest extends RunTesting {
+
+ @Test
+ def compNaNFalse(): Unit = {
+ def code(tp: String) =
+ s"""val n = $tp.NaN
+ |def ne(x: $tp, y: $tp) = x != y
+ |val fs: List[($tp, $tp) => Boolean] = List(_ < _, _ <= _, _ > _, _ >= _, _ == _, (x, y) => !ne(x, y))
+ |val vs = List[$tp](n, 1, -1, 0)
+ |for (f <- fs; v <- vs; (x, y) <- List((n, v), (v, n))) yield f(x, y)
+ """.stripMargin
+
+ runner.run[List[Boolean]](code("Double")).foreach(assertFalse)
+ runner.run[List[Boolean]](code("Float")).foreach(assertFalse)
+ }
+
+ @Test
+ def genericEqNe(): Unit = {
+ def code(tp: String) =
+ s"""def a[T](x: T, y: T) = x == y
+ |def b[T](x: T, y: T) = x != y
+ |val n = $tp.NaN
+ |a(n, n) :: a(n, 0) :: a (0, n) :: !b(n, n) :: !b(n, 0) :: !b(0, n) :: Nil
+ """.stripMargin
+ runner.run[List[Boolean]](code("Double")).foreach(assertFalse)
+ runner.run[List[Boolean]](code("Float")).foreach(assertFalse)
+ }
+}
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 %<d ${9}%d" -> "42 42 9",
+ f"${7}%d %<d ${9}%d" -> "7 7 9",
+ f"${7}%d %2$$d ${9}%d" -> "7 9 9",
+
+ f"${null}%d %<B" -> "null FALSE",
+
+ f"${5: Any}" -> "5",
+ f"${5}%s%<d" -> "55",
+ f"${3.14}%s,%<f" -> 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..ccf53fe3b1
--- /dev/null
+++ b/test/junit/scala/lang/traits/BytecodeTest.scala
@@ -0,0 +1,612 @@
+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._
+
+ val noForwardersCompiler = newCompiler(extraArgs = "-Xmixin-force-forwarders:false")
+
+ def checkForwarder(classes: Map[String, ClassNode], clsName: Symbol, target: String) = {
+ val f = getMethod(classes(clsName.name), "f")
+ assertSameCode(f, List(VarOp(ALOAD, 0), Invoke(INVOKESTATIC, target, "f$", s"(L$target;)I", true), 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 = noForwardersCompiler.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) = noForwardersCompiler.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 = noForwardersCompiler.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) = noForwardersCompiler.compileClasses(invocationReceiversTestCode.definitions("Object"))
+ // mixin forwarder in C1
+ assertSameCode(getMethod(c1, "clone"), List(VarOp(ALOAD, 0), Invoke(INVOKESTATIC, "T", "clone$", "(LT;)Ljava/lang/Object;", true), 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) = noForwardersCompiler.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, "<init>"),
+ // pass `null` to super constructor, no box-unbox, no Integer created
+ List(ALOAD, ILOAD, PUTFIELD, ALOAD, ACONST_NULL, "<init>", RETURN))
+ }
+
+ @Test
+ def mixinForwarders(): Unit = {
+ val code =
+ """trait T { def f = 1 }
+ |class C extends T
+ """.stripMargin
+ val List(c1, _) = noForwardersCompiler.compileClasses(code)
+ val List(c2, _) = compileClasses(code)
+ assert(getMethods(c1, "f").isEmpty)
+ assertSameCode(getMethod(c2, "f"),
+ List(VarOp(ALOAD, 0), Invoke(INVOKESTATIC, "T", "f$", "(LT;)I", true), Op(IRETURN)))
+ }
+
+ @Test
+ def sd143(): Unit = {
+ val code =
+ """class A { def m = 1 }
+ |class B extends A { override def m = 2 }
+ |trait T extends A
+ |class C extends B with T {
+ | override def m = super[T].m // should invoke A.m
+ |}
+ """.stripMargin
+
+ val err =
+ """cannot emit super call: the selected method m is declared in class A, which is not the direct superclass of class C.
+ |An unqualified super call (super.m) would be allowed.""".stripMargin
+ val cls = compileClasses(code, allowMessage = _.msg contains err)
+ assert(cls.isEmpty, cls.map(_.name))
+ }
+
+ @Test
+ def sd143b(): Unit = {
+ val jCode = List("interface A { default int m() { return 1; } }" -> "A.java")
+ val code =
+ """class B extends A { override def m = 2 }
+ |trait T extends A
+ |class C extends B with T {
+ | override def m = super[T].m
+ |}
+ """.stripMargin
+
+ val err = "unable to emit super call unless interface A (which declares method m) is directly extended by class C"
+ val cls = compileClasses(code, jCode, allowMessage = _.msg contains err)
+ assert(cls.isEmpty, cls.map(_.name))
+ }
+
+ @Test
+ def sd143c(): Unit = {
+ // Allow super calls to class methods of indirect super classes
+ val code =
+ """class A { def f = 1 }
+ |class B extends A
+ |trait T extends A { override def f = 2 }
+ |class C extends B with T {
+ | def t1 = super[B].f
+ | def t2 = super.f
+ | def t3 = super[T].f
+ |}
+ """.stripMargin
+ val List(_, _, c, _) = compileClasses(code)
+ val t1 = getInstructions(c, "t1")
+ assert(t1 contains Invoke(INVOKESPECIAL, "A", "f", "()I", false), t1.stringLines)
+ val t2 = getInstructions(c, "t2")
+ val invStat = Invoke(INVOKESTATIC, "T", "f$", "(LT;)I", true)
+ assert(t2 contains invStat, t2.stringLines)
+ val t3 = getInstructions(c, "t3")
+ assert(t3 contains invStat, t3.stringLines)
+ }
+
+ @Test
+ def sd210(): Unit = {
+ val jCode = List("interface A { default int m() { return 1; } }" -> "A.java")
+
+
+ // used to crash in the backend (SD-210) under `-Xmixin-force-forwarders:true`
+ val code1 =
+ """trait B1 extends A // called "B1" not "B" due to scala-dev#214
+ |class C extends B1
+ """.stripMargin
+
+ val List(_, c1a) = noForwardersCompiler.compileClasses(code1, jCode)
+ assert(getAsmMethods(c1a, "m").isEmpty) // ok, no forwarder
+
+ // here we test a warning. without `-Xmixin-force-forwarders:true`, the forwarder would not be
+ // generated, it is not necessary for correctness.
+ val List(_, c1b) = compileClasses(code1, jCode)
+ assert(getAsmMethods(c1b, "m").isEmpty) // no forwarder: it cannot be implemented because A is not a direct parent of C
+
+
+ val code2 =
+ """abstract class B { def m(): Int }
+ |trait T extends B with A
+ |class C extends T
+ """.stripMargin
+
+ // here we test a compilation error. the forwarder is required for correctness, but it cannot be generated.
+ val err = "Unable to implement a mixin forwarder for method m in class C unless interface A is directly extended by class C"
+ val cs = compileClasses(code2, jCode, allowMessage = _.msg contains err)
+ assert(cs.isEmpty, cs.map(_.name))
+
+
+ val code3 =
+ """abstract class B { def m: Int }
+ |class C extends B with A
+ """.stripMargin
+
+ val List(_, c3) = compileClasses(code3, jCode)
+ // invokespecial to A.m is correct here: A is an interface, so resolution starts at A.
+ // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial
+ val ins3 = getMethod(c3, "m").instructions
+ assert(ins3 contains Invoke(INVOKESPECIAL, "A", "m", "()I", true), ins3.stringLines)
+
+
+ val code4 =
+ """trait B { self: A => override def m = 2 }
+ |class C extends A with B // forwarder, invokestatic B.m$
+ """.stripMargin
+
+ val List(_, c4) = compileClasses(code4, jCode)
+ val ins4 = getMethod(c4, "m").instructions
+ assert(ins4 contains Invoke(INVOKESTATIC, "B", "m$", "(LB;)I", true), ins4.stringLines)
+
+
+ // scala-only example
+ val code5 =
+ """trait AS { def m = 1 }
+ |abstract class B { def m: Int }
+ |class C extends B with AS // forwarder, invokestatic AS.m$
+ """.stripMargin
+
+ val List(_, _, c5) = compileClasses(code5)
+ val ins5 = getMethod(c5, "m").instructions
+ assert(ins5 contains Invoke(INVOKESTATIC, "AS", "m$", "(LAS;)I", true), ins5.stringLines)
+ }
+
+ @Test
+ def sd224(): Unit = {
+ val jCode = List("interface T { default int f() { return 1; } }" -> "T.java")
+ val code =
+ """trait U extends T
+ |class C extends U { def t = super.f }
+ """.stripMargin
+ val msg = "unable to emit super call unless interface T (which declares method f) is directly extended by class C"
+ val cls = compileClasses(code, jCode, allowMessage = _.msg contains msg)
+ assertEquals(cls, Nil)
+ }
+
+ def ifs(c: ClassNode, expected: List[String]) = assertEquals(expected, c.interfaces.asScala.toList.sorted)
+ def invSt(m: Method, receiver: String, method: String = "f$", itf: Boolean = true): Unit =
+ assert(m.instructions contains Invoke(INVOKESTATIC, receiver, method, s"(L$receiver;)I", itf), m.instructions.stringLines)
+ def invSp(m: Method, receiver: String, method: String = "f", sig: String = "()I", itf: Boolean = true): Unit =
+ assert(m.instructions contains Invoke(INVOKESPECIAL, receiver, method, sig, itf), m.instructions.stringLines)
+
+ @Test
+ def superCalls1(): Unit = {
+ val code =
+ """trait T { def f = 1 }
+ |trait U extends T
+ |class C extends U { def t = super.f }
+ """.stripMargin
+ val List(c, _*) = compileClasses(code)
+ ifs(c, List("U"))
+ invSt(getMethod(c, "t"), "T")
+ invSt(getMethod(c, "f"), "T")
+ }
+
+ @Test
+ def superCalls2(): Unit = {
+ val code =
+ """class A { def f = 1 }
+ |trait T extends A { override def f = 2 }
+ |class B extends A
+ |class C extends B with T {
+ | def t1 = super.f
+ | def t2 = super[T].f
+ | def t3 = super[B].f
+ |}
+ """.stripMargin
+ val List(_, _, c, _) = compileClasses(code)
+ invSt(getMethod(c, "f"), "T")
+ invSt(getMethod(c, "t1"), "T")
+ invSt(getMethod(c, "t2"), "T")
+ invSp(getMethod(c, "t3"), "A", itf = false)
+ }
+
+ @Test
+ def superCalls3(): Unit = {
+ val code =
+ """class A { def f = 1 }
+ |trait T extends A
+ |class B extends A { override def f = 2 }
+ |class C extends B with T {
+ | def t1 = super.f
+ | // def t2 = super[T].f // error: cannot emit super call. tested in sd143
+ | def t3 = super[B].f
+ |}
+ """.stripMargin
+ val List(_, _, c, _) = compileClasses(code)
+ invSp(getMethod(c, "t1"), "B", itf = false)
+ invSp(getMethod(c, "t3"), "B", itf = false)
+ assertEquals(getMethods(c, "f"), Nil)
+ }
+
+ @Test
+ def superCalls4(): Unit = {
+ val code =
+ """trait T1 { def f = 1 }
+ |trait T2 { self: T1 => override def f = 2 }
+ |trait U extends T1 with T2
+ |class C extends U {
+ | def t1 = super.f
+ | def t2 = super[U].f
+ |}
+ """.stripMargin
+ val List(c, _*) = compileClasses(code)
+ ifs(c, List("U"))
+ invSt(getMethod(c, "f"), "T2")
+ invSt(getMethod(c, "t1"), "T2")
+ invSt(getMethod(c, "t2"), "T2")
+ }
+
+ @Test
+ def superCalls5(): Unit = {
+ val code =
+ """trait T1 { def f = 1 }
+ |trait T2 { self: T1 => override def f = 2 }
+ |trait U extends T1 with T2
+ |class C extends U with T1 with T2
+ """.stripMargin
+ val List(c, _*) = compileClasses(code)
+ ifs(c, List("U")) // T1, T2 removed by minimizeParents
+ invSt(getMethod(c, "f"), "T2")
+ }
+
+ @Test
+ def superCalls6(): Unit = {
+ val code =
+ """trait T { override def hashCode = -1 }
+ |trait U extends T
+ |class C extends U {
+ | def t1 = super[U].hashCode
+ | def t2 = super.hashCode
+ |}
+ """.stripMargin
+ val List(c, _*) = compileClasses(code)
+ ifs(c, List("U"))
+ invSt(getMethod(c, "hashCode"), "T", "hashCode$")
+ invSt(getMethod(c, "t1"), "T", "hashCode$")
+ invSt(getMethod(c, "t2"), "T", "hashCode$")
+ }
+
+ @Test
+ def superCalls7(): Unit = {
+ val code =
+ """trait T { def f = 1 }
+ |trait U1 extends T { override def f = 2 }
+ |trait U2 extends T { override def f = 3 }
+ |class C1 extends T with U1 with U2 {
+ | def t1 = super.f
+ | def t2 = super[T].f
+ | def t3 = super[U1].f
+ | def t4 = super[U2].f
+ |}
+ |class C2 extends T with U2 with U1 {
+ | def t1 = super.f
+ |}
+ """.stripMargin
+ val List(c1, c2, _*) = compileClasses(code)
+ ifs(c1, List("U1", "U2"))
+ ifs(c2, List("U1", "U2"))
+ invSt(getMethod(c1, "f"), "U2")
+ invSt(getMethod(c1, "t1"), "U2")
+ invSt(getMethod(c1, "t2"), "T")
+ invSt(getMethod(c1, "t3"), "U1")
+ invSt(getMethod(c1, "t4"), "U2")
+ invSt(getMethod(c2, "f"), "U1")
+ invSt(getMethod(c2, "t1"), "U1")
+ }
+
+ @Test
+ def superCalls8(): Unit = {
+ val code =
+ """trait T1 { def f = 1 }
+ |trait T2 { _: T1 => override def f = 2 }
+ |trait U extends T1 with T2
+ |trait V extends U with T2
+ |class C extends V {
+ | def t1 = super.f
+ | def t2 = super[V].f
+ |}
+ """.stripMargin
+ val List(c, _*) = compileClasses(code)
+ ifs(c, List("V"))
+ invSt(getMethod(c, "f"), "T2")
+ invSt(getMethod(c, "t1"), "T2")
+ invSt(getMethod(c, "t2"), "T2")
+ }
+
+ @Test
+ def superCalls9(): Unit = {
+ val code =
+ """trait T { def f: Int }
+ |trait U1 extends T { def f = 0 }
+ |trait U2 extends T { override def f = 1 }
+ |trait V extends U1
+ |
+ |trait W1 extends V with U2
+ |class C1 extends W1 with U2
+ |
+ |trait W2 extends V with U2 { override def f = super[U2].f }
+ |class C2 extends W2 with U2
+ |
+ |trait W3 extends V with U2 { override def f = super.f }
+ |class C3 extends W3 with U2
+ """.stripMargin
+ val List(c1, c2, c3, _*) = compileClasses(code)
+
+ ifs(c1, List("W1"))
+ invSt(getMethod(c1, "f"), "U2")
+
+ ifs(c2, List("W2"))
+ invSt(getMethod(c2, "f"), "W2")
+
+ ifs(c3, List("W3"))
+ invSt(getMethod(c3, "W3$$super$f"), "U2")
+ invSt(getMethod(c3, "f"), "W3")
+ }
+}
+
+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
+ }
+}