From cca13ad0cb12669ed7a7931c5029a52486b9d6fc Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 8 Jul 2015 15:45:03 +1000 Subject: [indylambda] Improve test coverage Adding tests for the selective use of boxing bridge methods and to show that specialization is not subverted by indylambda. Other aspects of indylambda are tested by tests like: - run/lambda-serialization.scala - run/indylambda-boxing When those tests were written, they only tested the old backend. However, now that we have Java 8 and the new backend avaialble by default to partest, they provide the intended coverage. --- test/files/run/indylambda-specialization.scala | 15 ++++++ .../tools/nsc/backend/jvm/IndyLambdaTest.scala | 54 ++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 test/files/run/indylambda-specialization.scala create mode 100644 test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala (limited to 'test') 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..8bb003c8fb --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala @@ -0,0 +1,54 @@ +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 { + 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)java/lang/Object;` / `(I)java/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")) + } +} -- cgit v1.2.3 From 04977b71e6e7097afdd1cbe0d2c7514591ed3f9e Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 9 Jul 2015 12:18:46 +1000 Subject: Address review feedback - Add requisite boilerplate for test cleanup to IndyLambdaTest - ... and to the spot where I copy/pasted the wrong code from - Fix comment. --- test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala | 5 +++-- test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala b/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala index 8bb003c8fb..f5f93fef5c 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala @@ -18,7 +18,8 @@ object IndyLambdaTest extends ClearAfterClass.Clearable { } } -class IndyLambdaTest { +class IndyLambdaTest extends ClearAfterClass { + ClearAfterClass.stateToClear = IndyLambdaTest val compiler = IndyLambdaTest.compiler @Test def boxingBridgeMethodUsedSelectively(): Unit = { @@ -35,7 +36,7 @@ class IndyLambdaTest { // 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)java/lang/Object;` / `(I)java/lang/Object;` respectively.) + // 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")) 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) = { -- cgit v1.2.3