summaryrefslogblamecommitdiff
path: root/test/junit/scala/tools/nsc/backend/jvm/IndySammyTest.scala
blob: 5c2ab6a2c7859d5a90e94e9471982f11a50f28ab (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
















                                              


                                             
 


                                                                                                                                                     








































                                                                                                         
















                                                                                                                          























































                                                                                                                                           






                                                  





                                                                                                 
 
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


@RunWith(classOf[JUnit4])
class IndySammyTest extends ClearAfterClass {

  val compiler = cached("compiler", () => newCompiler())
  def compile(scalaCode: String, javaCode: List[(String, String)] = Nil, allowMessage: StoreReporter#Info => Boolean = _ => false): List[ClassNode] =
    compileClasses(compiler)(scalaCode, javaCode, allowMessage)

  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)
  }
}