summaryrefslogtreecommitdiff
path: root/test/junit
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2016-06-06 14:24:38 +1000
committerAdriaan Moors <adriaan.moors@typesafe.com>2016-06-28 09:18:34 -0700
commit7d51b3fd1569917cb804363bd418466a306f5c89 (patch)
treeede84e6a0dda8750276d7c0986ffc6d15c9fb1dc /test/junit
parent91b066aac5edf53ca18603f8486eb255514b3118 (diff)
downloadscala-7d51b3fd1569917cb804363bd418466a306f5c89.tar.gz
scala-7d51b3fd1569917cb804363bd418466a306f5c89.tar.bz2
scala-7d51b3fd1569917cb804363bd418466a306f5c89.zip
Emit trait method bodies in statics
And use this as the target of the default methods or statically resolved super or $init calls. The call-site change is predicated on `-Yuse-trait-statics` as a stepping stone for experimentation / bootstrapping. I have performed this transformation in the backend, rather than trying to reflect this in the view from Scala symbols + ASTs. We also need to add an restriction related to invokespecial to Java parents: to support a super call to one of these to implement a super accessor, the interface must be listed as a direct parent of the class. The static method names has a trailing $ added to avoid duplicate name and signature errors in classfiles.
Diffstat (limited to 'test/junit')
-rw-r--r--test/junit/scala/lang/traits/BytecodeTest.scala9
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala5
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala4
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala4
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala35
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala23
-rw-r--r--test/junit/scala/tools/testing/BytecodeTesting.scala15
7 files changed, 67 insertions, 28 deletions
diff --git a/test/junit/scala/lang/traits/BytecodeTest.scala b/test/junit/scala/lang/traits/BytecodeTest.scala
index f47fc9c127..ec8508df99 100644
--- a/test/junit/scala/lang/traits/BytecodeTest.scala
+++ b/test/junit/scala/lang/traits/BytecodeTest.scala
@@ -9,6 +9,7 @@ import scala.collection.JavaConverters._
import scala.tools.asm.Opcodes
import scala.tools.asm.Opcodes._
import scala.tools.asm.tree.ClassNode
+import scala.tools.nsc.backend.jvm.opt.BytecodeUtils
import scala.tools.partest.ASMConverters._
import scala.tools.testing.BytecodeTesting
import scala.tools.testing.BytecodeTesting._
@@ -18,8 +19,8 @@ class BytecodeTest extends BytecodeTesting {
import compiler._
def checkForwarder(classes: Map[String, ClassNode], clsName: Symbol, target: String) = {
- val List(f) = getMethods(classes(clsName.name), "f")
- assertSameCode(f, List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, target, "f", "()I", false), Op(IRETURN)))
+ val f = getMethod(classes(clsName.name), "f")
+ assertSameCode(f, List(VarOp(ALOAD, 0), Invoke(INVOKESTATIC, target, "f$", s"(L$target;)I", true), Op(IRETURN)))
}
@Test
@@ -88,7 +89,7 @@ class BytecodeTest extends BytecodeTesting {
assertSameSummary(getMethod(c("C18"), "f"), List(BIPUSH, IRETURN))
checkForwarder(c, 'C19, "T7")
assertSameCode(getMethod(c("C19"), "T7$$super$f"), List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, "C18", "f", "()I", false), Op(IRETURN)))
- assertInvoke(getMethod(c("C20"), "clone"), "T8", "clone") // mixin forwarder
+ assertInvoke(getMethod(c("C20"), "clone"), "T8", "clone$") // mixin forwarder
}
@Test
@@ -141,7 +142,7 @@ class BytecodeTest extends BytecodeTesting {
def invocationReceivers(): Unit = {
val List(c1, c2, t, u) = compileClasses(invocationReceiversTestCode.definitions("Object"))
// mixin forwarder in C1
- assertSameCode(getMethod(c1, "clone"), List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, "T", "clone", "()Ljava/lang/Object;", false), Op(ARETURN)))
+ assertSameCode(getMethod(c1, "clone"), List(VarOp(ALOAD, 0), Invoke(INVOKESTATIC, "T", "clone$", "(LT;)Ljava/lang/Object;", true), Op(ARETURN)))
assertInvoke(getMethod(c1, "f1"), "T", "clone")
assertInvoke(getMethod(c1, "f2"), "T", "clone")
assertInvoke(getMethod(c1, "f3"), "C1", "clone")
diff --git a/test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala b/test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala
index c9a958ee4f..841e850b49 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala
@@ -5,6 +5,7 @@ import org.junit.Test
import scala.collection.JavaConverters
import scala.collection.JavaConverters._
+import scala.reflect.internal.Flags
import scala.tools.asm.Opcodes
import scala.tools.asm.tree.ClassNode
import scala.tools.testing.BytecodeTesting
@@ -21,7 +22,7 @@ class DefaultMethodTest extends BytecodeTesting {
/** Transforms a single tree. */
override def transform(tree: global.Tree): global.Tree = tree match {
case dd @ DefDef(_, Foo, _, _, _, _) =>
- dd.symbol.setFlag(reflect.internal.Flags.JAVA_DEFAULTMETHOD)
+ dd.symbol.setFlag(Flags.JAVA_DEFAULTMETHOD).resetFlag(Flags.DEFERRED)
copyDefDef(dd)(rhs = Literal(Constant(1)).setType(definitions.IntTpe))
case _ => super.transform(tree)
}
@@ -31,6 +32,4 @@ class DefaultMethodTest extends BytecodeTesting {
assertTrue("default method should not be abstract", (foo.access & Opcodes.ACC_ABSTRACT) == 0)
assertTrue("default method body emitted", foo.instructions.size() > 0)
}
-
-
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala b/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala
index a28599cd92..38285fbce1 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala
@@ -1,7 +1,9 @@
package scala.tools.nsc.backend.jvm
+import java.nio.file.{Files, Paths}
+
import org.junit.Assert._
-import org.junit.Test
+import org.junit.{Ignore, Test}
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala
index a2513cacdc..85df42e069 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala
@@ -97,7 +97,7 @@ class InlinerSeparateCompilationTest {
""".stripMargin
val List(a, t) = compileClassesSeparately(List(codeA, assembly), args)
- assertNoInvoke(getMethod(t, "f"))
- assertNoInvoke(getMethod(a, "n"))
+ assertNoInvoke(getMethod(t, "f$"))
+ assertNoInvoke(getMethod(a, "n$"))
}
}
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 9173a1d189..f531ce9322 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
@@ -475,11 +475,9 @@ class InlinerTest extends BytecodeTesting {
| def t2 = this.f
|}
""".stripMargin
- val warns = Set(
- "C::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden",
- "T::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden")
+ val warn = "T::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden"
var count = 0
- val List(c, t) = compile(code, allowMessage = i => {count += 1; warns.exists(i.msg contains _)})
+ val List(c, t) = compile(code, allowMessage = i => {count += 1; i.msg contains warn})
assert(count == 2, count)
assertInvoke(getMethod(c, "t1"), "T", "f")
assertInvoke(getMethod(c, "t2"), "C", "f")
@@ -520,7 +518,7 @@ class InlinerTest extends BytecodeTesting {
val List(c, oMirror, oModule, t) = compile(code, allowMessage = i => {count += 1; i.msg contains warn})
assert(count == 1, count)
- assertNoInvoke(getMethod(t, "f"))
+ assertNoInvoke(getMethod(t, "f$"))
assertNoInvoke(getMethod(c, "t1"))
assertNoInvoke(getMethod(c, "t2"))
@@ -546,9 +544,9 @@ class InlinerTest extends BytecodeTesting {
val List(assembly, c, t) = compile(code)
- assertNoInvoke(getMethod(t, "f"))
+ assertNoInvoke(getMethod(t, "f$"))
- assertNoInvoke(getMethod(assembly, "n"))
+ assertNoInvoke(getMethod(assembly, "n$"))
assertNoInvoke(getMethod(c, "t1"))
assertNoInvoke(getMethod(c, "t2"))
@@ -624,8 +622,8 @@ class InlinerTest extends BytecodeTesting {
val List(ca, cb, t1, t2a, t2b) = compile(code, allowMessage = i => {count += 1; i.msg contains warning})
assert(count == 4, count) // see comments, f is not inlined 4 times
- assertNoInvoke(getMethod(t2a, "g2a"))
- assertInvoke(getMethod(t2b, "g2b"), "T1", "f")
+ assertNoInvoke(getMethod(t2a, "g2a$"))
+ assertInvoke(getMethod(t2b, "g2b$"), "T1", "f")
assertInvoke(getMethod(ca, "m1a"), "T1", "f")
assertNoInvoke(getMethod(ca, "m2a")) // no invoke, see comment on def g2a
@@ -684,8 +682,8 @@ class InlinerTest extends BytecodeTesting {
|}
""".stripMargin
val List(c, t) = compile(code)
- val t1 = getMethod(t, "t1")
- val t2 = getMethod(t, "t2")
+ val t1 = getMethod(t, "t1$")
+ val t2 = getMethod(t, "t2$")
val cast = TypeOp(CHECKCAST, "C")
Set(t1, t2).foreach(m => assert(m.instructions.contains(cast), m.instructions))
}
@@ -1574,4 +1572,19 @@ class InlinerTest extends BytecodeTesting {
Label(0), LineNumber(9, Label(0)), VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "C", "fx", "()V", false),
Label(4), LineNumber(10, Label(4)), Op(ICONST_1), Op(IRETURN), Label(8)))
}
+
+ @Test
+ def traitHO(): Unit = {
+ val code =
+ """trait T {
+ | def foreach(f: Int => Unit): Unit = f(1)
+ |}
+ |final class C extends T {
+ | def cons(x: Int): Unit = ()
+ | def t1 = foreach(cons)
+ |}
+ """.stripMargin
+ val List(c, t) = compile(code)
+ assertNoIndy(getMethod(c, "t1"))
+ }
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
index 4791a29bfb..54f4c805c1 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
@@ -31,6 +31,14 @@ class ScalaInlineInfoTest extends BytecodeTesting {
r.toString
}
+ def assertSameMethods(c: ClassNode, nameAndSigs: Set[String]): Unit = {
+ val r = new StringBuilder
+ val inClass = c.methods.iterator.asScala.map(m => m.name + m.desc).toSet
+ for (m <- inClass.diff(nameAndSigs)) r.append(s"method in classfile found, but no inline info: $m")
+ for (m <- nameAndSigs.diff(inClass)) r.append(s"inline info found, but no method in classfile: $m")
+ assert(r.isEmpty, r.toString)
+ }
+
@Test
def traitMembersInlineInfo(): Unit = {
val code =
@@ -79,26 +87,32 @@ class ScalaInlineInfoTest extends BytecodeTesting {
("T$$super$toString()Ljava/lang/String;", MethodInlineInfo(true ,false,false)),
("T$_setter_$x1_$eq(I)V", MethodInlineInfo(false,false,false)),
("f1()I", MethodInlineInfo(false,false,false)),
- ("f2()I", MethodInlineInfo(true, false,false)),
+ ("f1$(LT;)I", MethodInlineInfo(true ,false,false)),
+ ("f2()I", MethodInlineInfo(true ,false,false)), // no static impl method for private method f2
("f3()I", MethodInlineInfo(false,false,false)),
+ ("f3$(LT;)I", MethodInlineInfo(true ,false,false)),
("f4()Ljava/lang/String;", MethodInlineInfo(false,true, false)),
+ ("f4$(LT;)Ljava/lang/String;", MethodInlineInfo(true ,true, false)),
("f5()I", MethodInlineInfo(true ,false,false)),
- ("f6()I", MethodInlineInfo(false,false,true )),
+ ("f5$(LT;)I", MethodInlineInfo(true ,false,false)),
+ ("f6()I", MethodInlineInfo(false,false,true )), // no static impl method for abstract method f6
("x1()I", MethodInlineInfo(false,false,false)),
("y2()I", MethodInlineInfo(false,false,false)),
("y2_$eq(I)V", MethodInlineInfo(false,false,false)),
("x3()I", MethodInlineInfo(false,false,false)),
("x3_$eq(I)V", MethodInlineInfo(false,false,false)),
("x4()I", MethodInlineInfo(false,false,false)),
+ ("x4$(LT;)I", MethodInlineInfo(true ,false,false)),
("x5()I", MethodInlineInfo(true, false,false)),
("L$lzycompute$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true, false,false)),
("L$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true ,false,false)),
("nest$1()I", MethodInlineInfo(true, false,false)),
- ("$init$()V", MethodInlineInfo(false,false,false))),
+ ("$init$(LT;)V", MethodInlineInfo(true,false,false))),
None // warning
)
assert(infoT == expectT, mapDiff(expectT.methodInfos, infoT.methodInfos) + infoT)
+ assertSameMethods(t, expectT.methodInfos.keySet)
val infoC = inlineInfo(c)
val expectC = InlineInfo(false, None, Map(
@@ -119,6 +133,7 @@ class ScalaInlineInfoTest extends BytecodeTesting {
None)
assert(infoC == expectC, mapDiff(expectC.methodInfos, infoC.methodInfos) + infoC)
+ assertSameMethods(c, expectC.methodInfos.keySet)
}
@Test
@@ -156,7 +171,6 @@ class ScalaInlineInfoTest extends BytecodeTesting {
("F",None),
("T",Some("h(Ljava/lang/String;)I")),
("U",None)))
-
}
@Test
@@ -169,5 +183,6 @@ class ScalaInlineInfoTest extends BytecodeTesting {
"O$lzycompute()LC$O$;" -> MethodInlineInfo(true,false,false),
"O()LC$O$;" -> MethodInlineInfo(true,false,false))
assert(infoC.methodInfos == expected, mapDiff(infoC.methodInfos, expected))
+ assertSameMethods(c, expected.keySet)
}
}
diff --git a/test/junit/scala/tools/testing/BytecodeTesting.scala b/test/junit/scala/tools/testing/BytecodeTesting.scala
index 4ddb6580df..c0fdb8010f 100644
--- a/test/junit/scala/tools/testing/BytecodeTesting.scala
+++ b/test/junit/scala/tools/testing/BytecodeTesting.scala
@@ -12,6 +12,7 @@ import scala.tools.asm.tree.{AbstractInsnNode, ClassNode, MethodNode}
import scala.tools.cmd.CommandLineParser
import scala.tools.nsc.backend.jvm.AsmUtils
import scala.tools.nsc.backend.jvm.AsmUtils._
+import scala.tools.nsc.backend.jvm.opt.BytecodeUtils
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.reporters.StoreReporter
import scala.tools.nsc.{Global, Settings}
@@ -247,11 +248,19 @@ object BytecodeTesting {
def getAsmMethod(c: ClassNode, name: String): MethodNode = {
val methods = getAsmMethods(c, name)
+ def fail() = {
+ val allNames = getAsmMethods(c, _ => true).map(_.name)
+ throw new AssertionFailedError(s"Could not find method named $name among ${allNames}")
+ }
methods match {
case List(m) => m
- case ms =>
- val allNames = getAsmMethods(c, _ => true).map(_.name)
- throw new AssertionFailedError(s"Could not find method named $name among ${allNames}")
+ case ms @ List(m1, m2) if BytecodeUtils.isInterface(c) =>
+ val (statics, nonStatics) = ms.partition(BytecodeUtils.isStaticMethod)
+ (statics, nonStatics) match {
+ case (List(staticMethod), List(_)) => m1 // prefer the static method of the pair if methods in traits
+ case _ => fail()
+ }
+ case ms => fail()
}
}