diff options
author | Lukas Rytz <lukas.rytz@typesafe.com> | 2015-07-09 07:49:32 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@typesafe.com> | 2015-07-09 07:49:32 +0200 |
commit | 057664807902aeab50e399455d7c7917e9a6d071 (patch) | |
tree | 0c46e5a6a8a6cb091d8142162ae8744af73ddb27 | |
parent | 41edbe65a738a4a109f1b76b977354a3fee474c0 (diff) | |
parent | 04977b71e6e7097afdd1cbe0d2c7514591ed3f9e (diff) | |
download | scala-057664807902aeab50e399455d7c7917e9a6d071.tar.gz scala-057664807902aeab50e399455d7c7917e9a6d071.tar.bz2 scala-057664807902aeab50e399455d7c7917e9a6d071.zip |
Merge pull request #4609 from retronym/topic/indylambda-test
[indylambda] Improve test coverage
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) = { |