summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@typesafe.com>2014-09-17 11:01:20 +0200
committerLukas Rytz <lukas.rytz@typesafe.com>2014-09-17 11:01:20 +0200
commit344151f17b04bf75d6ebafec6aa4229dfe6cbf09 (patch)
tree319c7d297d26d577190c0a977bf03e0a5f3f2870 /test
parentbe825be0b0c5dcbd210c52295022ccee0a859262 (diff)
parent5c6e7b3d6587107bf6f9746c9be8643b53321614 (diff)
downloadscala-344151f17b04bf75d6ebafec6aa4229dfe6cbf09.tar.gz
scala-344151f17b04bf75d6ebafec6aa4229dfe6cbf09.tar.bz2
scala-344151f17b04bf75d6ebafec6aa4229dfe6cbf09.zip
Merge pull request #3987 from retronym/merge/2.11.x-to-2.12.x-20140917
Merge 2.11.x to 2.12.x
Diffstat (limited to 'test')
-rw-r--r--test/files/jvm/t6941/test.scala4
-rw-r--r--test/files/jvm/t7253/test.scala6
-rw-r--r--test/files/jvm/unreachable.flags1
-rw-r--r--test/files/neg/warn-inferred-any.check5
-rw-r--r--test/files/neg/warn-inferred-any.scala8
-rw-r--r--test/files/run/delambdafyLambdaClassNames.check1
-rw-r--r--test/files/run/delambdafyLambdaClassNames.flags1
-rw-r--r--test/files/run/delambdafyLambdaClassNames/A_1.scala5
-rw-r--r--test/files/run/delambdafyLambdaClassNames/Test.scala4
-rw-r--r--test/files/run/nothingTypeDce.flags1
-rw-r--r--test/files/run/nothingTypeDce.scala63
-rw-r--r--test/files/run/nothingTypeNoFramesNoDce.check1
-rw-r--r--test/files/run/nothingTypeNoFramesNoDce.flags1
-rw-r--r--test/files/run/nothingTypeNoFramesNoDce.scala61
-rw-r--r--test/files/run/nothingTypeNoOpt.flags1
-rw-r--r--test/files/run/nothingTypeNoOpt.scala61
-rw-r--r--test/files/run/t8680.scala53
-rw-r--r--test/junit/scala/collection/SetMapConsistencyTest.scala15
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala1
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala79
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala81
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala92
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala217
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala87
-rw-r--r--test/junit/scala/tools/testing/AssertThrowsTest.scala11
25 files changed, 851 insertions, 9 deletions
diff --git a/test/files/jvm/t6941/test.scala b/test/files/jvm/t6941/test.scala
index 248617f71f..fceb54487f 100644
--- a/test/files/jvm/t6941/test.scala
+++ b/test/files/jvm/t6941/test.scala
@@ -1,4 +1,4 @@
-import scala.tools.partest.BytecodeTest
+import scala.tools.partest.{BytecodeTest, ASMConverters}
import scala.tools.nsc.util.JavaClassPath
import java.io.InputStream
@@ -10,6 +10,6 @@ import scala.collection.JavaConverters._
object Test extends BytecodeTest {
def show: Unit = {
val classNode = loadClassNode("SameBytecode")
- similarBytecode(getMethod(classNode, "a"), getMethod(classNode, "b"), equalsModuloVar)
+ similarBytecode(getMethod(classNode, "a"), getMethod(classNode, "b"), ASMConverters.equivalentBytecode(_, _))
}
}
diff --git a/test/files/jvm/t7253/test.scala b/test/files/jvm/t7253/test.scala
index 7fe08e8813..a3f1e86e65 100644
--- a/test/files/jvm/t7253/test.scala
+++ b/test/files/jvm/t7253/test.scala
@@ -1,4 +1,4 @@
-import scala.tools.partest.BytecodeTest
+import scala.tools.partest.{BytecodeTest, ASMConverters}
import scala.tools.nsc.util.JavaClassPath
import java.io.InputStream
@@ -8,10 +8,10 @@ import asm.tree.{ClassNode, InsnList}
import scala.collection.JavaConverters._
object Test extends BytecodeTest {
- import instructions._
+ import ASMConverters._
def show: Unit = {
- val instrBaseSeqs = Seq("ScalaClient_1", "JavaClient_1") map (name => instructions.fromMethod(getMethod(loadClassNode(name), "foo")))
+ val instrBaseSeqs = Seq("ScalaClient_1", "JavaClient_1") map (name => instructionsFromMethod(getMethod(loadClassNode(name), "foo")))
val instrSeqs = instrBaseSeqs map (_ filter isInvoke)
cmpInstructions(instrSeqs(0), instrSeqs(1))
}
diff --git a/test/files/jvm/unreachable.flags b/test/files/jvm/unreachable.flags
deleted file mode 100644
index 49f2d2c4c8..0000000000
--- a/test/files/jvm/unreachable.flags
+++ /dev/null
@@ -1 +0,0 @@
--Ybackend:GenASM
diff --git a/test/files/neg/warn-inferred-any.check b/test/files/neg/warn-inferred-any.check
index 4628033e55..8ad81d1529 100644
--- a/test/files/neg/warn-inferred-any.check
+++ b/test/files/neg/warn-inferred-any.check
@@ -7,6 +7,9 @@ warn-inferred-any.scala:16: warning: a type was inferred to be `AnyVal`; this ma
warn-inferred-any.scala:17: warning: a type was inferred to be `AnyVal`; this may indicate a programming error.
{ 1l to 5l contains 5d }
^
+warn-inferred-any.scala:25: warning: a type was inferred to be `Any`; this may indicate a programming error.
+ def za = f(1, "one")
+ ^
error: No warnings can be incurred under -Xfatal-warnings.
-three warnings found
+four warnings found
one error found
diff --git a/test/files/neg/warn-inferred-any.scala b/test/files/neg/warn-inferred-any.scala
index b853e6e5a8..693c33e7be 100644
--- a/test/files/neg/warn-inferred-any.scala
+++ b/test/files/neg/warn-inferred-any.scala
@@ -17,3 +17,11 @@ trait Ys[+A] {
{ 1l to 5l contains 5d }
{ 1l to 5l contains 5l }
}
+
+trait Zs {
+ def f[A](a: A*) = 42
+ def g[A >: Any](a: A*) = 42 // don't warn
+
+ def za = f(1, "one")
+ def zu = g(1, "one")
+}
diff --git a/test/files/run/delambdafyLambdaClassNames.check b/test/files/run/delambdafyLambdaClassNames.check
new file mode 100644
index 0000000000..d425d15dd0
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames.check
@@ -0,0 +1 @@
+A$$nestedInAnon$1$lambda$$run$1
diff --git a/test/files/run/delambdafyLambdaClassNames.flags b/test/files/run/delambdafyLambdaClassNames.flags
new file mode 100644
index 0000000000..b10233d322
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames.flags
@@ -0,0 +1 @@
+-Ybackend:GenBCode -Ydelambdafy:method \ No newline at end of file
diff --git a/test/files/run/delambdafyLambdaClassNames/A_1.scala b/test/files/run/delambdafyLambdaClassNames/A_1.scala
new file mode 100644
index 0000000000..10489414b7
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames/A_1.scala
@@ -0,0 +1,5 @@
+class A {
+ def f = new Runnable {
+ def run(): Unit = List(1,2).foreach(println)
+ }
+}
diff --git a/test/files/run/delambdafyLambdaClassNames/Test.scala b/test/files/run/delambdafyLambdaClassNames/Test.scala
new file mode 100644
index 0000000000..49a397d1d2
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames/Test.scala
@@ -0,0 +1,4 @@
+object Test extends App {
+ val c = Class.forName("A$$nestedInAnon$1$lambda$$run$1")
+ println(c.getName)
+}
diff --git a/test/files/run/nothingTypeDce.flags b/test/files/run/nothingTypeDce.flags
new file mode 100644
index 0000000000..d85321ca0e
--- /dev/null
+++ b/test/files/run/nothingTypeDce.flags
@@ -0,0 +1 @@
+-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code
diff --git a/test/files/run/nothingTypeDce.scala b/test/files/run/nothingTypeDce.scala
new file mode 100644
index 0000000000..5f3692fd33
--- /dev/null
+++ b/test/files/run/nothingTypeDce.scala
@@ -0,0 +1,63 @@
+// See comment in BCodeBodyBuilder
+
+// -target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code
+// target enables stack map frames generation
+
+class C {
+ // can't just emit a call to ???, that returns value of type Nothing$ (not Int).
+ def f1: Int = ???
+
+ def f2: Int = throw new Error("")
+
+ def f3(x: Boolean) = {
+ var y = 0
+ // cannot assign an object of type Nothing$ to Int
+ if (x) y = ???
+ else y = 1
+ y
+ }
+
+ def f4(x: Boolean) = {
+ var y = 0
+ // tests that whatever is emitted after the throw is valid (what? depends on opts, presence of stack map frames)
+ if (x) y = throw new Error("")
+ else y = 1
+ y
+ }
+
+ def f5(x: Boolean) = {
+ // stack heights need to be the same. ??? looks to the jvm like returning a value of
+ // type Nothing$, need to drop or throw it.
+ println(
+ if (x) { ???; 10 }
+ else 20
+ )
+ }
+
+ def f6(x: Boolean) = {
+ println(
+ if (x) { throw new Error(""); 10 }
+ else 20
+ )
+ }
+
+ def f7(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+
+ def f8(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+}
+
+object Test extends App {
+ // creating an instance is enough to trigger bytecode verification for all methods,
+ // no need to invoke the methods.
+ new C()
+}
diff --git a/test/files/run/nothingTypeNoFramesNoDce.check b/test/files/run/nothingTypeNoFramesNoDce.check
new file mode 100644
index 0000000000..b1d08b45ff
--- /dev/null
+++ b/test/files/run/nothingTypeNoFramesNoDce.check
@@ -0,0 +1 @@
+warning: -target:jvm-1.5 is deprecated: use target for Java 1.6 or above.
diff --git a/test/files/run/nothingTypeNoFramesNoDce.flags b/test/files/run/nothingTypeNoFramesNoDce.flags
new file mode 100644
index 0000000000..a035c86179
--- /dev/null
+++ b/test/files/run/nothingTypeNoFramesNoDce.flags
@@ -0,0 +1 @@
+-target:jvm-1.5 -Ybackend:GenBCode -Yopt:l:none -deprecation
diff --git a/test/files/run/nothingTypeNoFramesNoDce.scala b/test/files/run/nothingTypeNoFramesNoDce.scala
new file mode 100644
index 0000000000..3d1298303a
--- /dev/null
+++ b/test/files/run/nothingTypeNoFramesNoDce.scala
@@ -0,0 +1,61 @@
+// See comment in BCodeBodyBuilder
+
+// -target:jvm-1.5 -Ybackend:GenBCode -Yopt:l:none
+// target disables stack map frame generation. in this mode, the ClssWriter just emits dead code as is.
+
+class C {
+ // can't just emit a call to ???, that returns value of type Nothing$ (not Int).
+ def f1: Int = ???
+
+ def f2: Int = throw new Error("")
+
+ def f3(x: Boolean) = {
+ var y = 0
+ // cannot assign an object of type Nothing$ to Int
+ if (x) y = ???
+ else y = 1
+ y
+ }
+
+ def f4(x: Boolean) = {
+ var y = 0
+ // tests that whatever is emitted after the throw is valid (what? depends on opts, presence of stack map frames)
+ if (x) y = throw new Error("")
+ else y = 1
+ y
+ }
+
+ def f5(x: Boolean) = {
+ // stack heights need to be the smae. ??? looks to the jvm like returning a value of
+ // type Nothing$, need to drop or throw it.
+ println(
+ if (x) { ???; 10 }
+ else 20
+ )
+ }
+
+ def f6(x: Boolean) = {
+ println(
+ if (x) { throw new Error(""); 10 }
+ else 20
+ )
+ }
+
+ def f7(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+
+ def f8(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+}
+
+object Test extends App {
+ new C()
+}
diff --git a/test/files/run/nothingTypeNoOpt.flags b/test/files/run/nothingTypeNoOpt.flags
new file mode 100644
index 0000000000..b3b518051b
--- /dev/null
+++ b/test/files/run/nothingTypeNoOpt.flags
@@ -0,0 +1 @@
+-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:none
diff --git a/test/files/run/nothingTypeNoOpt.scala b/test/files/run/nothingTypeNoOpt.scala
new file mode 100644
index 0000000000..5c5a20fa3b
--- /dev/null
+++ b/test/files/run/nothingTypeNoOpt.scala
@@ -0,0 +1,61 @@
+// See comment in BCodeBodyBuilder
+
+// -target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:none
+// target enables stack map frame generation
+
+class C {
+ // can't just emit a call to ???, that returns value of type Nothing$ (not Int).
+ def f1: Int = ???
+
+ def f2: Int = throw new Error("")
+
+ def f3(x: Boolean) = {
+ var y = 0
+ // cannot assign an object of type Nothing$ to Int
+ if (x) y = ???
+ else y = 1
+ y
+ }
+
+ def f4(x: Boolean) = {
+ var y = 0
+ // tests that whatever is emitted after the throw is valid (what? depends on opts, presence of stack map frames)
+ if (x) y = throw new Error("")
+ else y = 1
+ y
+ }
+
+ def f5(x: Boolean) = {
+ // stack heights need to be the smae. ??? looks to the jvm like returning a value of
+ // type Nothing$, need to drop or throw it.
+ println(
+ if (x) { ???; 10 }
+ else 20
+ )
+ }
+
+ def f6(x: Boolean) = {
+ println(
+ if (x) { throw new Error(""); 10 }
+ else 20
+ )
+ }
+
+ def f7(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+
+ def f8(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+}
+
+object Test extends App {
+ new C()
+}
diff --git a/test/files/run/t8680.scala b/test/files/run/t8680.scala
new file mode 100644
index 0000000000..2bce09c507
--- /dev/null
+++ b/test/files/run/t8680.scala
@@ -0,0 +1,53 @@
+object Test extends App {
+ def pre(n: Int) = (-n to -1).toStream
+
+ def cyc(m: Int) = {
+ lazy val s: Stream[Int] = (0 until m).toStream #::: s
+ s
+ }
+
+ def precyc(n: Int, m: Int) = pre(n) #::: cyc(m)
+
+ def str(s: Stream[Int]) = {
+ val b = new StringBuilder
+ s.addString(b, "", "", "")
+ b.toString
+ }
+
+ def goal(n: Int, m: Int) = (-n until m).mkString + "..."
+
+ // Check un-forced cyclic and non-cyclic streams
+ assert(str(pre(2)) == pre(2).take(1).toList.mkString + "?")
+ assert(str(cyc(2)) == cyc(2).take(1).toList.mkString + "?")
+ assert(str(precyc(2,2)) == precyc(2,2).take(1).toList.mkString + "?")
+ assert(!pre(2).hasDefiniteSize)
+ assert(!cyc(2).hasDefiniteSize)
+ assert(!precyc(2,2).hasDefiniteSize)
+
+ // Check forced cyclic and non-cyclic streams
+ assert(str(pre(2).force) == (-2 to -1).mkString)
+ assert(str(cyc(2).force) == (0 until 2).mkString + "...")
+ assert(str(precyc(2,2).force) == (-2 until 2).mkString + "...")
+ assert(pre(2).force.hasDefiniteSize)
+ assert(!cyc(2).force.hasDefiniteSize)
+ assert(!precyc(2,2).force.hasDefiniteSize)
+
+ // Special cases
+ assert(str(cyc(1).force) == goal(0,1))
+ assert(str(precyc(1,6).force) == goal(1,6))
+ assert(str(precyc(6,1).force) == goal(6,1))
+
+ // Make sure there are no odd/even problems
+ for (n <- 3 to 4; m <- 3 to 4) {
+ assert(precyc(n,m).mkString == goal(n,m), s"mkString $n $m")
+ assert(!precyc(n,m).force.hasDefiniteSize, s"hasDef $n$m")
+ }
+
+ // Make sure there are no cycle/prefix modulus problems
+ for (i <- 6 to 8) {
+ assert(precyc(i,3).mkString == goal(i,3), s"mkString $i 3")
+ assert(precyc(3,i).mkString == goal(3,i), s"mkString 3 $i")
+ assert(!precyc(i,3).force.hasDefiniteSize, s"hasDef $i 3")
+ assert(!precyc(3,i).force.hasDefiniteSize, s"hasDef 3 $i")
+ }
+}
diff --git a/test/junit/scala/collection/SetMapConsistencyTest.scala b/test/junit/scala/collection/SetMapConsistencyTest.scala
index eed6007eef..261c11a98b 100644
--- a/test/junit/scala/collection/SetMapConsistencyTest.scala
+++ b/test/junit/scala/collection/SetMapConsistencyTest.scala
@@ -514,4 +514,19 @@ class SetMapConsistencyTest {
assert( hs.toList.toSet == hs )
assert( hs == hs.toList.toSet )
}
+
+ @Test
+ def testSI8815() {
+ val lm = new scala.collection.mutable.LongMap[String]
+ lm += (Long.MinValue, "min")
+ lm += (-1, "neg-one")
+ lm += (0, "zero")
+ lm += (Long.MaxValue, "max")
+ var nit = 0
+ lm.iterator.foreach(_ => nit += 1)
+ var nfe = 0
+ lm.foreach(_ => nfe += 1)
+ assert(nit == 4)
+ assert(nfe == 4)
+ }
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala b/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
index cb7e7050b0..221aad6536 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
@@ -1,7 +1,6 @@
package scala.tools.nsc
package backend.jvm
-import scala.tools.testing.AssertUtil._
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.Test
diff --git a/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala
new file mode 100644
index 0000000000..15bc1f427d
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala
@@ -0,0 +1,79 @@
+package scala.tools.nsc.backend.jvm
+
+import org.junit.Assert._
+
+import scala.reflect.internal.util.BatchSourceFile
+import scala.reflect.io.VirtualDirectory
+import scala.tools.asm.Opcodes
+import scala.tools.asm.tree.{AbstractInsnNode, LabelNode, ClassNode, MethodNode}
+import scala.tools.cmd.CommandLineParser
+import scala.tools.nsc.{Settings, Global}
+import scala.tools.partest.ASMConverters
+import scala.collection.JavaConverters._
+
+object CodeGenTools {
+ import ASMConverters._
+
+ def genMethod( flags: Int = Opcodes.ACC_PUBLIC,
+ name: String = "m",
+ descriptor: String = "()V",
+ genericSignature: String = null,
+ throwsExceptions: Array[String] = null,
+ handlers: List[ExceptionHandler] = Nil,
+ localVars: List[LocalVariable] = Nil)(body: Instruction*): MethodNode = {
+ val node = new MethodNode(flags, name, descriptor, genericSignature, throwsExceptions)
+ applyToMethod(node, Method(body.toList, handlers, localVars))
+ node
+ }
+
+ def wrapInClass(method: MethodNode): ClassNode = {
+ val cls = new ClassNode()
+ cls.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null)
+ cls.methods.add(method)
+ cls
+ }
+
+ private def resetOutput(compiler: Global): Unit = {
+ compiler.settings.outputDirs.setSingleOutput(new VirtualDirectory("(memory)", None))
+ }
+
+ def newCompiler(defaultArgs: String = "-usejavacp", extraArgs: String = ""): Global = {
+ val settings = new Settings()
+ val args = (CommandLineParser tokenize defaultArgs) ++ (CommandLineParser tokenize extraArgs)
+ settings.processArguments(args, processAll = true)
+ val compiler = new Global(settings)
+ resetOutput(compiler)
+ compiler
+ }
+
+ def compile(compiler: Global)(code: String): List[(String, Array[Byte])] = {
+ compiler.reporter.reset()
+ resetOutput(compiler)
+ val run = new compiler.Run()
+ run.compileSources(List(new BatchSourceFile("unitTestSource.scala", code)))
+ val outDir = compiler.settings.outputDirs.getSingleOutput.get
+ (for (f <- outDir.iterator if !f.isDirectory) yield (f.name, f.toByteArray)).toList
+ }
+
+ def compileClasses(compiler: Global)(code: String): List[ClassNode] = {
+ compile(compiler)(code).map(p => AsmUtils.readClass(p._2)).sortBy(_.name)
+ }
+
+ def compileMethods(compiler: Global)(code: String): List[MethodNode] = {
+ compileClasses(compiler)(s"class C { $code }").head.methods.asScala.toList.filterNot(_.name == "<init>")
+ }
+
+ def singleMethodInstructions(compiler: Global)(code: String): List[Instruction] = {
+ val List(m) = compileMethods(compiler)(code)
+ instructionsFromMethod(m)
+ }
+
+ def singleMethod(compiler: Global)(code: String): Method = {
+ val List(m) = compileMethods(compiler)(code)
+ convertMethod(m)
+ }
+
+ def assertSameCode(actual: List[Instruction], expected: List[Instruction]): Unit = {
+ assertTrue(s"\nExpected: $expected\nActual : $actual", actual === expected)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala b/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala
new file mode 100644
index 0000000000..2fb5bb8052
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala
@@ -0,0 +1,81 @@
+package scala.tools.nsc.backend.jvm
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Assert._
+import CodeGenTools._
+import scala.tools.asm.Opcodes._
+import scala.tools.partest.ASMConverters._
+
+@RunWith(classOf[JUnit4])
+class DirectCompileTest {
+ val compiler = newCompiler(extraArgs = "-Ybackend:GenBCode")
+
+ @Test
+ def testCompile(): Unit = {
+ val List(("C.class", bytes)) = compile(compiler)(
+ """
+ |class C {
+ | def f = 1
+ |}
+ """.stripMargin)
+ def s(i: Int, n: Int) = (bytes(i) & 0xff) << n
+ assertTrue((s(0, 24) | s(1, 16) | s(2, 8) | s(3, 0)) == 0xcafebabe) // mocha java latte machiatto surpreme dark roasted espresso
+ }
+
+ @Test
+ def testCompileClasses(): Unit = {
+ val List(cClass, cModuleClass) = compileClasses(compiler)(
+ """
+ |class C
+ |object C
+ """.stripMargin)
+
+ assertTrue(cClass.name == "C")
+ assertTrue(cModuleClass.name == "C$")
+
+ val List(dMirror, dModuleClass) = compileClasses(compiler)(
+ """
+ |object D
+ """.stripMargin)
+
+ assertTrue(dMirror.name == "D")
+ assertTrue(dModuleClass.name == "D$")
+ }
+
+ @Test
+ def testCompileMethods(): Unit = {
+ val List(f, g) = compileMethods(compiler)(
+ """
+ |def f = 10
+ |def g = f
+ """.stripMargin)
+ assertTrue(f.name == "f")
+ assertTrue(g.name == "g")
+
+ assertTrue(instructionsFromMethod(f).dropNonOp ===
+ List(IntOp(BIPUSH, 10), Op(IRETURN)))
+
+ assertTrue(instructionsFromMethod(g).dropNonOp ===
+ List(VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "C", "f", "()I", false), Op(IRETURN)))
+ }
+
+ @Test
+ def testDropNonOpAliveLabels(): Unit = {
+ val List(f) = compileMethods(compiler)("""def f(x: Int) = if (x == 0) "a" else "b"""")
+ assertTrue(instructionsFromMethod(f).dropNonOp === List(
+ VarOp(ILOAD, 1),
+ Op(ICONST_0),
+ Jump(IF_ICMPEQ, Label(6)),
+ Jump(GOTO, Label(10)),
+ Label(6),
+ Ldc(LDC, "a"),
+ Jump(GOTO, Label(13)),
+ Label(10),
+ Ldc(LDC, "b"),
+ Label(13),
+ Op(ARETURN)
+ ))
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala
new file mode 100644
index 0000000000..57fa1a7b66
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala
@@ -0,0 +1,92 @@
+package scala.tools.nsc
+package backend.jvm
+package opt
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import scala.tools.asm.Opcodes._
+import org.junit.Assert._
+
+import CodeGenTools._
+import scala.tools.partest.ASMConverters
+import ASMConverters._
+
+@RunWith(classOf[JUnit4])
+class EmptyExceptionHandlersTest {
+
+ val exceptionDescriptor = "java/lang/Exception"
+
+ @Test
+ def eliminateEmpty(): Unit = {
+ val handlers = List(ExceptionHandler(Label(1), Label(2), Label(2), Some(exceptionDescriptor)))
+ val asmMethod = genMethod(handlers = handlers)(
+ Label(1),
+ Label(2),
+ Op(RETURN)
+ )
+ assertTrue(convertMethod(asmMethod).handlers.length == 1)
+ LocalOpt.removeEmptyExceptionHandlers(asmMethod)
+ assertTrue(convertMethod(asmMethod).handlers.isEmpty)
+ }
+
+ @Test
+ def eliminateHandlersGuardingNops(): Unit = {
+ val handlers = List(ExceptionHandler(Label(1), Label(2), Label(2), Some(exceptionDescriptor)))
+ val asmMethod = genMethod(handlers = handlers)(
+ Label(1), // nops only
+ Op(NOP),
+ Op(NOP),
+ Jump(GOTO, Label(3)),
+ Op(NOP),
+ Label(3),
+ Op(NOP),
+ Jump(GOTO, Label(4)),
+
+ Label(2), // handler
+ Op(ACONST_NULL),
+ Op(ATHROW),
+
+ Label(4), // return
+ Op(RETURN)
+ )
+ assertTrue(convertMethod(asmMethod).handlers.length == 1)
+ LocalOpt.removeEmptyExceptionHandlers(asmMethod)
+ assertTrue(convertMethod(asmMethod).handlers.isEmpty)
+ }
+
+ val noOptCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:none")
+ val dceCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:unreachable-code")
+
+ @Test
+ def eliminateUnreachableHandler(): Unit = {
+ val code = "def f: Unit = try { } catch { case _: Exception => println(0) }; println(1)"
+
+ assertTrue(singleMethod(noOptCompiler)(code).handlers.length == 1)
+ val optMethod = singleMethod(dceCompiler)(code)
+ assertTrue(optMethod.handlers.isEmpty)
+
+ val code2 =
+ """def f: Unit = {
+ | println(0)
+ | return
+ | try { throw new Exception("") } // removed by dce, so handler will be removed as well
+ | catch { case _: Exception => println(1) }
+ | println(2)
+ |}""".stripMargin
+
+ assertTrue(singleMethod(dceCompiler)(code2).handlers.isEmpty)
+ }
+
+ @Test
+ def keepAliveHandlers(): Unit = {
+ val code =
+ """def f: Int = {
+ | println(0)
+ | try { 1 }
+ | catch { case _: Exception => 2 }
+ |}""".stripMargin
+
+ assertTrue(singleMethod(dceCompiler)(code).handlers.length == 1)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
new file mode 100644
index 0000000000..a3bd7ae6fe
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
@@ -0,0 +1,217 @@
+package scala.tools.nsc
+package backend.jvm
+package opt
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import scala.tools.asm.Opcodes._
+import org.junit.Assert._
+
+import scala.tools.testing.AssertUtil._
+
+import CodeGenTools._
+import scala.tools.partest.ASMConverters
+import ASMConverters._
+
+@RunWith(classOf[JUnit4])
+class UnreachableCodeTest {
+ import UnreachableCodeTest._
+
+ // jvm-1.6 enables emitting stack map frames, which impacts the code generation wrt dead basic blocks,
+ // see comment in BCodeBodyBuilder
+ val dceCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code")
+ val noOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:none")
+
+ // jvm-1.5 disables computing stack map frames, and it emits dead code as-is.
+ val noOptNoFramesCompiler = newCompiler(extraArgs = "-target:jvm-1.5 -Ybackend:GenBCode -Yopt:l:none")
+
+ @Test
+ def basicElimination(): Unit = {
+ assertEliminateDead(
+ Op(ACONST_NULL),
+ Op(ATHROW),
+ Op(RETURN).dead
+ )
+
+ assertEliminateDead(
+ Op(RETURN)
+ )
+
+ assertEliminateDead(
+ Op(RETURN),
+ Op(ACONST_NULL).dead,
+ Op(ATHROW).dead
+ )
+ }
+
+ @Test
+ def eliminateNop(): Unit = {
+ assertEliminateDead(
+ // not dead, since visited by data flow analysis. need a different opt to eliminate it.
+ Op(NOP),
+ Op(RETURN),
+ Op(NOP).dead
+ )
+ }
+
+ @Test
+ def eliminateBranchOver(): Unit = {
+ assertEliminateDead(
+ Jump(GOTO, Label(1)),
+ Op(ACONST_NULL).dead,
+ Op(ATHROW).dead,
+ Label(1),
+ Op(RETURN)
+ )
+
+ assertEliminateDead(
+ Jump(GOTO, Label(1)),
+ Label(1),
+ Op(RETURN)
+ )
+ }
+
+ @Test
+ def deadLabelsRemain(): Unit = {
+ assertEliminateDead(
+ Op(RETURN),
+ Jump(GOTO, Label(1)).dead,
+ // not dead - labels may be referenced from other places in a classfile (eg exceptions table).
+ // will need a different opt to get rid of them
+ Label(1)
+ )
+ }
+
+ @Test
+ def pushPopNotEliminated(): Unit = {
+ assertEliminateDead(
+ // not dead, visited by data flow analysis.
+ Op(ACONST_NULL),
+ Op(POP),
+ Op(RETURN)
+ )
+ }
+
+ @Test
+ def nullnessNotConsidered(): Unit = {
+ assertEliminateDead(
+ Op(ACONST_NULL),
+ Jump(IFNULL, Label(1)),
+ Op(RETURN), // not dead
+ Label(1),
+ Op(RETURN)
+ )
+ }
+
+ @Test
+ def basicEliminationCompiler(): Unit = {
+ val code = "def f: Int = { return 1; 2 }"
+ val withDce = singleMethodInstructions(dceCompiler)(code)
+ assertSameCode(withDce.dropNonOp, List(Op(ICONST_1), Op(IRETURN)))
+
+ val noDce = singleMethodInstructions(noOptCompiler)(code)
+
+ // The emitted code is ICONST_1, IRETURN, ICONST_2, IRETURN. The latter two are dead.
+ //
+ // GenBCode puts the last IRETURN into a new basic block: it emits a label before the second
+ // IRETURN. This is an implementation detail, it may change; it affects the outcome of this test.
+ //
+ // During classfile writing with COMPUTE_FAMES (-target:jvm-1.6 or larger), the ClassfileWriter
+ // puts the ICONST_2 into a new basic block, because the preceding operation (IRETURN) ends
+ // the current block. We get something like
+ //
+ // L1: ICONST_1; IRETURN
+ // L2: ICONST_2 << dead
+ // L3: IRETURN << dead
+ //
+ // Finally, instructions in the dead basic blocks are replaced by ATHROW, as explained in
+ // a comment in BCodeBodyBuilder.
+ assertSameCode(noDce.dropNonOp, List(Op(ICONST_1), Op(IRETURN), Op(ATHROW), Op(ATHROW)))
+
+ // when NOT computing stack map frames, ASM's ClassWriter does not replace dead code by NOP/ATHROW
+ val noDceNoFrames = singleMethodInstructions(noOptNoFramesCompiler)(code)
+ assertSameCode(noDceNoFrames.dropNonOp, List(Op(ICONST_1), Op(IRETURN), Op(ICONST_2), Op(IRETURN)))
+ }
+
+ @Test
+ def eliminateDeadCatchBlocks(): Unit = {
+ val code = "def f: Int = { return 0; try { 1 } catch { case _: Exception => 2 } }"
+ assertSameCode(singleMethodInstructions(dceCompiler)(code).dropNonOp,
+ List(Op(ICONST_0), Op(IRETURN)))
+
+ val code2 = "def f: Unit = { try { } catch { case _: Exception => () }; () }"
+ // DCE only removes dead basic blocks, but not NOPs, and also not useless jumps
+ assertSameCode(singleMethodInstructions(dceCompiler)(code2).dropNonOp,
+ List(Op(NOP), Jump(GOTO, Label(33)), Label(33), Op(RETURN)))
+
+ val code3 = "def f: Unit = { try { } catch { case _: Exception => try { } catch { case _: Exception => () } }; () }"
+ assertSameCode(singleMethodInstructions(dceCompiler)(code3).dropNonOp,
+ List(Op(NOP), Jump(GOTO, Label(33)), Label(33), Op(RETURN)))
+
+ val code4 = "def f: Unit = { try { try { } catch { case _: Exception => () } } catch { case _: Exception => () }; () }"
+ assertSameCode(singleMethodInstructions(dceCompiler)(code4).dropNonOp,
+ List(Op(NOP), Jump(GOTO, Label(4)), Label(4), Jump(GOTO, Label(7)), Label(7), Op(RETURN)))
+ }
+
+ @Test // test the dce-testing tools
+ def metaTest(): Unit = {
+ assertEliminateDead() // no instructions
+
+ assertThrows[AssertionError](
+ assertEliminateDead(Op(RETURN).dead),
+ _.contains("Expected: List()\nActual : List(Op(RETURN))")
+ )
+
+ assertThrows[AssertionError](
+ assertEliminateDead(Op(RETURN), Op(RETURN)),
+ _.contains("Expected: List(Op(RETURN), Op(RETURN))\nActual : List(Op(RETURN))")
+ )
+ }
+
+ @Test
+ def bytecodeEquivalence: Unit = {
+ assertTrue(List(VarOp(ILOAD, 1)) ===
+ List(VarOp(ILOAD, 2)))
+ assertTrue(List(VarOp(ILOAD, 1), VarOp(ISTORE, 1)) ===
+ List(VarOp(ILOAD, 2), VarOp(ISTORE, 2)))
+
+ // the first Op will associate 1->2, then the 2->2 will fail
+ assertFalse(List(VarOp(ILOAD, 1), VarOp(ISTORE, 2)) ===
+ List(VarOp(ILOAD, 2), VarOp(ISTORE, 2)))
+
+ // will associate 1->2 and 2->1, which is OK
+ assertTrue(List(VarOp(ILOAD, 1), VarOp(ISTORE, 2)) ===
+ List(VarOp(ILOAD, 2), VarOp(ISTORE, 1)))
+
+ assertTrue(List(Label(1), Label(2), Label(1)) ===
+ List(Label(2), Label(4), Label(2)))
+ assertTrue(List(LineNumber(1, Label(1)), Label(1)) ===
+ List(LineNumber(1, Label(3)), Label(3)))
+ assertFalse(List(LineNumber(1, Label(1)), Label(1)) ===
+ List(LineNumber(1, Label(3)), Label(1)))
+
+ assertTrue(List(TableSwitch(TABLESWITCH, 1, 3, Label(4), List(Label(5), Label(6))), Label(4), Label(5), Label(6)) ===
+ List(TableSwitch(TABLESWITCH, 1, 3, Label(9), List(Label(3), Label(4))), Label(9), Label(3), Label(4)))
+
+ assertTrue(List(FrameEntry(F_FULL, List(INTEGER, DOUBLE, Label(3)), List("java/lang/Object", Label(4))), Label(3), Label(4)) ===
+ List(FrameEntry(F_FULL, List(INTEGER, DOUBLE, Label(1)), List("java/lang/Object", Label(3))), Label(1), Label(3)))
+ }
+}
+
+object UnreachableCodeTest {
+ import scala.language.implicitConversions
+ implicit def aliveInstruction(ins: Instruction): (Instruction, Boolean) = (ins, true)
+
+ implicit class MortalInstruction(val ins: Instruction) extends AnyVal {
+ def dead: (Instruction, Boolean) = (ins, false)
+ }
+
+ def assertEliminateDead(code: (Instruction, Boolean)*): Unit = {
+ val cls = wrapInClass(genMethod()(code.map(_._1): _*))
+ LocalOpt.removeUnreachableCode(cls)
+ val nonEliminated = instructionsFromMethod(cls.methods.get(0))
+ val expectedLive = code.filter(_._2).map(_._1).toList
+ assertSameCode(nonEliminated, expectedLive)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala
new file mode 100644
index 0000000000..24a1f9d1c1
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala
@@ -0,0 +1,87 @@
+package scala.tools.nsc
+package backend.jvm
+package opt
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import scala.tools.asm.Opcodes._
+import org.junit.Assert._
+import scala.collection.JavaConverters._
+
+import CodeGenTools._
+import scala.tools.partest.ASMConverters
+import ASMConverters._
+
+@RunWith(classOf[JUnit4])
+class UnusedLocalVariablesTest {
+ val dceCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:unreachable-code")
+
+ @Test
+ def removeUnusedVar(): Unit = {
+ val code = """def f(a: Long, b: String, c: Double): Unit = { return; var x = a; var y = x + 10 }"""
+ assertLocalVarCount(code, 4) // `this, a, b, c`
+
+ val code2 = """def f(): Unit = { var x = if (true) return else () }"""
+ assertLocalVarCount(code2, 1) // x is eliminated, constant folding in scalac removes the if
+
+ val code3 = """def f: Unit = return""" // paramless method
+ assertLocalVarCount(code3, 1) // this
+ }
+
+ @Test
+ def keepUsedVar(): Unit = {
+ val code = """def f(a: Long, b: String, c: Double): Unit = { val x = 10 + a; val y = x + 10 }"""
+ assertLocalVarCount(code, 6)
+
+ val code2 = """def f(a: Long): Unit = { var x = if (a == 0l) return else () }"""
+ assertLocalVarCount(code2, 3) // remains
+ }
+
+ @Test
+ def constructorLocals(): Unit = {
+ val code = """class C {
+ | def this(a: Int) = {
+ | this()
+ | throw new Exception("")
+ | val y = 0
+ | }
+ |}
+ |""".stripMargin
+ val cls = compileClasses(dceCompiler)(code).head
+ val m = convertMethod(cls.methods.asScala.toList.find(_.desc == "(I)V").get)
+ assertTrue(m.localVars.length == 2) // this, a, but not y
+
+
+ val code2 =
+ """class C {
+ | {
+ | throw new Exception("")
+ | val a = 0
+ | }
+ |}
+ |
+ |object C {
+ | {
+ | throw new Exception("")
+ | val b = 1
+ | }
+ |}
+ """.stripMargin
+
+ val clss2 = compileClasses(dceCompiler)(code2)
+ val cls2 = clss2.find(_.name == "C").get
+ val companion2 = clss2.find(_.name == "C$").get
+
+ val clsConstr = convertMethod(cls2.methods.asScala.toList.find(_.name == "<init>").get)
+ val companionConstr = convertMethod(companion2.methods.asScala.toList.find(_.name == "<init>").get)
+
+ assertTrue(clsConstr.localVars.length == 1) // this
+ assertTrue(companionConstr.localVars.length == 1) // this
+ }
+
+ def assertLocalVarCount(code: String, numVars: Int): Unit = {
+ assertTrue(singleMethod(dceCompiler)(code).localVars.length == numVars)
+ }
+
+}
diff --git a/test/junit/scala/tools/testing/AssertThrowsTest.scala b/test/junit/scala/tools/testing/AssertThrowsTest.scala
index a70519e63c..d91e450bac 100644
--- a/test/junit/scala/tools/testing/AssertThrowsTest.scala
+++ b/test/junit/scala/tools/testing/AssertThrowsTest.scala
@@ -31,4 +31,13 @@ class AssertThrowsTest {
}
})
-} \ No newline at end of file
+ @Test
+ def errorIfNoThrow: Unit = {
+ try {
+ assertThrows[Foo] { () }
+ } catch {
+ case e: AssertionError => return
+ }
+ assert(false, "assertThrows should error if the tested expression does not throw anything")
+ }
+}