diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2016-03-21 08:47:45 -0700 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2016-03-26 22:54:12 -0700 |
commit | 2aa8eba5007f0e0eda3a2ef3fdffa1f468dc1fa4 (patch) | |
tree | a552aa90cac3d45cbcff3827407c5b3549f81298 /test/junit | |
parent | f922f367d58b3ba6bbb4cb0864ce82c5cd6f7966 (diff) | |
download | scala-2aa8eba5007f0e0eda3a2ef3fdffa1f468dc1fa4.tar.gz scala-2aa8eba5007f0e0eda3a2ef3fdffa1f468dc1fa4.tar.bz2 scala-2aa8eba5007f0e0eda3a2ef3fdffa1f468dc1fa4.zip |
Test bytecode emitted for indy sammy
Test that SAM conversion happens after implicit view application
A function node is first type checked, and parameter types are
inferred, regardless of whether the expected function type is one
of our built-in FunctionN classes, or a user-defined Single
Abstract Method type.
`typedFunction` always assigns a built-in `FunctionN` type to the
tree, though.
Next, if the expected type is a (polymorphic) SAM type, this
creates a tension between the tree's type and the expect type.
This gap is closed by the adapt method, by applying one of the
implicit conversion in the spec in order (e.g., numeric widening,
implicit view application, and now, also SAM conversion)
Thus, `adaptToSam` will assign the expected SAM type to the
`Function` tree. (This may require some type inference.) The
back-end will emit the right invokedynamic instruction that uses
Java's LambdaMetaFactory to spin up a class that implements the
target method (whether it's defined in FunctionN or some other
Java functional interface).
Diffstat (limited to 'test/junit')
-rw-r--r-- | test/junit/scala/tools/nsc/backend/jvm/IndySammyTest.scala | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/test/junit/scala/tools/nsc/backend/jvm/IndySammyTest.scala b/test/junit/scala/tools/nsc/backend/jvm/IndySammyTest.scala new file mode 100644 index 0000000000..6f213e6625 --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/IndySammyTest.scala @@ -0,0 +1,130 @@ +package scala.tools.nsc +package backend.jvm + +import org.junit.Assert.assertEquals +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test + +import scala.tools.asm.Opcodes._ +import scala.tools.asm.tree._ +import scala.tools.nsc.reporters.StoreReporter +import CodeGenTools._ +import scala.tools.partest.ASMConverters +import ASMConverters._ + +import scala.tools.testing.ClearAfterClass + +object IndySammyTest extends ClearAfterClass.Clearable { + var _compiler = newCompiler() + + def compile(scalaCode: String, javaCode: List[(String, String)] = Nil, allowMessage: StoreReporter#Info => Boolean = _ => false): List[ClassNode] = + compileClasses(_compiler)(scalaCode, javaCode, allowMessage) + + def clear(): Unit = { _compiler = null } +} + +@RunWith(classOf[JUnit4]) +class IndySammyTest extends ClearAfterClass { + ClearAfterClass.stateToClear = IndySammyTest + import IndySammyTest._ + + val compiler = _compiler + + def funClassName(from: String, to: String) = s"Fun$from$to" + def classPrologue(from: String, to: String) = + "class VC(private val i: Int) extends AnyVal\n" + + s"trait ${funClassName(from, to)} { def apply(a: $from): $to}" + + def lamDef(from: String, to: String, body: String => String) = + s"""def lam = (x => ${body("x")}): ${funClassName(from, to)}""" + + def appDef(arg: String) = s"""def app = lam($arg)""" + + /* Create a lambda of type "$from => $to" (with body "$body(x)" if "x" is the argument name), + * and apply it to `arg`. + * + * Check: + * - the signature of the apply method + * - the instructions in the lambda's body (anonfun method) + * - the instructions used to create the argument for the application + * (and the return corresponding to the lambda's result type) + */ + def test(from: String, to: String, arg: String, body: String => String = x => x) + (expectedSig: String, lamBody: List[Instruction], appArgs: List[Instruction], ret: Instruction) + (allowMessage: StoreReporter#Info => Boolean = _ => false) = { + val cls = compile(s"${classPrologue(from, to)}") + val methodNodes = compileMethods(compiler)(lamDef(from, to, body) +";"+ appDef(arg), allowMessage) + + val applySig = cls.head.methods.get(0).desc + val anonfun = methodNodes.find(_.name contains "$anonfun$").map(convertMethod).get + val lamInsn = methodNodes.find(_.name == "lam").map(instructionsFromMethod).get.dropNonOp + val applyInvoke = methodNodes.find(_.name == "app").map(convertMethod).get + + assertEquals(expectedSig, applySig) + assert(lamInsn.length == 2 && lamInsn.head.isInstanceOf[InvokeDynamic], lamInsn) + assertSameCode(anonfun, lamBody) + assertSameCode(applyInvoke, List( + VarOp(ALOAD, 0), + Invoke(INVOKEVIRTUAL, "C", "lam", s"()L${funClassName(from, to)};", false)) ++ appArgs ++ List( + Invoke(INVOKEINTERFACE, funClassName(from, to), "apply", applySig, true), ret) + ) + } + + // x => x : VC => VC applied to VC(1) + @Test + def testVC_VC_VC = + test("VC", "VC", "new VC(1)")("(I)I", + List(VarOp(ILOAD, 0), Op(IRETURN)), + List(Op(ICONST_1)), + Op(IRETURN))() + + // x => new VC(x) : Int => VC applied to 1 + @Test + def testInt_VC_1 = + test("Int", "VC", "1", x => s"new VC($x)")("(I)I", + List(VarOp(ILOAD, 0), Op(IRETURN)), + List(Op(ICONST_1)), + Op(IRETURN))() + + // x => x : VC => Int applied to VC(1) + @Test + def testVC_Int_VC = + test("VC", "Int", "new VC(1)", x => "1")("(I)I", + List(Op(ICONST_1), Op(IRETURN)), + List(Op(ICONST_1)), + Op(IRETURN))() + + // x => new VC(1) : VC => Any applied to VC(1) + @Test + def testVC_Any_VC = + test("VC", "Any", "new VC(1)", x => s"new VC(1)")("(I)Ljava/lang/Object;", + List(TypeOp(NEW, "VC"), Op(DUP), Op(ICONST_1), Invoke(INVOKESPECIAL, "VC", "<init>", "(I)V", false), Op(ARETURN)), + List(Op(ICONST_1)), + Op(ARETURN))() + + + // x => x : VC => Unit applied to VC(1) + @Test + def testVC_Unit_VC = + test("VC", "Unit", "new VC(1)")("(I)V", + List(VarOp(ILOAD, 0), Op(POP), Op(RETURN)), + List(Op(ICONST_1)), + Op(RETURN))(allowMessage = _.msg.contains("pure expression")) + + // x => new VC(x.asInstanceOf[Int]) : Any => VC applied to 1 + // + // Scala: + // def lam = (x => new VC(x.asInstanceOf[Int])): FunAny_VC + // def app = lam(1) + // Java: + // FunAny_VC lam() { return x -> BoxesRunTime.unboxToInt((Object)x); } + // int app() { lam().apply(BoxesRunTime.boxToInteger((int)1)); + @Test + def testAny_VC_1 = + test("Any", "VC", "1", x => s"new VC($x.asInstanceOf[Int])")("(Ljava/lang/Object;)I", + List(VarOp(ALOAD, 0), Invoke(INVOKESTATIC, "scala/runtime/BoxesRunTime", "unboxToInt", "(Ljava/lang/Object;)I", false), Op(IRETURN)), + List(Op(ICONST_1), Invoke(INVOKESTATIC, "scala/runtime/BoxesRunTime", "boxToInteger", "(I)Ljava/lang/Integer;", false)), + Op(IRETURN))() + +} |