diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2015-03-12 15:32:46 -0700 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2015-03-12 16:34:21 -0700 |
commit | c2ab768287cc02b5e01342ac993d6c2b6e7ee2aa (patch) | |
tree | 8f6eb1d2132585be7f869b488e195149b03bd461 /test | |
parent | ba325127141e70e3464be80fddc699e23b638a3d (diff) | |
download | scala-c2ab768287cc02b5e01342ac993d6c2b6e7ee2aa.tar.gz scala-c2ab768287cc02b5e01342ac993d6c2b6e7ee2aa.tar.bz2 scala-c2ab768287cc02b5e01342ac993d6c2b6e7ee2aa.zip |
Don't inline methods containing super calls into other classes
Method bodies that contain a super call cannot be inlined into other
classes. The same goes for methods containing calls to private
methods, but that was already ensured before by accessibility checks.
The last case of `invokespecial` instructions is constructor calls.
Those can be safely moved into different classes (as long as the
constructor is accessible at the new location).
Note that scalac never emits methods / constructors as private in
bytecode.
Diffstat (limited to 'test')
-rw-r--r-- | test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala | 26 | ||||
-rw-r--r-- | test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala | 48 |
2 files changed, 61 insertions, 13 deletions
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala index 03d2f2f108..b4839dcec8 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala @@ -59,7 +59,7 @@ class InlinerIllegalAccessTest extends ClearAfterClass { def check(classNode: ClassNode, test: Option[AbstractInsnNode] => Unit) = { for (m <- methods) - test(inliner.findIllegalAccess(m.instructions, classBTypeFromParsedClassfile(classNode.name)).map(_._1)) + test(inliner.findIllegalAccess(m.instructions, classBTypeFromParsedClassfile(cClass.name), classBTypeFromParsedClassfile(classNode.name)).map(_._1)) } check(cClass, assertEmpty) @@ -152,8 +152,8 @@ class InlinerIllegalAccessTest extends ClearAfterClass { val List(rbD, rcD, rfD, rgD) = dCl.methods.asScala.toList.filter(_.name(0) == 'r').sortBy(_.name) - def check(method: MethodNode, dest: ClassNode, test: Option[AbstractInsnNode] => Unit): Unit = { - test(inliner.findIllegalAccess(method.instructions, classBTypeFromParsedClassfile(dest.name)).map(_._1)) + def check(method: MethodNode, decl: ClassNode, dest: ClassNode, test: Option[AbstractInsnNode] => Unit): Unit = { + test(inliner.findIllegalAccess(method.instructions, classBTypeFromParsedClassfile(decl.name), classBTypeFromParsedClassfile(dest.name)).map(_._1)) } val cOrDOwner = (_: Option[AbstractInsnNode] @unchecked) match { @@ -164,35 +164,35 @@ class InlinerIllegalAccessTest extends ClearAfterClass { // PUBLIC // public methods allowed everywhere - for (m <- Set(raC, reC); c <- allClasses) check(m, c, assertEmpty) + for (m <- Set(raC, reC); c <- allClasses) check(m, cCl, c, assertEmpty) // DEFAULT ACCESS // default access OK in same package - for (m <- Set(rbC, rfC, rbD, rfD); c <- allClasses) { - if (c.name startsWith "a/") check(m, c, assertEmpty) - else check(m, c, cOrDOwner) + for ((m, declCls) <- Set((rbC, cCl), (rfC, cCl), (rbD, dCl), (rfD, dCl)); c <- allClasses) { + if (c.name startsWith "a/") check(m, declCls, c, assertEmpty) + else check(m, declCls, c, cOrDOwner) } // PROTECTED // protected accessed in same class, or protected static accessed in subclass(rgD). // can be inlined to subclasses, and classes in the same package (gCl) - for (m <- Set(rcC, rgC, rgD); c <- Set(cCl, dCl, eCl, fCl, gCl, hCl)) check(m, c, assertEmpty) + for ((m, declCls) <- Set((rcC, cCl), (rgC, cCl), (rgD, dCl)); c <- Set(cCl, dCl, eCl, fCl, gCl, hCl)) check(m, declCls, c, assertEmpty) // protected in non-subclass and different package - for (m <- Set(rcC, rgC)) check(m, iCl, cOrDOwner) + for (m <- Set(rcC, rgC)) check(m, cCl, iCl, cOrDOwner) // non-static protected accessed in subclass (rcD). can be inlined to related class, or classes in the same package - for (c <- Set(cCl, dCl, eCl, fCl, gCl)) check(rcD, c, assertEmpty) + for (c <- Set(cCl, dCl, eCl, fCl, gCl)) check(rcD, dCl, c, assertEmpty) // rcD cannot be inlined into non-related classes, if the declaration and destination are not in the same package - for (c <- Set(hCl, iCl)) check(rcD, c, cOrDOwner) + for (c <- Set(hCl, iCl)) check(rcD, dCl, c, cOrDOwner) // PRIVATE // privated method accesses can only be inlined in the same class - for (m <- Set(rdC, rhC)) check(m, cCl, assertEmpty) - for (m <- Set(rdC, rhC); c <- allClasses.tail) check(m, c, cOrDOwner) + for (m <- Set(rdC, rhC)) check(m, cCl, cCl, assertEmpty) + for (m <- Set(rdC, rhC); c <- allClasses.tail) check(m, cCl, c, cOrDOwner) } } 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 0581bd24fe..39fb28570e 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala @@ -902,4 +902,52 @@ class InlinerTest extends ClearAfterClass { allowMessage = i => {c += 1; i.msg contains warn}) assert(c == 1, c) } + + @Test + def inlineInvokeSpecial(): Unit = { + val code = + """class Aa { + | def f1 = 0 + |} + |class B extends Aa { + | @inline final override def f1 = 1 + super.f1 // invokespecial Aa.f1 + | + | private def f2m = 0 // public B$$f2m in bytecode + | @inline final def f2 = f2m // invokevirtual B.B$$f2m + | + | private def this(x: Int) = this() // public in bytecode + | @inline final def f3 = new B() // invokespecial B.<init>() + | @inline final def f4 = new B(1) // invokespecial B.<init>(I) + | + | def t1 = f1 // inlined + | def t2 = f2 // inlined + | def t3 = f3 // inlined + | def t4 = f4 // inlined + |} + |class T { + | def t1(b: B) = b.f1 // cannot inline: contains a super call + | def t2(b: B) = b.f2 // inlined + | def t3(b: B) = b.f3 // inlined + | def t4(b: B) = b.f4 // inlined + |} + """.stripMargin + + val warn = + """B::f1()I is annotated @inline but could not be inlined: + |The callee B::f1()I contains the instruction INVOKESPECIAL Aa.f1 ()I + |that would cause an IllegalAccessError when inlined into class T.""".stripMargin + var c = 0 + val List(a, b, t) = compile(code, allowMessage = i => {c += 1; i.msg contains warn}) + assert(c == 1, c) + + assertInvoke(getSingleMethod(b, "t1"), "Aa", "f1") + assertInvoke(getSingleMethod(b, "t2"), "B", "B$$f2m") + assertInvoke(getSingleMethod(b, "t3"), "B", "<init>") + assertInvoke(getSingleMethod(b, "t4"), "B", "<init>") + + assertInvoke(getSingleMethod(t, "t1"), "B", "f1") + assertInvoke(getSingleMethod(t, "t2"), "B", "B$$f2m") + assertInvoke(getSingleMethod(t, "t3"), "B", "<init>") + assertInvoke(getSingleMethod(t, "t4"), "B", "<init>") + } } |