diff options
Diffstat (limited to 'test/junit/scala/tools/nsc/backend/jvm/IndySammyTest.scala')
-rw-r--r-- | test/junit/scala/tools/nsc/backend/jvm/IndySammyTest.scala | 160 |
1 files changed, 160 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..b9e45a7dc9 --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/IndySammyTest.scala @@ -0,0 +1,160 @@ +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) + ) + } + +// def testSpecial(lam: String, lamTp: String, arg: String)(allowMessage: StoreReporter#Info => Boolean = _ => false) = { +// val cls = compile("trait Special[@specialized A] { def apply(a: A): A}" ) +// val methodNodes = compileMethods(compiler)(s"def lam : $lamTp = $lam" +";"+ appDef(arg), allowMessage) +// +// val anonfun = methodNodes.filter(_.name contains "$anonfun$").map(convertMethod) +// val lamInsn = methodNodes.find(_.name == "lam").map(instructionsFromMethod).get.dropNonOp +// val applyInvoke = methodNodes.find(_.name == "app").map(convertMethod).get +// +// 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))() + + // TODO + // x => x : Special[Int] applied to 1 +// @Test +// def testSpecial_Int_1 = +// testSpecial("x => x", "Special[Int]", "1")() + + + // Tests ThisReferringMethodsTraverser + @Test + def testStaticIfNoThisReference: Unit = { + val methodNodes = compileMethods(compiler)("def foo = () => () => () => 42") + methodNodes.forall(m => !m.name.contains("anonfun") || (m.access & ACC_STATIC) == ACC_STATIC) + } +} |