summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@typesafe.com>2015-07-09 07:49:32 +0200
committerLukas Rytz <lukas.rytz@typesafe.com>2015-07-09 07:49:32 +0200
commit057664807902aeab50e399455d7c7917e9a6d071 (patch)
tree0c46e5a6a8a6cb091d8142162ae8744af73ddb27
parent41edbe65a738a4a109f1b76b977354a3fee474c0 (diff)
parent04977b71e6e7097afdd1cbe0d2c7514591ed3f9e (diff)
downloadscala-057664807902aeab50e399455d7c7917e9a6d071.tar.gz
scala-057664807902aeab50e399455d7c7917e9a6d071.tar.bz2
scala-057664807902aeab50e399455d7c7917e9a6d071.zip
Merge pull request #4609 from retronym/topic/indylambda-test
[indylambda] Improve test coverage
-rw-r--r--test/files/run/indylambda-specialization.scala15
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala55
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala4
3 files changed, 73 insertions, 1 deletions
diff --git a/test/files/run/indylambda-specialization.scala b/test/files/run/indylambda-specialization.scala
new file mode 100644
index 0000000000..2c66073e90
--- /dev/null
+++ b/test/files/run/indylambda-specialization.scala
@@ -0,0 +1,15 @@
+object Test {
+ def assertApply(expected: Boolean) = {
+ val frames = Thread.currentThread.getStackTrace.takeWhile(_.getMethodName != "main")
+ val usesObjectApply = frames.exists(_.getMethodName == "apply")
+ assert(expected == usesObjectApply, frames.mkString("\n"))
+ }
+ def assertSpecialized() = assertApply(false)
+ def assertUnspecialized() = assertApply(true)
+ def main(args: Array[String]): Unit = {
+ ((i: String) => {assertUnspecialized(); i}).apply("")
+ (() => {assertSpecialized(); 0}).apply()
+ ((i: Int) => {assertSpecialized(); i}).apply(0)
+ ((i: Int, j: Int) => {assertSpecialized(); i + j}).apply(0, 0)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala b/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala
new file mode 100644
index 0000000000..f5f93fef5c
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala
@@ -0,0 +1,55 @@
+package scala.tools.nsc.backend.jvm
+
+import org.junit.Assert._
+import org.junit.{Assert, Test}
+
+import scala.tools.asm.{Handle, Opcodes}
+import scala.tools.asm.tree.InvokeDynamicInsnNode
+import scala.tools.nsc.backend.jvm.AsmUtils._
+import scala.tools.nsc.backend.jvm.CodeGenTools._
+import scala.tools.testing.ClearAfterClass
+import scala.collection.JavaConverters._
+
+object IndyLambdaTest extends ClearAfterClass.Clearable {
+ var compiler = newCompiler(extraArgs = "-Ybackend:GenBCode")
+
+ def clear(): Unit = {
+ compiler = null
+ }
+}
+
+class IndyLambdaTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = IndyLambdaTest
+ val compiler = IndyLambdaTest.compiler
+
+ @Test def boxingBridgeMethodUsedSelectively(): Unit = {
+ def implMethodDescriptorFor(code: String): String = {
+ val method = compileMethods(compiler)(s"""def f = $code """).find(_.name == "f").get
+ val x = method.instructions.iterator.asScala.toList
+ x.flatMap {
+ case insn : InvokeDynamicInsnNode => insn.bsmArgs.collect { case h : Handle => h.getDesc }
+ case _ => Nil
+ }.head
+ }
+ // unspecialized functions that have a primitive in parameter or return position
+ // give rise to a "boxing bridge" method (which has the suffix `$adapted`).
+ // This is because Scala's unboxing of null values gives zero, whereas Java's throw a NPE.
+
+ // 1. Here we show that we are calling the boxing bridge (the lambda bodies here are compiled into
+ // methods of `(I)Ljava/lang/Object;` / `(I)Ljava/lang/Object;` respectively.)
+ assertEquals("(Ljava/lang/Object;)Ljava/lang/Object;", implMethodDescriptorFor("(x: Int) => new Object"))
+ assertEquals("(Ljava/lang/Object;)Ljava/lang/Object;", implMethodDescriptorFor("(x: Object) => 0"))
+
+ // 2a. We don't need such adaptations for parameters or return values with types that differ
+ // from Object due to other generic substitution, LambdaMetafactory will downcast the arguments.
+ assertEquals("(Ljava/lang/String;)Ljava/lang/String;", implMethodDescriptorFor("(x: String) => x"))
+
+ // 2b. Testing 2a. in combination with 1.
+ assertEquals("(Ljava/lang/Object;)Ljava/lang/String;", implMethodDescriptorFor("(x: Int) => \"\""))
+ assertEquals("(Ljava/lang/String;)Ljava/lang/Object;", implMethodDescriptorFor("(x: String) => 0"))
+
+ // 3. Specialized functions, don't need any of this as they implement a method like `apply$mcII$sp`,
+ // and the (un)boxing is handled in the base class in code emitted by scalac.
+ assertEquals("(I)I", implMethodDescriptorFor("(x: Int) => x"))
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala
index 57088bdd2f..5ccb940415 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala
@@ -27,7 +27,9 @@ object InlineInfoTest extends ClearAfterClass.Clearable {
}
@RunWith(classOf[JUnit4])
-class InlineInfoTest {
+class InlineInfoTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = InlineInfoTest
+
val compiler = InlineInfoTest.compiler
def compile(code: String) = {