summaryrefslogtreecommitdiff
path: root/test/junit/scala/tools/nsc
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2015-01-20 23:20:26 +0100
committerLukas Rytz <lukas.rytz@gmail.com>2015-03-11 12:53:35 -0700
commit4e982451decdc3821febfe975e1b8e406a3741e8 (patch)
tree95438438c6cd166b68418e765d8d599bb6a91573 /test/junit/scala/tools/nsc
parent37c91654433a12249ae125b9454ba17cef103327 (diff)
downloadscala-4e982451decdc3821febfe975e1b8e406a3741e8.tar.gz
scala-4e982451decdc3821febfe975e1b8e406a3741e8.tar.bz2
scala-4e982451decdc3821febfe975e1b8e406a3741e8.zip
Don't crash the inliner in mixed compilation
In mixed compilation, the bytecode of Java classes is not availalbe: the Scala compiler does not produce any, and there are no classfiles yet. When inlining a (Scala defined) method that contains an invocation to a Java method, we need the Java method's bytecode in order to check whether that invocation can be transplanted to the new location without causing an IllegalAccessError. If the bytecode cannot be found, inlining won't be allowed.
Diffstat (limited to 'test/junit/scala/tools/nsc')
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala2
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala4
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala43
3 files changed, 42 insertions, 7 deletions
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala
index 65c96226ff..f7c9cab284 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala
@@ -90,7 +90,7 @@ class BTypesFromClassfileTest {
clearCache()
val fromSymbol = classBTypeFromSymbol(classSym)
clearCache()
- val fromClassfile = bTypes.classBTypeFromParsedClassfile(fromSymbol.internalName)
+ val fromClassfile = bTypes.classBTypeFromParsedClassfile(fromSymbol.internalName).get
sameBType(fromSymbol, fromClassfile)
}
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 40dc990c0f..ef0f6bcd77 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)))
+ test(inliner.findIllegalAccess(m.instructions, classBTypeFromParsedClassfile(classNode.name).get))
}
check(cClass, assertEmpty)
@@ -153,7 +153,7 @@ 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)))
+ test(inliner.findIllegalAccess(method.instructions, classBTypeFromParsedClassfile(dest.name).get))
}
val cOrDOwner = (_: Option[AbstractInsnNode] @unchecked) match {
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 240d106f5c..4e7a2399a2 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
@@ -6,12 +6,15 @@ import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.Test
import scala.collection.generic.Clearable
+import scala.collection.mutable.ListBuffer
+import scala.reflect.internal.util.BatchSourceFile
import scala.tools.asm.Opcodes._
import org.junit.Assert._
import scala.tools.asm.tree._
import scala.tools.asm.tree.analysis._
import scala.tools.nsc.backend.jvm.opt.BytecodeUtils.BasicAnalyzer
+import scala.tools.nsc.io._
import scala.tools.testing.AssertUtil._
import CodeGenTools._
@@ -57,7 +60,7 @@ class InlinerTest extends ClearAfterClass {
def inlineTest(code: String, mod: ClassNode => Unit = _ => ()): (MethodNode, Option[String]) = {
val List(cls) = compile(code)
mod(cls)
- val clsBType = classBTypeFromParsedClassfile(cls.name)
+ val clsBType = classBTypeFromParsedClassfile(cls.name).get
val List(f, g) = cls.methods.asScala.filter(m => Set("f", "g")(m.name)).toList.sortBy(_.name)
val fCall = g.instructions.iterator.asScala.collect({ case i: MethodInsnNode if i.name == "f" => i }).next()
@@ -191,8 +194,8 @@ class InlinerTest extends ClearAfterClass {
val List(c, d) = compile(code)
- val cTp = classBTypeFromParsedClassfile(c.name)
- val dTp = classBTypeFromParsedClassfile(d.name)
+ val cTp = classBTypeFromParsedClassfile(c.name).get
+ val dTp = classBTypeFromParsedClassfile(d.name).get
val g = c.methods.asScala.find(_.name == "g").get
val h = d.methods.asScala.find(_.name == "h").get
@@ -350,7 +353,7 @@ class InlinerTest extends ClearAfterClass {
val List(c) = compile(code)
val f = c.methods.asScala.find(_.name == "f").get
val callsiteIns = f.instructions.iterator().asScala.collect({ case c: MethodInsnNode => c }).next()
- val clsBType = classBTypeFromParsedClassfile(c.name)
+ val clsBType = classBTypeFromParsedClassfile(c.name).get
val analyzer = new BasicAnalyzer(f, clsBType.internalName)
val integerClassBType = classBTypeFromInternalName("java/lang/Integer")
@@ -414,4 +417,36 @@ class InlinerTest extends ClearAfterClass {
// like maxLocals for g1 / f1, but no return value
assert(g2.maxLocals == 4 && f2.maxLocals == 3, s"${g2.maxLocals} - ${f2.maxLocals}")
}
+
+ @Test
+ def mixedCompilationNoInline(): Unit = {
+ // The inliner checks if the invocation `A.bar` can be safely inlined. For that it needs to have
+ // the bytecode of the invoked method. In mixed compilation, there's no classfile available for
+ // A, so `flop` cannot be inlined, we cannot check if it's safe.
+
+ val javaCode =
+ """public class A {
+ | public static final int bar() { return 100; }
+ |}
+ """.stripMargin
+
+ val scalaCode =
+ """class B {
+ | @inline final def flop = A.bar
+ | def g = flop
+ |}
+ """.stripMargin
+
+ InlinerTest.notPerRun.foreach(_.clear())
+ compiler.reporter.reset()
+ compiler.settings.outputDirs.setSingleOutput(new VirtualDirectory("(memory)", None))
+ val run = new compiler.Run()
+ run.compileSources(List(new BatchSourceFile("A.java", javaCode), new BatchSourceFile("B.scala", scalaCode)))
+ val outDir = compiler.settings.outputDirs.getSingleOutput.get
+
+ val List(b) = outDir.iterator.map(f => AsmUtils.readClass(f.toByteArray)).toList.sortBy(_.name)
+ val ins = getSingleMethod(b, "g").instructions
+ val invokeFlop = Invoke(INVOKEVIRTUAL, "B", "flop", "()I", false)
+ assert(ins contains invokeFlop, ins mkString "\n")
+ }
}