summaryrefslogtreecommitdiff
path: root/test/junit
diff options
context:
space:
mode:
Diffstat (limited to 'test/junit')
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala4
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala110
2 files changed, 94 insertions, 20 deletions
diff --git a/test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala b/test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala
index f9a55bb26e..e57e95bac4 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala
@@ -16,8 +16,8 @@ object DefaultMethodTest extends ClearAfterClass.Clearable {
}
class DefaultMethodTest extends ClearAfterClass {
- ClearAfterClass.stateToClear = DirectCompileTest
- val compiler = DirectCompileTest.compiler
+ ClearAfterClass.stateToClear = DefaultMethodTest
+ val compiler = DefaultMethodTest.compiler
@Test
def defaultMethodsViaGenBCode(): Unit = {
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
index 1108a37266..81c6dd2ce2 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
@@ -103,8 +103,7 @@ class InlinerTest extends ClearAfterClass {
callsitePosition = NoPosition),
post = post)
- // inline first invocation of f into g in class C
- def inlineTest(code: String, mod: ClassNode => Unit = _ => ()): (MethodNode, List[CannotInlineWarning]) = {
+ def inlineRequest(code: String, mod: ClassNode => Unit = _ => ()): (inlinerHeuristics.InlineRequest, MethodNode) = {
val List(cls) = compile(code)
mod(cls)
val clsBType = classBTypeFromParsedClassfile(cls.name)
@@ -123,9 +122,19 @@ class InlinerTest extends ClearAfterClass {
callsiteStackHeight = analyzer.frameAt(fCall).getStackSize,
receiverKnownNotNull = true
)
+ (request, g)
+ }
+
+ // inline first invocation of f into g in class C
+ def inlineTest(code: String, mod: ClassNode => Unit = _ => ()): MethodNode = {
+ val (request, g) = inlineRequest(code, mod)
+ inliner.inline(request)
+ g
+ }
- val r = inliner.inline(request)
- (g, r)
+ def canInlineTest(code: String, mod: ClassNode => Unit = _ => ()): Option[OptimizerWarning] = {
+ val cs = inlineRequest(code, mod)._1.callsite
+ inliner.earlyCanInlineCheck(cs) orElse inliner.canInlineBody(cs)
}
@Test
@@ -137,7 +146,7 @@ class InlinerTest extends ClearAfterClass {
|}
""".stripMargin
- val (g, _) = inlineTest(code)
+ val g = inlineTest(code)
val gConv = convertMethod(g)
assertSameCode(gConv.instructions.dropNonOp,
@@ -171,7 +180,7 @@ class InlinerTest extends ClearAfterClass {
// See also discussion around ATHROW in BCodeBodyBuilder
- val (g, _) = inlineTest(code)
+ val g = inlineTest(code)
val expectedInlined = List(
VarOp(ALOAD, 0), VarOp(ASTORE, 1), // store this
Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "$qmark$qmark$qmark", "()Lscala/runtime/Nothing$;", false)) // inlined call to ???
@@ -192,11 +201,11 @@ class InlinerTest extends ClearAfterClass {
|}
""".stripMargin
- val (_, can) = inlineTest(code, cls => {
+ val can = canInlineTest(code, cls => {
val f = cls.methods.asScala.find(_.name == "f").get
f.access |= ACC_SYNCHRONIZED
})
- assert(can.length == 1 && can.head.isInstanceOf[SynchronizedMethod], can)
+ assert(can.nonEmpty && can.get.isInstanceOf[SynchronizedMethod], can)
}
@Test
@@ -207,7 +216,7 @@ class InlinerTest extends ClearAfterClass {
| def g = f + 1
|}
""".stripMargin
- val (_, r) = inlineTest(code)
+ val r = canInlineTest(code)
assert(r.isEmpty, r)
}
@@ -221,8 +230,8 @@ class InlinerTest extends ClearAfterClass {
| def g = println(f)
|}
""".stripMargin
- val (_, r) = inlineTest(code)
- assert(r.length == 1 && r.head.isInstanceOf[MethodWithHandlerCalledOnNonEmptyStack], r)
+ val r = canInlineTest(code)
+ assert(r.nonEmpty && r.get.isInstanceOf[MethodWithHandlerCalledOnNonEmptyStack], r)
}
@Test
@@ -264,9 +273,8 @@ class InlinerTest extends ClearAfterClass {
receiverKnownNotNull = true
)
- val r = inliner.inline(request)
-
- assert(r.length == 1 && r.head.isInstanceOf[IllegalAccessInstruction], r)
+ val r = inliner.canInlineBody(request.callsite)
+ assert(r.nonEmpty && r.get.isInstanceOf[IllegalAccessInstruction], r)
}
@Test
@@ -421,8 +429,9 @@ class InlinerTest extends ClearAfterClass {
receiverKnownNotNull = false
)
- val r = inliner.inline(request)
- assert(r.isEmpty, r)
+ val warning = inliner.canInlineBody(request.callsite)
+ assert(warning.isEmpty, warning)
+ inliner.inline(request)
val ins = instructionsFromMethod(f)
// no invocations, lowestOneBit is inlined
@@ -1096,10 +1105,11 @@ class InlinerTest extends ClearAfterClass {
post = List(inlinerHeuristics.PostInlineRequest(fCall, Nil))
)
- val r = inliner.inline(request)
+ val warning = inliner.canInlineBody(request.callsite)
+ assert(warning.isEmpty, warning)
+ inliner.inline(request)
assertNoInvoke(getSingleMethod(c, "h")) // no invoke in h: first g is inlined, then the inlined call to f is also inlined
assertInvoke(getSingleMethod(c, "g"), "C", "f") // g itself still has the call to f
- assert(r.isEmpty, r)
}
/**
@@ -1134,4 +1144,68 @@ class InlinerTest extends ClearAfterClass {
assertInvoke(getSingleMethod(c, "t4"), "scala/Function1", "apply$mcII$sp")
assertInvoke(getSingleMethod(c, "t5"), "C", "h")
}
+
+ @Test
+ def twoStepNoInlineHandler(): Unit = {
+ val code =
+ """class C {
+ | @inline final def f = try 1 catch { case _: Throwable => 2 }
+ | @inline final def g = f
+ | def t = println(g) // cannot inline g onto non-empty stack once that f was inlined into g
+ |}
+ """.stripMargin
+
+ val warn =
+ """C::g()I is annotated @inline but could not be inlined:
+ |The operand stack at the callsite in C::t()V contains more values than the
+ |arguments expected by the callee C::g()I. These values would be discarded
+ |when entering an exception handler declared in the inlined method.""".stripMargin
+
+ val List(c) = compile(code, allowMessage = _.msg contains warn)
+ assertInvoke(getSingleMethod(c, "t"), "C", "g")
+ }
+
+ @Test
+ def twoStepNoInlinePrivate(): Unit = {
+ val code =
+ """class C {
+ | @inline final def g = {
+ | @noinline def f = 0
+ | f
+ | }
+ | @inline final def h = g // after inlining g, h has an invocate of private method f$1
+ |}
+ |class D {
+ | def t(c: C) = c.h // cannot inline
+ |}
+ """.stripMargin
+
+ val warn =
+ """C::h()I is annotated @inline but could not be inlined:
+ |The callee C::h()I contains the instruction INVOKESPECIAL C.f$1 ()I
+ |that would cause an IllegalAccessError when inlined into class D.""".stripMargin
+
+ val List(c, d) = compile(code, allowMessage = _.msg contains warn)
+ assertInvoke(getSingleMethod(c, "h"), "C", "f$1")
+ assertInvoke(getSingleMethod(d, "t"), "C", "h")
+ }
+
+ @Test
+ def twoStepInlinePrivate(): Unit = {
+ val code =
+ """class C {
+ | @inline final def g = { // initially, g invokes the private method f$1, but then f$1 is inlined
+ | @inline def f = 0
+ | f
+ | }
+ |}
+ |class D {
+ | def t(c: C) = c.g // can inline
+ |}
+ """.stripMargin
+
+ val List(c, d) = compile(code)
+ assertNoInvoke(getSingleMethod(c, "g"))
+ assertNoInvoke(getSingleMethod(d, "t"))
+ }
}