summaryrefslogtreecommitdiff
path: root/test/junit
diff options
context:
space:
mode:
Diffstat (limited to 'test/junit')
-rw-r--r--test/junit/scala/collection/IteratorTest.scala22
-rw-r--r--test/junit/scala/collection/SeqViewTest.scala16
-rw-r--r--test/junit/scala/collection/SetMapConsistencyTest.scala23
-rw-r--r--test/junit/scala/collection/convert/NullSafetyTest.scala279
-rw-r--r--test/junit/scala/collection/convert/WrapperSerializationTest.scala29
-rw-r--r--test/junit/scala/collection/immutable/StreamTest.scala126
-rw-r--r--test/junit/scala/collection/immutable/StringLikeTest.scala6
-rw-r--r--test/junit/scala/collection/mutable/BitSetTest.scala7
-rw-r--r--test/junit/scala/collection/mutable/OpenHashMapTest.scala2
-rw-r--r--test/junit/scala/io/SourceTest.scala4
-rw-r--r--test/junit/scala/issues/BytecodeTests.scala63
-rw-r--r--test/junit/scala/reflect/internal/TreeGenTest.scala51
-rw-r--r--test/junit/scala/runtime/LambdaDeserializerTest.java193
-rw-r--r--test/junit/scala/runtime/ScalaRunTimeTest.scala57
-rw-r--r--test/junit/scala/sys/process/t7350.scala298
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala17
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala43
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala55
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala108
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala1
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala204
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala73
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/CompactLocalVariablesTest.scala4
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala9
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala34
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala2
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala221
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala2
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala54
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala18
-rw-r--r--test/junit/scala/tools/nsc/settings/ScalaVersionTest.scala53
-rw-r--r--test/junit/scala/tools/nsc/settings/SettingsTest.scala4
-rw-r--r--test/junit/scala/tools/nsc/symtab/FlagsTest.scala7
-rw-r--r--test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala4
34 files changed, 1902 insertions, 187 deletions
diff --git a/test/junit/scala/collection/IteratorTest.scala b/test/junit/scala/collection/IteratorTest.scala
index 1c1e50aed9..b0639ef365 100644
--- a/test/junit/scala/collection/IteratorTest.scala
+++ b/test/junit/scala/collection/IteratorTest.scala
@@ -135,6 +135,20 @@ class IteratorTest {
assertEquals(3, List(1, 2, 3, 4, 5).iterator.indexWhere { x: Int => x >= 4 })
assertEquals(-1, List(1, 2, 3, 4, 5).iterator.indexWhere { x: Int => x >= 16 })
}
+ @Test def indexOfFrom(): Unit = {
+ assertEquals(1, List(1, 2, 3, 4, 5).iterator.indexOf(2, 0))
+ assertEquals(1, List(1, 2, 3, 4, 5).iterator.indexOf(2, 1))
+ assertEquals(-1, List(1, 2, 3, 4, 5).iterator.indexOf(2, 2))
+ assertEquals(4, List(1, 2, 3, 2, 1).iterator.indexOf(1, 1))
+ assertEquals(1, List(1, 2, 3, 2, 1).iterator.indexOf(2, 1))
+ }
+ @Test def indexWhereFrom(): Unit = {
+ assertEquals(1, List(1, 2, 3, 4, 5).iterator.indexWhere(_ == 2, 0))
+ assertEquals(1, List(1, 2, 3, 4, 5).iterator.indexWhere(_ == 2, 1))
+ assertEquals(-1, List(1, 2, 3, 4, 5).iterator.indexWhere(_ == 2, 2))
+ assertEquals(4, List(1, 2, 3, 2, 1).iterator.indexWhere(_ < 2, 1))
+ assertEquals(1, List(1, 2, 3, 2, 1).iterator.indexWhere(_ <= 2, 1))
+ }
// iterator-iterate-lazy.scala
// was java.lang.UnsupportedOperationException: tail of empty list
@Test def iterateIsSufficientlyLazy(): Unit = {
@@ -154,6 +168,14 @@ class IteratorTest {
results += (Stream from 1).toIterator.drop(10).toStream.drop(10).toIterator.next()
assertSameElements(List(1,1,21), results)
}
+ // SI-8552
+ @Test def indexOfShouldWorkForTwoParams(): Unit = {
+ assertEquals(1, List(1, 2, 3).iterator.indexOf(2, 0))
+ assertEquals(-1, List(5 -> 0).iterator.indexOf(5, 0))
+ assertEquals(0, List(5 -> 0).iterator.indexOf((5, 0)))
+ assertEquals(-1, List(5 -> 0, 9 -> 2, 0 -> 3).iterator.indexOf(9, 2))
+ assertEquals(1, List(5 -> 0, 9 -> 2, 0 -> 3).iterator.indexOf(9 -> 2))
+ }
// SI-9332
@Test def spanExhaustsLeadingIterator(): Unit = {
def it = Iterator.iterate(0)(_ + 1).take(6)
diff --git a/test/junit/scala/collection/SeqViewTest.scala b/test/junit/scala/collection/SeqViewTest.scala
new file mode 100644
index 0000000000..24474fc4b9
--- /dev/null
+++ b/test/junit/scala/collection/SeqViewTest.scala
@@ -0,0 +1,16 @@
+package scala.collection
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Assert._
+import org.junit.Test
+
+@RunWith(classOf[JUnit4])
+class SeqViewTest {
+
+ @Test
+ def test_SI8691() {
+ // Really just testing to make sure ++: doesn't throw an exception
+ assert( Seq(1,2) ++: Seq(3,4).view == Seq(1,2,3,4) )
+ }
+}
diff --git a/test/junit/scala/collection/SetMapConsistencyTest.scala b/test/junit/scala/collection/SetMapConsistencyTest.scala
index 261c11a98b..eb864a8449 100644
--- a/test/junit/scala/collection/SetMapConsistencyTest.scala
+++ b/test/junit/scala/collection/SetMapConsistencyTest.scala
@@ -66,6 +66,8 @@ class SetMapConsistencyTest {
def boxMhm[A] = new BoxMutableMap[A, cm.HashMap[A, Int]](new cm.HashMap[A, Int], "mutable.HashMap")
def boxMohm[A] = new BoxMutableMap[A, cm.OpenHashMap[A, Int]](new cm.OpenHashMap[A, Int], "mutable.OpenHashMap")
+
+ def boxMtm[A: Ordering] = new BoxMutableMap[A, cm.TreeMap[A, Int]](new cm.TreeMap[A, Int], "mutable.TreeMap")
def boxMarm[A <: AnyRef] = new BoxMutableMap[A, cm.AnyRefMap[A, Int]](new cm.AnyRefMap[A, Int](_ => -1), "mutable.AnyRefMap") {
private def arm: cm.AnyRefMap[A, Int] = m.asInstanceOf[cm.AnyRefMap[A, Int]]
@@ -188,7 +190,9 @@ class SetMapConsistencyTest {
def boxMbs = new BoxMutableSet[Int, cm.BitSet](new cm.BitSet, "mutable.BitSet")
def boxMhs[A] = new BoxMutableSet[A, cm.HashSet[A]](new cm.HashSet[A], "mutable.HashSet")
-
+
+ def boxMts[A: Ordering] = new BoxMutableSet[A, cm.TreeSet[A]](new cm.TreeSet[A], "mutable.TreeSet")
+
def boxJavaS[A] = new BoxMutableSet[A, cm.Set[A]]((new java.util.HashSet[A]).asScala, "java.util.HashSet") {
override def adders = 3
override def subbers = 1
@@ -315,7 +319,7 @@ class SetMapConsistencyTest {
@Test
def churnIntMaps() {
val maps = Array[() => MapBox[Int]](
- () => boxMlm[Int], () => boxMhm[Int], () => boxMohm[Int], () => boxJavaM[Int],
+ () => boxMlm[Int], () => boxMhm[Int], () => boxMohm[Int], () => boxMtm[Int], () => boxJavaM[Int],
() => boxIim, () => boxIhm[Int], () => boxIlm[Int], () => boxItm[Int]
)
assert( maps.sliding(2).forall{ ms => churn(ms(0)(), ms(1)(), intKeys, 2000) } )
@@ -325,7 +329,7 @@ class SetMapConsistencyTest {
def churnLongMaps() {
val maps = Array[() => MapBox[Long]](
() => boxMjm, () => boxIjm, () => boxJavaM[Long],
- () => boxMlm[Long], () => boxMhm[Long], () => boxMohm[Long], () => boxIhm[Long], () => boxIlm[Long]
+ () => boxMlm[Long], () => boxMhm[Long], () => boxMtm[Long], () => boxMohm[Long], () => boxIhm[Long], () => boxIlm[Long]
)
assert( maps.sliding(2).forall{ ms => churn(ms(0)(), ms(1)(), longKeys, 10000) } )
}
@@ -352,7 +356,7 @@ class SetMapConsistencyTest {
def churnIntSets() {
val sets = Array[() => MapBox[Int]](
() => boxMhm[Int], () => boxIhm[Int], () => boxJavaS[Int],
- () => boxMbs, () => boxMhs[Int], () => boxIbs, () => boxIhs[Int], () => boxIls[Int], () => boxIts[Int]
+ () => boxMbs, () => boxMhs[Int], () => boxMts[Int], () => boxIbs, () => boxIhs[Int], () => boxIls[Int], () => boxIts[Int]
)
assert( sets.sliding(2).forall{ ms => churn(ms(0)(), ms(1)(), smallKeys, 1000, valuer = _ => 0) } )
}
@@ -529,4 +533,15 @@ class SetMapConsistencyTest {
assert(nit == 4)
assert(nfe == 4)
}
+
+ @Test
+ def test_SI8727() {
+ import scala.tools.testing.AssertUtil._
+ type NSEE = NoSuchElementException
+ val map = Map(0 -> "zero", 1 -> "one")
+ val m = map.filterKeys(i => if (map contains i) true else throw new NSEE)
+ assert{ (m contains 0) && (m get 0).nonEmpty }
+ assertThrows[NSEE]{ m contains 2 }
+ assertThrows[NSEE]{ m get 2 }
+ }
}
diff --git a/test/junit/scala/collection/convert/NullSafetyTest.scala b/test/junit/scala/collection/convert/NullSafetyTest.scala
new file mode 100644
index 0000000000..de5481d9e2
--- /dev/null
+++ b/test/junit/scala/collection/convert/NullSafetyTest.scala
@@ -0,0 +1,279 @@
+package scala.collection.convert
+
+import java.{util => ju, lang => jl}
+import ju.{concurrent => juc}
+
+import org.junit.Test
+import org.junit.experimental.runners.Enclosed
+import org.junit.runner.RunWith
+
+import scala.collection.JavaConversions._
+import scala.collection.JavaConverters._
+import scala.collection.{mutable, concurrent}
+
+@RunWith(classOf[Enclosed])
+object NullSafetyTest {
+
+ /*
+ * Pertinent: SI-9113
+ * Tests to insure that wrappers return null instead of wrapping it as a collection
+ */
+
+ class ToScala {
+
+ @Test def testIteratorWrapping(): Unit = {
+ val nullJIterator: ju.Iterator[AnyRef] = null
+ val iterator: Iterator[AnyRef] = nullJIterator
+
+ assert(iterator == null)
+ }
+
+ @Test def testEnumerationWrapping(): Unit = {
+ val nullJEnumeration: ju.Enumeration[AnyRef] = null
+ val enumeration: Iterator[AnyRef] = nullJEnumeration
+
+ assert(enumeration == null)
+ }
+
+ @Test def testIterableWrapping(): Unit = {
+ val nullJIterable: jl.Iterable[AnyRef] = null
+ val iterable: Iterable[AnyRef] = nullJIterable
+
+ assert(iterable == null)
+ }
+
+ @Test def testCollectionWrapping(): Unit = {
+ val nullJCollection: ju.Collection[AnyRef] = null
+ val collection: Iterable[AnyRef] = nullJCollection
+
+ assert(collection == null)
+ }
+
+ @Test def testBufferWrapping(): Unit = {
+ val nullJList: ju.List[AnyRef] = null
+ val buffer: mutable.Buffer[AnyRef] = nullJList
+
+ assert(buffer == null)
+ }
+
+ @Test def testSetWrapping(): Unit = {
+ val nullJSet: ju.Set[AnyRef] = null
+ val set: mutable.Set[AnyRef] = nullJSet
+
+ assert(set == null)
+ }
+
+ @Test def testMapWrapping(): Unit = {
+ val nullJMap: ju.Map[AnyRef, AnyRef] = null
+ val map: mutable.Map[AnyRef, AnyRef] = nullJMap
+
+ assert(map == null)
+ }
+
+ @Test def testConcurrentMapWrapping(): Unit = {
+ val nullJConMap: juc.ConcurrentMap[AnyRef, AnyRef] = null
+ val conMap: concurrent.Map[AnyRef, AnyRef] = nullJConMap
+
+ assert(conMap == null)
+ }
+
+ @Test def testDictionaryWrapping(): Unit = {
+ val nullJDict: ju.Dictionary[AnyRef, AnyRef] = null
+ val dict: mutable.Map[AnyRef, AnyRef] = nullJDict
+
+ assert(dict == null)
+ }
+
+
+ @Test def testPropertyWrapping(): Unit = {
+ val nullJProps: ju.Properties = null
+ val props: mutable.Map[String, String] = nullJProps
+
+ assert(props == null)
+ }
+
+ @Test def testIteratorDecoration(): Unit = {
+ val nullJIterator: ju.Iterator[AnyRef] = null
+
+ assert(nullJIterator.asScala == null)
+ }
+
+ @Test def testEnumerationDecoration(): Unit = {
+ val nullJEnumeration: ju.Enumeration[AnyRef] = null
+
+ assert(nullJEnumeration.asScala == null)
+ }
+
+ @Test def testIterableDecoration(): Unit = {
+ val nullJIterable: jl.Iterable[AnyRef] = null
+
+ assert(nullJIterable.asScala == null)
+ }
+
+ @Test def testCollectionDecoration(): Unit = {
+ val nullJCollection: ju.Collection[AnyRef] = null
+
+ assert(nullJCollection.asScala == null)
+ }
+
+ @Test def testBufferDecoration(): Unit = {
+ val nullJBuffer: ju.List[AnyRef] = null
+
+ assert(nullJBuffer.asScala == null)
+ }
+
+ @Test def testSetDecoration(): Unit = {
+ val nullJSet: ju.Set[AnyRef] = null
+
+ assert(nullJSet.asScala == null)
+ }
+
+ @Test def testMapDecoration(): Unit = {
+ val nullJMap: ju.Map[AnyRef, AnyRef] = null
+
+ assert(nullJMap.asScala == null)
+ }
+
+ @Test def testConcurrentMapDecoration(): Unit = {
+ val nullJConMap: juc.ConcurrentMap[AnyRef, AnyRef] = null
+
+ assert(nullJConMap.asScala == null)
+ }
+
+ @Test def testDictionaryDecoration(): Unit = {
+ val nullJDict: ju.Dictionary[AnyRef, AnyRef] = null
+
+ assert(nullJDict.asScala == null)
+ }
+
+ @Test def testPropertiesDecoration(): Unit = {
+ val nullJProperties: ju.Properties = null
+
+ assert(nullJProperties.asScala == null)
+ }
+ }
+
+ class ToJava {
+
+ @Test def testIteratorWrapping(): Unit = {
+ val nullIterator: Iterator[AnyRef] = null
+ val jIterator: ju.Iterator[AnyRef] = nullIterator
+
+ assert(jIterator == null)
+ }
+
+ @Test def testEnumerationWrapping(): Unit = {
+ val nullEnumeration: Iterator[AnyRef] = null
+ val enumeration: ju.Iterator[AnyRef] = nullEnumeration
+
+ assert(enumeration == null)
+ }
+
+ @Test def testIterableWrapping(): Unit = {
+ val nullIterable: Iterable[AnyRef] = null
+ val iterable: jl.Iterable[AnyRef] = asJavaIterable(nullIterable)
+
+ assert(iterable == null)
+ }
+
+ @Test def testCollectionWrapping(): Unit = {
+ val nullCollection: Iterable[AnyRef] = null
+ val collection: ju.Collection[AnyRef] = nullCollection
+
+ assert(collection == null)
+ }
+
+ @Test def testBufferWrapping(): Unit = {
+ val nullList: mutable.Buffer[AnyRef] = null
+ val buffer: ju.List[AnyRef] = nullList
+
+ assert(buffer == null)
+ }
+
+ @Test def testSetWrapping(): Unit = {
+ val nullSet: mutable.Set[AnyRef] = null
+ val set: ju.Set[AnyRef] = nullSet
+
+ assert(set == null)
+ }
+
+ @Test def testMapWrapping(): Unit = {
+ val nullMap: mutable.Map[AnyRef, AnyRef] = null
+ val map: ju.Map[AnyRef, AnyRef] = nullMap
+
+ assert(map == null)
+ }
+
+ @Test def testConcurrentMapWrapping(): Unit = {
+ val nullConMap: concurrent.Map[AnyRef, AnyRef] = null
+ val conMap: juc.ConcurrentMap[AnyRef, AnyRef] = nullConMap
+
+ assert(conMap == null)
+ }
+
+ @Test def testDictionaryWrapping(): Unit = {
+ val nullDict: mutable.Map[AnyRef, AnyRef] = null
+ val dict: ju.Dictionary[AnyRef, AnyRef] = nullDict
+
+ assert(dict == null)
+ }
+
+ // Implicit conversion to ju.Properties is not available
+
+ @Test def testIteratorDecoration(): Unit = {
+ val nullIterator: Iterator[AnyRef] = null
+
+ assert(nullIterator.asJava == null)
+ }
+
+ @Test def testEnumerationDecoration(): Unit = {
+ val nullEnumeration: Iterator[AnyRef] = null
+
+ assert(nullEnumeration.asJavaEnumeration == null)
+ }
+
+ @Test def testIterableDecoration(): Unit = {
+ val nullIterable: Iterable[AnyRef] = null
+
+ assert(nullIterable.asJava == null)
+ }
+
+ @Test def testCollectionDecoration(): Unit = {
+ val nullCollection: Iterable[AnyRef] = null
+
+ assert(nullCollection.asJavaCollection == null)
+ }
+
+ @Test def testBufferDecoration(): Unit = {
+ val nullBuffer: mutable.Buffer[AnyRef] = null
+
+ assert(nullBuffer.asJava == null)
+ }
+
+ @Test def testSetDecoration(): Unit = {
+ val nullSet: Set[AnyRef] = null
+
+ assert(nullSet.asJava == null)
+ }
+
+ @Test def testMapDecoration(): Unit = {
+ val nullMap: mutable.Map[AnyRef, AnyRef] = null
+
+ assert(nullMap.asJava == null)
+ }
+
+ @Test def testConcurrentMapDecoration(): Unit = {
+ val nullConMap: concurrent.Map[AnyRef, AnyRef] = null
+
+ assert(nullConMap.asJava == null)
+ }
+
+ @Test def testDictionaryDecoration(): Unit = {
+ val nullDict: mutable.Map[AnyRef, AnyRef] = null
+
+ assert(nullDict.asJavaDictionary == null)
+ }
+
+ // Decorator conversion to ju.Properties is not available
+ }
+}
diff --git a/test/junit/scala/collection/convert/WrapperSerializationTest.scala b/test/junit/scala/collection/convert/WrapperSerializationTest.scala
new file mode 100644
index 0000000000..d398be806a
--- /dev/null
+++ b/test/junit/scala/collection/convert/WrapperSerializationTest.scala
@@ -0,0 +1,29 @@
+package scala.collection.convert
+
+import org.junit.Assert._
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(classOf[JUnit4])
+class WrapperSerializationTest {
+ def ser(a: AnyRef) = {
+ val baos = new java.io.ByteArrayOutputStream
+ (new java.io.ObjectOutputStream(baos)).writeObject(a)
+ baos
+ }
+ def des(baos: java.io.ByteArrayOutputStream): AnyRef = {
+ val bais = new java.io.ByteArrayInputStream(baos.toByteArray)
+ (new java.io.ObjectInputStream(bais)).readObject()
+ }
+ def serdes(a: AnyRef): Boolean = a == des(ser(a))
+
+ @Test
+ def test_SI8911() {
+ import scala.collection.JavaConverters._
+ assert( serdes(scala.collection.mutable.ArrayBuffer(1,2).asJava) )
+ assert( serdes(Seq(1,2).asJava) )
+ assert( serdes(Set(1,2).asJava) )
+ assert( serdes(Map(1 -> "one", 2 -> "two").asJava) )
+ }
+}
diff --git a/test/junit/scala/collection/immutable/StreamTest.scala b/test/junit/scala/collection/immutable/StreamTest.scala
new file mode 100644
index 0000000000..1b257aabc4
--- /dev/null
+++ b/test/junit/scala/collection/immutable/StreamTest.scala
@@ -0,0 +1,126 @@
+package scala.collection.immutable
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import org.junit.Assert._
+
+import scala.ref.WeakReference
+import scala.util.Try
+
+@RunWith(classOf[JUnit4])
+class StreamTest {
+
+ @Test
+ def t6727_and_t6440_and_8627(): Unit = {
+ assertTrue(Stream.continually(()).filter(_ => true).take(2) == Seq((), ()))
+ assertTrue(Stream.continually(()).filterNot(_ => false).take(2) == Seq((), ()))
+ assertTrue(Stream(1,2,3,4,5).filter(_ < 4) == Seq(1,2,3))
+ assertTrue(Stream(1,2,3,4,5).filterNot(_ > 4) == Seq(1,2,3,4))
+ assertTrue(Stream.from(1).filter(_ > 4).take(3) == Seq(5,6,7))
+ assertTrue(Stream.from(1).filterNot(_ <= 4).take(3) == Seq(5,6,7))
+ }
+
+ /** Test helper to verify that the given Stream operation allows
+ * GC of the head during processing of the tail.
+ */
+ def assertStreamOpAllowsGC(op: (=> Stream[Int], Int => Unit) => Any, f: Int => Unit): Unit = {
+ val msgSuccessGC = "GC success"
+ val msgFailureGC = "GC failure"
+
+ // A stream of 500 elements at most. We will test that the head can be collected
+ // while processing the tail. After each element we will GC and wait 10 ms, so a
+ // failure to collect will take roughly 5 seconds.
+ val ref = WeakReference( Stream.from(1).take(500) )
+
+ def gcAndThrowIfCollected(n: Int): Unit = {
+ System.gc() // try to GC
+ Thread.sleep(10) // give it 10 ms
+ if (ref.get.isEmpty) throw new RuntimeException(msgSuccessGC) // we're done if head collected
+ f(n)
+ }
+
+ val res = Try { op(ref(), gcAndThrowIfCollected) }.failed // success is indicated by an
+ val msg = res.map(_.getMessage).getOrElse(msgFailureGC) // exception with expected message
+ // failure is indicated by no
+ assertTrue(msg == msgSuccessGC) // exception, or one with different message
+ }
+
+ @Test
+ def foreach_allows_GC() {
+ assertStreamOpAllowsGC(_.foreach(_), _ => ())
+ }
+
+ @Test
+ def filter_all_foreach_allows_GC() {
+ assertStreamOpAllowsGC(_.filter(_ => true).foreach(_), _ => ())
+ }
+
+ @Test // SI-8990
+ def withFilter_after_first_foreach_allows_GC: Unit = {
+ assertStreamOpAllowsGC(_.withFilter(_ > 1).foreach(_), _ => ())
+ }
+
+ @Test // SI-8990
+ def withFilter_after_first_withFilter_foreach_allows_GC: Unit = {
+ assertStreamOpAllowsGC(_.withFilter(_ > 1).withFilter(_ < 100).foreach(_), _ => ())
+ }
+
+ @Test // SI-8990
+ def withFilter_can_retry_after_exception_thrown_in_filter: Unit = {
+ // use mutable state to control an intermittent failure in filtering the Stream
+ var shouldThrow = true
+
+ val wf = Stream.from(1).take(10).withFilter { n =>
+ if (shouldThrow && n == 5) throw new RuntimeException("n == 5") else n > 5
+ }
+
+ assertTrue( Try { wf.map(identity) }.isFailure ) // throws on n == 5
+
+ shouldThrow = false // won't throw next time
+
+ assertTrue( wf.map(identity).length == 5 ) // success instead of NPE
+ }
+
+ /** Test helper to verify that the given Stream operation is properly lazy in the tail */
+ def assertStreamOpLazyInTail(op: (=> Stream[Int]) => Stream[Int], expectedEvaluated: List[Int]): Unit = {
+ // mutable state to record every strict evaluation
+ var evaluated: List[Int] = Nil
+
+ def trackEffectsOnNaturals: Stream[Int] = {
+ def loop(i: Int): Stream[Int] = { evaluated ++= List(i); i #:: loop(i + 1) }
+ loop(1)
+ }
+
+ // call op on a stream which records every strict evaluation
+ val result = op(trackEffectsOnNaturals)
+
+ assertTrue( evaluated == expectedEvaluated )
+ }
+
+ @Test // SI-9134
+ def filter_map_properly_lazy_in_tail: Unit = {
+ assertStreamOpLazyInTail(_.filter(_ % 2 == 0).map(identity), List(1, 2))
+ }
+
+ @Test // SI-9134
+ def withFilter_map_properly_lazy_in_tail: Unit = {
+ assertStreamOpLazyInTail(_.withFilter(_ % 2 == 0).map(identity), List(1, 2))
+ }
+
+ @Test
+ def test_si9379() {
+ class Boom {
+ private var i = -1
+ def inc = {
+ i += 1
+ if (i > 1000) throw new NoSuchElementException("Boom! Too many elements!")
+ i
+ }
+ }
+ val b = new Boom
+ val s = Stream.continually(b.inc)
+ // zipped.toString must allow s to short-circuit evaluation
+ assertTrue((s, s).zipped.toString contains s.toString)
+ }
+}
diff --git a/test/junit/scala/collection/immutable/StringLikeTest.scala b/test/junit/scala/collection/immutable/StringLikeTest.scala
index 3722bdfe4d..50be638b89 100644
--- a/test/junit/scala/collection/immutable/StringLikeTest.scala
+++ b/test/junit/scala/collection/immutable/StringLikeTest.scala
@@ -28,10 +28,16 @@ class StringLikeTest {
@Test
def testSplitEdgeCases: Unit = {
+ val high = 0xD852.toChar
+ val low = 0xDF62.toChar
+ val surrogatepair = List(high, low).mkString
+ val twopairs = surrogatepair + "_" + surrogatepair
+
AssertUtil.assertSameElements("abcd".split('d'), Array("abc")) // not Array("abc", "")
AssertUtil.assertSameElements("abccc".split('c'), Array("ab")) // not Array("ab", "", "", "")
AssertUtil.assertSameElements("xxx".split('x'), Array[String]()) // not Array("", "", "", "")
AssertUtil.assertSameElements("".split('x'), Array("")) // not Array()
AssertUtil.assertSameElements("--ch--omp--".split("-"), Array("", "", "ch", "", "omp")) // All the cases!
+ AssertUtil.assertSameElements(twopairs.split(high), Array(twopairs)) //don't split on characters that are half a surrogate pair
}
}
diff --git a/test/junit/scala/collection/mutable/BitSetTest.scala b/test/junit/scala/collection/mutable/BitSetTest.scala
index d56cc45601..e832194989 100644
--- a/test/junit/scala/collection/mutable/BitSetTest.scala
+++ b/test/junit/scala/collection/mutable/BitSetTest.scala
@@ -28,4 +28,11 @@ class BitSetTest {
littleBitSet &= bigBitSet
assert(littleBitSet.toBitMask.length < bigBitSet.toBitMask.length, "Needlessly extended the size of bitset on &=")
}
+
+ @Test def test_SI8647() {
+ val bs = BitSet()
+ bs.map(_ + 1) // Just needs to compile
+ val xs = bs: SortedSet[Int]
+ xs.map(_ + 1) // Also should compile (did before)
+ }
}
diff --git a/test/junit/scala/collection/mutable/OpenHashMapTest.scala b/test/junit/scala/collection/mutable/OpenHashMapTest.scala
index 1459c14d78..9b5c20e01a 100644
--- a/test/junit/scala/collection/mutable/OpenHashMapTest.scala
+++ b/test/junit/scala/collection/mutable/OpenHashMapTest.scala
@@ -28,7 +28,7 @@ class OpenHashMapTest {
val fieldMirror = mirror.reflect(m).reflectField(termSym)
*/
// Use Java reflection instead for now.
- val field = m.getClass.getDeclaredField("scala$collection$mutable$OpenHashMap$$deleted")
+ val field = m.getClass.getDeclaredField("deleted")
field.setAccessible(true)
m.put(0, 0)
diff --git a/test/junit/scala/io/SourceTest.scala b/test/junit/scala/io/SourceTest.scala
index 3138a4589c..3fe48940a0 100644
--- a/test/junit/scala/io/SourceTest.scala
+++ b/test/junit/scala/io/SourceTest.scala
@@ -28,6 +28,10 @@ class SourceTest {
@Test def canIterateLines() = {
assertEquals(sampler.lines.size, (Source fromString sampler).getLines.size)
}
+ @Test def loadFromResource() = {
+ val res = Source.fromResource("rootdoc.txt")
+ assertTrue("No classpath resource found", res.getLines().size > 5)
+ }
@Test def canCustomizeReporting() = {
class CapitalReporting(is: InputStream) extends BufferedSource(is) {
override def report(pos: Int, msg: String, out: PrintStream): Unit = {
diff --git a/test/junit/scala/issues/BytecodeTests.scala b/test/junit/scala/issues/BytecodeTests.scala
index d4ed063a03..7c446894df 100644
--- a/test/junit/scala/issues/BytecodeTests.scala
+++ b/test/junit/scala/issues/BytecodeTests.scala
@@ -9,10 +9,17 @@ import scala.tools.nsc.backend.jvm.CodeGenTools._
import org.junit.Assert._
import scala.collection.JavaConverters._
import scala.tools.partest.ASMConverters._
+import scala.tools.testing.ClearAfterClass
+
+object BytecodeTests extends ClearAfterClass.Clearable {
+ var compiler = newCompiler()
+ def clear(): Unit = { compiler = null }
+}
@RunWith(classOf[JUnit4])
-class BytecodeTests {
- val compiler = newCompiler()
+class BytecodeTests extends ClearAfterClass {
+ ClearAfterClass.stateToClear = BytecodeTests
+ val compiler = BytecodeTests.compiler
@Test
def t8731(): Unit = {
@@ -59,7 +66,6 @@ class BytecodeTests {
|@AnnotB class B
""".stripMargin
- val compiler = newCompiler()
val run = new compiler.Run()
run.compileSources(List(new BatchSourceFile("AnnotA.java", annotA), new BatchSourceFile("AnnotB.java", annotB), new BatchSourceFile("Test.scala", scalaSrc)))
val outDir = compiler.settings.outputDirs.getSingleOutput.get
@@ -77,4 +83,55 @@ class BytecodeTests {
// a @Retention annotation are currently emitted as RUNTIME.
check("B.class", "AnnotB")
}
+
+ @Test
+ def t6288bJumpPosition(): Unit = {
+ val code =
+ """object Case3 { // 01
+ | def unapply(z: Any): Option[Int] = Some(-1) // 02
+ | def main(args: Array[String]) { // 03
+ | ("": Any) match { // 04
+ | case x : String => // 05
+ | println("case 0") // 06 println and jump at 6
+ | case _ => // 07
+ | println("default") // 08 println and jump at 8
+ | } // 09
+ | println("done") // 10
+ | }
+ |}
+ """.stripMargin
+ val List(mirror, module) = compileClasses(compiler)(code)
+
+ val unapplyLineNumbers = getSingleMethod(module, "unapply").instructions.filter(_.isInstanceOf[LineNumber])
+ assert(unapplyLineNumbers == List(LineNumber(2, Label(0))), unapplyLineNumbers)
+
+ import Opcodes._
+ val expected = List(
+ LineNumber(3, Label(0)),
+ LineNumber(4, Label(0)),
+ LineNumber(5, Label(5)),
+ Jump(IFNE, Label(11)),
+ Jump(GOTO, Label(20)),
+
+ LineNumber(6, Label(11)),
+ Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false),
+ Jump(GOTO, Label(33)),
+
+ LineNumber(5, Label(20)),
+ Jump(GOTO, Label(24)),
+
+ LineNumber(8, Label(24)),
+ Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false),
+ Jump(GOTO, Label(33)),
+
+ LineNumber(10, Label(33)),
+ Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false)
+ )
+
+ val mainIns = getSingleMethod(module, "main").instructions filter {
+ case _: LineNumber | _: Invoke | _: Jump => true
+ case _ => false
+ }
+ assertSameCode(mainIns, expected)
+ }
}
diff --git a/test/junit/scala/reflect/internal/TreeGenTest.scala b/test/junit/scala/reflect/internal/TreeGenTest.scala
new file mode 100644
index 0000000000..db1ea5cf6a
--- /dev/null
+++ b/test/junit/scala/reflect/internal/TreeGenTest.scala
@@ -0,0 +1,51 @@
+package scala.reflect.internal
+
+import org.junit.Assert._
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+import scala.runtime.ScalaRunTime
+import scala.tools.nsc.symtab.SymbolTableForUnitTesting
+
+@RunWith(classOf[JUnit4])
+class TreeGenTest {
+ object symbolTable extends SymbolTableForUnitTesting
+
+ import symbolTable._
+
+ @Test
+ def attributedRefToTopLevelMemberNotPrefixedByThis_t9473_a(): Unit = {
+ val SomeClass = symbolOf[Some[_]]
+ val ref = gen.mkAttributedRef(SomeClass)
+ assertEquals("scala.Some", ref.toString) // was scala.this.Some
+ ref match {
+ case sel @ Select(pre @ Ident(preName), name) =>
+ assertEquals(TermName("scala"), preName)
+ assertEquals(TypeName("Some"), name)
+ assertEquals(SomeClass, sel.symbol)
+ case _ => fail(showRaw(ref))
+ }
+ }
+
+ @Test
+ def attributedRefToTopLevelMemberNotPrefixedByThis_t9473_b(): Unit = {
+ val ScalaRuntimeModule = symbolOf[ScalaRunTime.type].sourceModule
+ val ref = gen.mkAttributedRef(ScalaRuntimeModule)
+ assertEquals("scala.runtime.ScalaRunTime", ref.toString)
+ ref match {
+ case sel @ Select(Select(Ident(TermName("scala")), TermName("runtime")), TermName("ScalaRunTime")) =>
+ case _ => fail(showRaw(ref))
+ }
+ }
+ @Test
+ def attributedRefToTopLevelMemberNotPrefixedByThis_t9473_c(): Unit = {
+ val DummyImplicitClass = symbolOf[Predef.DummyImplicit]
+ val ref = gen.mkAttributedRef(DummyImplicitClass)
+ assertEquals("scala.Predef.DummyImplicit", ref.toString)
+// ref match {
+// case sel @ Select(Select(Ident(TermName("scala")), TermName("runtime")), TermName("ScalaRunTime")) =>
+// case _ => fail(showRaw(ref))
+// }
+ }
+}
diff --git a/test/junit/scala/runtime/LambdaDeserializerTest.java b/test/junit/scala/runtime/LambdaDeserializerTest.java
new file mode 100644
index 0000000000..069eb4aab6
--- /dev/null
+++ b/test/junit/scala/runtime/LambdaDeserializerTest.java
@@ -0,0 +1,193 @@
+package scala.runtime;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.Serializable;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.SerializedLambda;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+
+public final class LambdaDeserializerTest {
+ private LambdaHost lambdaHost = new LambdaHost();
+
+ @Test
+ public void serializationPrivate() {
+ F1<Boolean, String> f1 = lambdaHost.lambdaBackedByPrivateImplMethod();
+ Assert.assertEquals(f1.apply(true), reconstitute(f1).apply(true));
+ }
+
+ @Test
+ public void serializationStatic() {
+ F1<Boolean, String> f1 = lambdaHost.lambdaBackedByStaticImplMethod();
+ Assert.assertEquals(f1.apply(true), reconstitute(f1).apply(true));
+ }
+
+ @Test
+ public void serializationVirtualMethodReference() {
+ F1<Boolean, String> f1 = lambdaHost.lambdaBackedByVirtualMethodReference();
+ Assert.assertEquals(f1.apply(true), reconstitute(f1).apply(true));
+ }
+
+ @Test
+ public void serializationInterfaceMethodReference() {
+ F1<I, Object> f1 = lambdaHost.lambdaBackedByInterfaceMethodReference();
+ I i = new I() {
+ };
+ Assert.assertEquals(f1.apply(i), reconstitute(f1).apply(i));
+ }
+
+ @Test
+ public void serializationStaticMethodReference() {
+ F1<Boolean, String> f1 = lambdaHost.lambdaBackedByStaticMethodReference();
+ Assert.assertEquals(f1.apply(true), reconstitute(f1).apply(true));
+ }
+
+ @Test
+ public void serializationNewInvokeSpecial() {
+ F0<Object> f1 = lambdaHost.lambdaBackedByConstructorCall();
+ Assert.assertEquals(f1.apply(), reconstitute(f1).apply());
+ }
+
+ @Test
+ public void uncached() {
+ F0<Object> f1 = lambdaHost.lambdaBackedByConstructorCall();
+ F0<Object> reconstituted1 = reconstitute(f1);
+ F0<Object> reconstituted2 = reconstitute(f1);
+ Assert.assertNotEquals(reconstituted1.getClass(), reconstituted2.getClass());
+ }
+
+ @Test
+ public void cached() {
+ HashMap<String, MethodHandle> cache = new HashMap<>();
+ F0<Object> f1 = lambdaHost.lambdaBackedByConstructorCall();
+ F0<Object> reconstituted1 = reconstitute(f1, cache);
+ F0<Object> reconstituted2 = reconstitute(f1, cache);
+ Assert.assertEquals(reconstituted1.getClass(), reconstituted2.getClass());
+ }
+
+ @Test
+ public void cachedStatic() {
+ HashMap<String, MethodHandle> cache = new HashMap<>();
+ F1<Boolean, String> f1 = lambdaHost.lambdaBackedByStaticImplMethod();
+ // Check that deserialization of a static lambda always returns the
+ // same instance.
+ Assert.assertSame(reconstitute(f1, cache), reconstitute(f1, cache));
+
+ // (as is the case with regular invocation.)
+ Assert.assertSame(f1, lambdaHost.lambdaBackedByStaticImplMethod());
+ }
+
+ @Test
+ public void implMethodNameChanged() {
+ F1<Boolean, String> f1 = lambdaHost.lambdaBackedByStaticImplMethod();
+ SerializedLambda sl = writeReplace(f1);
+ checkIllegalAccess(copySerializedLambda(sl, sl.getImplMethodName() + "___", sl.getImplMethodSignature()));
+ }
+
+ @Test
+ public void implMethodSignatureChanged() {
+ F1<Boolean, String> f1 = lambdaHost.lambdaBackedByStaticImplMethod();
+ SerializedLambda sl = writeReplace(f1);
+ checkIllegalAccess(copySerializedLambda(sl, sl.getImplMethodName(), sl.getImplMethodSignature().replace("Boolean", "Integer")));
+ }
+
+ private void checkIllegalAccess(SerializedLambda serialized) {
+ try {
+ LambdaDeserializer.deserializeLambda(MethodHandles.lookup(), null, serialized);
+ throw new AssertionError();
+ } catch (IllegalArgumentException iae) {
+ if (!iae.getMessage().contains("Illegal lambda deserialization")) {
+ Assert.fail("Unexpected message: " + iae.getMessage());
+ }
+ }
+ }
+
+ private SerializedLambda copySerializedLambda(SerializedLambda sl, String implMethodName, String implMethodSignature) {
+ Object[] captures = new Object[sl.getCapturedArgCount()];
+ for (int i = 0; i < captures.length; i++) {
+ captures[i] = sl.getCapturedArg(i);
+ }
+ return new SerializedLambda(loadClass(sl.getCapturingClass()), sl.getFunctionalInterfaceClass(), sl.getFunctionalInterfaceMethodName(),
+ sl.getFunctionalInterfaceMethodSignature(), sl.getImplMethodKind(), sl.getImplClass(), implMethodName, implMethodSignature,
+ sl.getInstantiatedMethodType(), captures);
+ }
+
+ private Class<?> loadClass(String className) {
+ try {
+ return Class.forName(className.replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ private <A, B> A reconstitute(A f1) {
+ return reconstitute(f1, null);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <A, B> A reconstitute(A f1, java.util.HashMap<String, MethodHandle> cache) {
+ try {
+ return (A) LambdaDeserializer.deserializeLambda(LambdaHost.lookup(), cache, writeReplace(f1));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private <A> SerializedLambda writeReplace(A f1) {
+ try {
+ Method writeReplace = f1.getClass().getDeclaredMethod("writeReplace");
+ writeReplace.setAccessible(true);
+ return (SerializedLambda) writeReplace.invoke(f1);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
+
+
+interface F1<A, B> extends Serializable {
+ B apply(A a);
+}
+
+interface F0<A> extends Serializable {
+ A apply();
+}
+
+class LambdaHost {
+ public F1<Boolean, String> lambdaBackedByPrivateImplMethod() {
+ int local = 42;
+ return (b) -> Arrays.asList(local, b ? "true" : "false", LambdaHost.this).toString();
+ }
+
+ @SuppressWarnings("Convert2MethodRef")
+ public F1<Boolean, String> lambdaBackedByStaticImplMethod() {
+ return (b) -> String.valueOf(b);
+ }
+
+ public F1<Boolean, String> lambdaBackedByStaticMethodReference() {
+ return String::valueOf;
+ }
+
+ public F1<Boolean, String> lambdaBackedByVirtualMethodReference() {
+ return Object::toString;
+ }
+
+ public F1<I, Object> lambdaBackedByInterfaceMethodReference() {
+ return I::i;
+ }
+
+ public F0<Object> lambdaBackedByConstructorCall() {
+ return String::new;
+ }
+
+ public static MethodHandles.Lookup lookup() {
+ return MethodHandles.lookup();
+ }
+}
+
+interface I {
+ default String i() { return "i"; };
+}
diff --git a/test/junit/scala/runtime/ScalaRunTimeTest.scala b/test/junit/scala/runtime/ScalaRunTimeTest.scala
index 9da197c71a..728d8c0ce9 100644
--- a/test/junit/scala/runtime/ScalaRunTimeTest.scala
+++ b/test/junit/scala/runtime/ScalaRunTimeTest.scala
@@ -67,4 +67,61 @@ class ScalaRunTimeTest {
val c = new C()
assertFalse(c.toString, isTuple(c))
}
+
+ @Test
+ def testStingOf() {
+ import ScalaRunTime.stringOf
+ import scala.collection._
+ import parallel.ParIterable
+
+ assertEquals("null", stringOf(null))
+ assertEquals( "\"\"", stringOf(""))
+
+ assertEquals("abc", stringOf("abc"))
+ assertEquals("\" abc\"", stringOf(" abc"))
+ assertEquals("\"abc \"", stringOf("abc "))
+
+ assertEquals("""Array()""", stringOf(Array.empty[AnyRef]))
+ assertEquals("""Array()""", stringOf(Array.empty[Int]))
+ assertEquals("""Array(1, 2, 3)""", stringOf(Array(1, 2, 3)))
+ assertEquals("""Array(a, "", " c", null)""", stringOf(Array("a", "", " c", null)))
+ assertEquals("""Array(Array("", 1, Array(5)), Array(1))""",
+ stringOf(Array(Array("", 1, Array(5)), Array(1))))
+
+ val map = Map(1->"", 2->"a", 3->" a", 4->null)
+ assertEquals(s"""${map.stringPrefix}(1 -> "", 2 -> a, 3 -> " a", 4 -> null)""", stringOf(map))
+ assertEquals(s"""${map.stringPrefix}(1 -> "", 2 -> a)""", stringOf(map, 2))
+
+ val iterable = Iterable("a", "", " c", null)
+ assertEquals(s"""${iterable.stringPrefix}(a, "", " c", null)""", stringOf(iterable))
+ assertEquals(s"""${iterable.stringPrefix}(a, "")""", stringOf(iterable, 2))
+
+ val parIterable = ParIterable("a", "", " c", null)
+ assertEquals(s"""${parIterable.stringPrefix}(a, "", " c", null)""", stringOf(parIterable))
+ assertEquals(s"""${parIterable.stringPrefix}(a, "")""", stringOf(parIterable, 2))
+
+ val traversable = new Traversable[Int] {
+ def foreach[U](f: Int => U): Unit = (0 to 3).foreach(f)
+ }
+ assertEquals(s"${traversable.stringPrefix}(0, 1, 2, 3)", stringOf(traversable))
+ assertEquals(s"${traversable.stringPrefix}(0, 1)", stringOf(traversable, 2))
+
+ val tuple1 = Tuple1(0)
+ assertEquals("(0,)", stringOf(tuple1))
+ assertEquals("(0,)", stringOf(tuple1, 0))
+
+ val tuple2 = Tuple2(0, 1)
+ assertEquals("(0,1)", stringOf(tuple2))
+ assertEquals("(0,1)", stringOf(tuple2, 0))
+
+ val tuple3 = Tuple3(0, 1, 2)
+ assertEquals("(0,1,2)", stringOf(tuple3))
+ assertEquals("(0,1,2)", stringOf(tuple3, 0))
+
+ val x = new Object {
+ override def toString(): String = "this is the stringOf string"
+ }
+ assertEquals(stringOf(x), "this is the stringOf string")
+ assertEquals(stringOf(x, 2), "this is the stringOf string")
+ }
}
diff --git a/test/junit/scala/sys/process/t7350.scala b/test/junit/scala/sys/process/t7350.scala
new file mode 100644
index 0000000000..7f3e8897f2
--- /dev/null
+++ b/test/junit/scala/sys/process/t7350.scala
@@ -0,0 +1,298 @@
+
+package scala.sys.process
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import java.io.{InputStream, OutputStream, PipedInputStream, PipedOutputStream, ByteArrayInputStream,
+ ByteArrayOutputStream, IOException, Closeable}
+import java.lang.reflect.InvocationTargetException
+import scala.concurrent.{Await, Future}
+import scala.concurrent.duration.{Duration, SECONDS}
+import scala.concurrent.ExecutionContext.Implicits.global
+import scala.util.control.Exception.ignoring
+
+// Each test normally ends in a moment, but for failure cases, waits until one second.
+
+@RunWith(classOf[JUnit4])
+class PipedProcessTest {
+ class ProcessMock(error: Boolean) extends Process {
+ var destroyCount = 0
+ def exitValue(): Int = {
+ if (error) {
+ throw new InterruptedException()
+ }
+ 0
+ }
+ def destroy(): Unit = { destroyCount += 1 }
+ }
+
+ class ProcessBuilderMock(process: Process, error: Boolean) extends ProcessBuilder.AbstractBuilder {
+ override def run(io: ProcessIO): Process = {
+ if (error) {
+ throw new IOException()
+ }
+ process
+ }
+ }
+
+ class PipeSinkMock extends Process.PipeSink("PipeSinkMock") {
+ var releaseCount = 0
+ override val pipe = null
+ override val sink = null
+ override def run(): Unit = {}
+ override def connectOut(out: OutputStream): Unit = {}
+ override def connectIn(pipeOut: PipedOutputStream): Unit = {}
+ override def release(): Unit = { releaseCount += 1 }
+ }
+
+ class PipeSourceMock extends Process.PipeSource("PipeSourceMock") {
+ var releaseCount = 0
+ override val pipe = null
+ override val source = null
+ override def run(): Unit = {}
+ override def connectIn(in: InputStream): Unit = {}
+ override def connectOut(sink: Process.PipeSink): Unit = {}
+ override def release(): Unit = { releaseCount += 1 }
+ }
+
+ class PipedProcesses(a: ProcessBuilder, b: ProcessBuilder, defaultIO: ProcessIO, toError: Boolean)
+ extends Process.PipedProcesses(a, b, defaultIO, toError) {
+ def callRunAndExitValue(source: Process.PipeSource, sink: Process.PipeSink) = {
+ val m = classOf[Process.PipedProcesses].getDeclaredMethod("runAndExitValue", classOf[Process.PipeSource], classOf[Process.PipeSink])
+ m.setAccessible(true)
+ try m.invoke(this, source, sink).asInstanceOf[Option[Int]]
+ catch {
+ case err: InvocationTargetException => throw err.getTargetException
+ }
+ }
+ }
+
+ // PipedProcesses need not to release resources when it normally end
+ @Test
+ def normallyEnd() {
+ val io = BasicIO(false, ProcessLogger(_ => ()))
+ val source = new PipeSourceMock
+ val sink = new PipeSinkMock
+ val a = new ProcessMock(error = false)
+ val b = new ProcessMock(error = false)
+ val p = new PipedProcesses(new ProcessBuilderMock(a, error = false), new ProcessBuilderMock(b, error = false), io, false)
+ val f = Future {
+ p.callRunAndExitValue(source, sink)
+ }
+ Await.result(f, Duration(1, SECONDS))
+ assert(source.releaseCount == 0)
+ assert(sink.releaseCount == 0)
+ assert(a.destroyCount == 0)
+ assert(b.destroyCount == 0)
+ }
+
+ // PipedProcesses must release resources when b.run() failed
+ @Test
+ def bFailed() {
+ val io = BasicIO(false, ProcessLogger(_ => ()))
+ val source = new PipeSourceMock
+ val sink = new PipeSinkMock
+ val a = new ProcessMock(error = false)
+ val b = new ProcessMock(error = false)
+ val p = new PipedProcesses(new ProcessBuilderMock(a, error = false), new ProcessBuilderMock(b, error = true), io, false)
+ val f = Future {
+ ignoring(classOf[IOException]) {
+ p.callRunAndExitValue(source, sink)
+ }
+ }
+ Await.result(f, Duration(1, SECONDS))
+ assert(source.releaseCount == 1)
+ assert(sink.releaseCount == 1)
+ assert(a.destroyCount == 0)
+ assert(b.destroyCount == 0)
+ }
+
+ // PipedProcesses must release resources when a.run() failed
+ @Test
+ def aFailed() {
+ val io = BasicIO(false, ProcessLogger(_ => ()))
+ val source = new PipeSourceMock
+ val sink = new PipeSinkMock
+ val a = new ProcessMock(error = false)
+ val b = new ProcessMock(error = false)
+ val p = new PipedProcesses(new ProcessBuilderMock(a, error = true), new ProcessBuilderMock(b, error = false), io, false)
+ val f = Future {
+ ignoring(classOf[IOException]) {
+ p.callRunAndExitValue(source, sink)
+ }
+ }
+ Await.result(f, Duration(1, SECONDS))
+ assert(source.releaseCount == 1)
+ assert(sink.releaseCount == 1)
+ assert(a.destroyCount == 0)
+ assert(b.destroyCount == 1)
+ }
+
+ // PipedProcesses must release resources when interrupted during waiting for first.exitValue()
+ @Test
+ def firstInterrupted() {
+ val io = BasicIO(false, ProcessLogger(_ => ()))
+ val source = new PipeSourceMock
+ val sink = new PipeSinkMock
+ val a = new ProcessMock(error = true)
+ val b = new ProcessMock(error = false)
+ val p = new PipedProcesses(new ProcessBuilderMock(a, error = false), new ProcessBuilderMock(b, error = false), io, false)
+ val f = Future {
+ p.callRunAndExitValue(source, sink)
+ }
+ Await.result(f, Duration(1, SECONDS))
+ assert(source.releaseCount == 1)
+ assert(sink.releaseCount == 1)
+ assert(a.destroyCount == 1)
+ assert(b.destroyCount == 1)
+ }
+
+ // PipedProcesses must release resources when interrupted during waiting for second.exitValue()
+ @Test
+ def secondInterrupted() {
+ val io = BasicIO(false, ProcessLogger(_ => ()))
+ val source = new PipeSourceMock
+ val sink = new PipeSinkMock
+ val a = new ProcessMock(error = false)
+ val b = new ProcessMock(error = true)
+ val p = new PipedProcesses(new ProcessBuilderMock(a, error = false), new ProcessBuilderMock(b, error = false), io, false)
+ val f = Future {
+ p.callRunAndExitValue(source, sink)
+ }
+ Await.result(f, Duration(1, SECONDS))
+ assert(source.releaseCount == 1)
+ assert(sink.releaseCount == 1)
+ assert(a.destroyCount == 1)
+ assert(b.destroyCount == 1)
+ }
+}
+
+@RunWith(classOf[JUnit4])
+class PipeSourceSinkTest {
+ def throwsIOException(f: => Unit) = {
+ try { f; false }
+ catch { case _: IOException => true }
+ }
+
+ class PipeSink extends Process.PipeSink("TestPipeSink") {
+ def ensureRunloopStarted() = {
+ while (sink.size() > 0) {
+ Thread.sleep(1)
+ }
+ }
+ def isReleased = {
+ val field = classOf[Process.PipeSink].getDeclaredField("pipe")
+ field.setAccessible(true)
+ val pipe = field.get(this).asInstanceOf[PipedInputStream]
+ !this.isAlive && throwsIOException { pipe.read() }
+ }
+ }
+
+ class PipeSource extends Process.PipeSource("TestPipeSource") {
+ def ensureRunloopStarted() = {
+ while (source.size() > 0) {
+ Thread.sleep(1)
+ }
+ }
+ def isReleased = {
+ val field = classOf[Process.PipeSource].getDeclaredField("pipe")
+ field.setAccessible(true)
+ val pipe = field.get(this).asInstanceOf[PipedOutputStream]
+ !this.isAlive && throwsIOException { pipe.write(1) }
+ }
+ }
+
+ trait CloseChecking extends Closeable {
+ var closed = false
+ override def close() = closed = true
+ }
+ class DebugOutputStream extends ByteArrayOutputStream with CloseChecking
+ class DebugInputStream(s: String) extends ByteArrayInputStream(s.getBytes()) with CloseChecking
+ class DebugInfinityInputStream extends InputStream with CloseChecking {
+ def read() = 1
+ }
+
+ def sourceSink() = {
+ val source = new PipeSource
+ val sink = new PipeSink
+ source connectOut sink
+ source.start()
+ sink.start()
+ (source, sink)
+ }
+
+ // PipeSource and PipeSink must release resources when it normally end
+ @Test
+ def normallyEnd() {
+ val in = new DebugInputStream("aaa")
+ val (source, sink) = sourceSink()
+ val out = new DebugOutputStream
+ source connectIn in
+ sink connectOut out
+ val f = Future {
+ source.join()
+ sink.join()
+ }
+ Await.result(f, Duration(1, SECONDS))
+ assert(in.closed == true)
+ assert(out.closed == true)
+ assert(source.isReleased == true)
+ assert(sink.isReleased == true)
+ }
+
+ // PipeSource and PipeSink must release resources when interrupted during waiting for source.take()
+ @Test
+ def sourceInterrupted() {
+ val (source, sink) = sourceSink()
+ val out = new DebugOutputStream
+ sink connectOut out
+ val f = Future {
+ sink.ensureRunloopStarted()
+ source.release()
+ sink.release()
+ }
+ Await.result(f, Duration(1, SECONDS))
+ assert(out.closed == true)
+ assert(source.isReleased == true)
+ assert(sink.isReleased == true)
+ }
+
+ // PipeSource and PipeSink must release resources when interrupted during waiting for sink.take()
+ @Test
+ def sinkInterrupted() {
+ val in = new DebugInputStream("aaa")
+ val (source, sink) = sourceSink()
+ source connectIn in
+ val f = Future {
+ source.ensureRunloopStarted()
+ source.release()
+ sink.release()
+ }
+ Await.result(f, Duration(1, SECONDS))
+ assert(in.closed == true)
+ assert(source.isReleased == true)
+ assert(sink.isReleased == true)
+ }
+
+ // PipeSource and PipeSink must release resources when interrupted during copy streams"
+ @Test
+ def runloopInterrupted() {
+ val in = new DebugInfinityInputStream
+ val (source, sink) = sourceSink()
+ val out = new DebugOutputStream
+ source connectIn in
+ sink connectOut out
+ val f = Future {
+ source.ensureRunloopStarted()
+ sink.ensureRunloopStarted()
+ source.release()
+ sink.release()
+ }
+ Await.result(f, Duration(1, SECONDS))
+ assert(in.closed == true)
+ assert(out.closed == true)
+ assert(source.isReleased == true)
+ assert(sink.isReleased == true)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala
index ee9580c1c3..769236ae49 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala
@@ -93,6 +93,23 @@ object CodeGenTools {
getGeneratedClassfiles(compiler.settings.outputDirs.getSingleOutput.get)
}
+ def compileTransformed(compiler: Global)(scalaCode: String, javaCode: List[(String, String)] = Nil, beforeBackend: compiler.Tree => compiler.Tree): List[(String, Array[Byte])] = {
+ compiler.settings.stopBefore.value = "jvm" :: Nil
+ val run = newRun(compiler)
+ import compiler._
+ val scalaUnit = newCompilationUnit(scalaCode, "unitTestSource.scala")
+ val javaUnits = javaCode.map(p => newCompilationUnit(p._1, p._2))
+ val units = scalaUnit :: javaUnits
+ run.compileUnits(units, run.parserPhase)
+ compiler.settings.stopBefore.value = Nil
+ scalaUnit.body = beforeBackend(scalaUnit.body)
+ checkReport(compiler, _ => false)
+ val run1 = newRun(compiler)
+ run1.compileUnits(units, run1.phaseNamed("jvm"))
+ checkReport(compiler, _ => false)
+ getGeneratedClassfiles(compiler.settings.outputDirs.getSingleOutput.get)
+ }
+
/**
* Compile multiple Scala files separately into a single output directory.
*
diff --git a/test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala b/test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala
new file mode 100644
index 0000000000..f9a55bb26e
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/DefaultMethodTest.scala
@@ -0,0 +1,43 @@
+package scala.tools.nsc.backend.jvm
+
+import org.junit.Assert._
+import org.junit.Test
+
+import scala.collection.JavaConverters
+import scala.tools.asm.Opcodes
+import scala.tools.asm.tree.ClassNode
+import scala.tools.nsc.backend.jvm.CodeGenTools._
+import JavaConverters._
+import scala.tools.testing.ClearAfterClass
+
+object DefaultMethodTest extends ClearAfterClass.Clearable {
+ var compiler = newCompiler(extraArgs = "-Ybackend:GenBCode")
+ def clear(): Unit = { compiler = null }
+}
+
+class DefaultMethodTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = DirectCompileTest
+ val compiler = DirectCompileTest.compiler
+
+ @Test
+ def defaultMethodsViaGenBCode(): Unit = {
+ import compiler._
+ val code = "package pack { trait T { def foo: Int }}"
+ object makeFooDefaultMethod extends Transformer {
+ val Foo = TermName("foo")
+ /** Transforms a single tree. */
+ override def transform(tree: compiler.Tree): compiler.Tree = tree match {
+ case dd @ DefDef(_, Foo, _, _, _, _) =>
+ dd.symbol.setFlag(reflect.internal.Flags.JAVA_DEFAULTMETHOD)
+ copyDefDef(dd)(rhs = Literal(Constant(1)).setType(definitions.IntTpe))
+ case _ => super.transform(tree)
+ }
+ }
+ val asmClasses: List[ClassNode] = readAsmClasses(compileTransformed(compiler)(code, Nil, makeFooDefaultMethod.transform(_)))
+ val foo = asmClasses.head.methods.iterator.asScala.toList.last
+ 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/IndyLambdaTest.scala b/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala
new file mode 100644
index 0000000000..f5f93fef5c
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/IndyLambdaTest.scala
@@ -0,0 +1,55 @@
+package scala.tools.nsc.backend.jvm
+
+import org.junit.Assert._
+import org.junit.{Assert, Test}
+
+import scala.tools.asm.{Handle, Opcodes}
+import scala.tools.asm.tree.InvokeDynamicInsnNode
+import scala.tools.nsc.backend.jvm.AsmUtils._
+import scala.tools.nsc.backend.jvm.CodeGenTools._
+import scala.tools.testing.ClearAfterClass
+import scala.collection.JavaConverters._
+
+object IndyLambdaTest extends ClearAfterClass.Clearable {
+ var compiler = newCompiler(extraArgs = "-Ybackend:GenBCode")
+
+ def clear(): Unit = {
+ compiler = null
+ }
+}
+
+class IndyLambdaTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = IndyLambdaTest
+ val compiler = IndyLambdaTest.compiler
+
+ @Test def boxingBridgeMethodUsedSelectively(): Unit = {
+ def implMethodDescriptorFor(code: String): String = {
+ val method = compileMethods(compiler)(s"""def f = $code """).find(_.name == "f").get
+ val x = method.instructions.iterator.asScala.toList
+ x.flatMap {
+ case insn : InvokeDynamicInsnNode => insn.bsmArgs.collect { case h : Handle => h.getDesc }
+ case _ => Nil
+ }.head
+ }
+ // unspecialized functions that have a primitive in parameter or return position
+ // give rise to a "boxing bridge" method (which has the suffix `$adapted`).
+ // This is because Scala's unboxing of null values gives zero, whereas Java's throw a NPE.
+
+ // 1. Here we show that we are calling the boxing bridge (the lambda bodies here are compiled into
+ // methods of `(I)Ljava/lang/Object;` / `(I)Ljava/lang/Object;` respectively.)
+ assertEquals("(Ljava/lang/Object;)Ljava/lang/Object;", implMethodDescriptorFor("(x: Int) => new Object"))
+ assertEquals("(Ljava/lang/Object;)Ljava/lang/Object;", implMethodDescriptorFor("(x: Object) => 0"))
+
+ // 2a. We don't need such adaptations for parameters or return values with types that differ
+ // from Object due to other generic substitution, LambdaMetafactory will downcast the arguments.
+ assertEquals("(Ljava/lang/String;)Ljava/lang/String;", implMethodDescriptorFor("(x: String) => x"))
+
+ // 2b. Testing 2a. in combination with 1.
+ assertEquals("(Ljava/lang/Object;)Ljava/lang/String;", implMethodDescriptorFor("(x: Int) => \"\""))
+ assertEquals("(Ljava/lang/String;)Ljava/lang/Object;", implMethodDescriptorFor("(x: String) => 0"))
+
+ // 3. Specialized functions, don't need any of this as they implement a method like `apply$mcII$sp`,
+ // and the (un)boxing is handled in the base class in code emitted by scalac.
+ assertEquals("(I)I", implMethodDescriptorFor("(x: Int) => x"))
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala
index 94e776aadb..a7d1dc168a 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzerTest.scala
@@ -31,25 +31,22 @@ object NullnessAnalyzerTest extends ClearAfterClass.Clearable {
class NullnessAnalyzerTest extends ClearAfterClass {
ClearAfterClass.stateToClear = NullnessAnalyzerTest
val noOptCompiler = NullnessAnalyzerTest.noOptCompiler
+ import noOptCompiler.genBCode.bTypes.backendUtils._
- def newNullnessAnalyzer(methodNode: MethodNode, classInternalName: InternalName = "C"): NullnessAnalyzer = {
- val nullnessAnalyzer = new NullnessAnalyzer
- nullnessAnalyzer.analyze(classInternalName, methodNode)
- nullnessAnalyzer
- }
+ def newNullnessAnalyzer(methodNode: MethodNode, classInternalName: InternalName = "C") = new AsmAnalyzer(methodNode, classInternalName, new NullnessAnalyzer)
- def testNullness(analyzer: NullnessAnalyzer, method: MethodNode, query: String, index: Int, nullness: Nullness): Unit = {
+ def testNullness(analyzer: AsmAnalyzer[NullnessValue], method: MethodNode, query: String, index: Int, nullness: NullnessValue): Unit = {
for (i <- findInstr(method, query)) {
- val r = analyzer.frameAt(i, method).getValue(index).nullness
+ val r = analyzer.frameAt(i).getValue(index)
assertTrue(s"Expected: $nullness, found: $r. At instr ${textify(i)}", nullness == r)
}
}
// debug / helper for writing tests
- def showAllNullnessFrames(analyzer: NullnessAnalyzer, method: MethodNode): String = {
+ def showAllNullnessFrames(analyzer: AsmAnalyzer[NullnessValue], method: MethodNode): String = {
val instrLength = method.instructions.iterator.asScala.map(textify(_).length).max
val lines = for (i <- method.instructions.iterator.asScala) yield {
- val f = analyzer.frameAt(i, method)
+ val f = analyzer.frameAt(i)
val frameString = {
if (f == null) "null"
else (0 until (f.getLocals + f.getStackSize)).iterator
@@ -77,6 +74,7 @@ class NullnessAnalyzerTest extends ClearAfterClass {
|INVOKEVIRTUAL java/lang/Object.toString ()Ljava/lang/String;: 0: NotNull, 1: NotNull
| ARETURN: 0: NotNull, 1: Unknown1
| L0: null""".stripMargin
+// println(showAllNullnessFrames(newNullnessAnalyzer(m), m))
assertEquals(showAllNullnessFrames(newNullnessAnalyzer(m), m), res)
}
@@ -84,15 +82,15 @@ class NullnessAnalyzerTest extends ClearAfterClass {
def thisNonNull(): Unit = {
val List(m) = compileMethods(noOptCompiler)("def f = this.toString")
val a = newNullnessAnalyzer(m)
- testNullness(a, m, "ALOAD 0", 0, NotNull)
+ testNullness(a, m, "ALOAD 0", 0, NotNullValue)
}
@Test
def instanceMethodCall(): Unit = {
val List(m) = compileMethods(noOptCompiler)("def f(a: String) = a.trim")
val a = newNullnessAnalyzer(m)
- testNullness(a, m, "INVOKEVIRTUAL java/lang/String.trim", 1, Unknown)
- testNullness(a, m, "ARETURN", 1, NotNull)
+ testNullness(a, m, "INVOKEVIRTUAL java/lang/String.trim", 1, UnknownValue1)
+ testNullness(a, m, "ARETURN", 1, NotNullValue)
}
@Test
@@ -110,13 +108,13 @@ class NullnessAnalyzerTest extends ClearAfterClass {
// ARETURN: 0: NotNull, 1: NotNull, 2: Unknown
for ((insn, index, nullness) <- List(
- ("+NEW", 2, Unknown), // new value at slot 2 on the stack
- ("+DUP", 3, Unknown),
- ("+INVOKESPECIAL java/lang/Object", 2, NotNull), // after calling the initializer on 3, the value at 2 becomes NotNull
- ("ASTORE 1", 1, Unknown), // before the ASTORE 1, nullness of the value in local 1 is Unknown
- ("+ASTORE 1", 1, NotNull), // after storing the value at 2 in local 1, the local 1 is NotNull
- ("+ALOAD 1", 2, NotNull), // loading the value 1 puts a NotNull value on the stack (at 2)
- ("+INVOKEVIRTUAL java/lang/Object.toString", 2, Unknown) // nullness of value returned by `toString` is Unknown
+ ("+NEW", 2, UnknownValue1), // new value at slot 2 on the stack
+ ("+DUP", 3, UnknownValue1),
+ ("+INVOKESPECIAL java/lang/Object", 2, NotNullValue), // after calling the initializer on 3, the value at 2 becomes NotNull
+ ("ASTORE 1", 1, UnknownValue1), // before the ASTORE 1, nullness of the value in local 1 is Unknown
+ ("+ASTORE 1", 1, NotNullValue), // after storing the value at 2 in local 1, the local 1 is NotNull
+ ("+ALOAD 1", 2, NotNullValue), // loading the value 1 puts a NotNull value on the stack (at 2)
+ ("+INVOKEVIRTUAL java/lang/Object.toString", 2, UnknownValue1) // nullness of value returned by `toString` is Unknown
)) testNullness(a, m, insn, index, nullness)
}
@@ -125,9 +123,9 @@ class NullnessAnalyzerTest extends ClearAfterClass {
val List(m) = compileMethods(noOptCompiler)("def f = { var a: Object = null; a }")
val a = newNullnessAnalyzer(m)
for ((insn, index, nullness) <- List(
- ("+ACONST_NULL", 2, Null),
- ("+ASTORE 1", 1, Null),
- ("+ALOAD 1", 2, Null)
+ ("+ACONST_NULL", 2, NullValue),
+ ("+ASTORE 1", 1, NullValue),
+ ("+ALOAD 1", 2, NullValue)
)) testNullness(a, m, insn, index, nullness)
}
@@ -135,15 +133,33 @@ class NullnessAnalyzerTest extends ClearAfterClass {
def stringLiteralsNotNull(): Unit = {
val List(m) = compileMethods(noOptCompiler)("""def f = { val a = "hi"; a.trim }""")
val a = newNullnessAnalyzer(m)
- testNullness(a, m, "+ASTORE 1", 1, NotNull)
+ testNullness(a, m, "+ASTORE 1", 1, NotNullValue)
}
@Test
def newArraynotNull() {
val List(m) = compileMethods(noOptCompiler)("def f = { val a = new Array[Int](2); a(0) }")
val a = newNullnessAnalyzer(m)
- testNullness(a, m, "+NEWARRAY T_INT", 2, NotNull) // new array on stack
- testNullness(a, m, "+ASTORE 1", 1, NotNull) // local var (a)
+ testNullness(a, m, "+NEWARRAY T_INT", 2, NotNullValue) // new array on stack
+ testNullness(a, m, "+ASTORE 1", 1, NotNullValue) // local var (a)
+ }
+
+ @Test
+ def mergeNullNotNull(): Unit = {
+ val code =
+ """def f(o: Object) = {
+ | var a: Object = o
+ | var c: Object = null
+ | if ("".trim eq "-") {
+ | c = o
+ | }
+ | a.toString
+ |}
+ """.stripMargin
+ val List(m) = compileMethods(noOptCompiler)(code)
+ val a = newNullnessAnalyzer(m)
+ val toSt = "+INVOKEVIRTUAL java/lang/Object.toString"
+ testNullness(a, m, toSt, 3, UnknownValue1)
}
@Test
@@ -173,22 +189,22 @@ class NullnessAnalyzerTest extends ClearAfterClass {
val toSt = "INVOKEVIRTUAL java/lang/Object.toString"
val end = s"+$toSt"
for ((insn, index, nullness) <- List(
- (trim, 0, NotNull), // this
- (trim, 1, Unknown), // parameter o
- (trim, 2, Unknown), // a
- (trim, 3, Null), // b
- (trim, 4, Null), // c
- (trim, 5, Unknown), // d
-
- (toSt, 2, Unknown), // a, still the same
- (toSt, 3, Unknown), // b, was re-assinged in both branches to Unknown
- (toSt, 4, Unknown), // c, was re-assigned in one branch to Unknown
- (toSt, 5, Null), // d, was assigned to null in both branches
-
- (end, 2, NotNull), // a, NotNull (alias of b)
- (end, 3, NotNull), // b, receiver of toString
- (end, 4, Unknown), // c, no change (not an alias of b)
- (end, 5, Null) // d, no change
+ (trim, 0, NotNullValue), // this
+ (trim, 1, UnknownValue1), // parameter o
+ (trim, 2, UnknownValue1), // a
+ (trim, 3, NullValue), // b
+ (trim, 4, NullValue), // c
+ (trim, 5, UnknownValue1), // d
+
+ (toSt, 2, UnknownValue1), // a, still the same
+ (toSt, 3, UnknownValue1), // b, was re-assinged in both branches to Unknown
+ (toSt, 4, UnknownValue1), // c, was re-assigned in one branch to Unknown
+ (toSt, 5, NullValue), // d, was assigned to null in both branches
+
+ (end, 2, NotNullValue), // a, NotNull (alias of b)
+ (end, 3, NotNullValue), // b, receiver of toString
+ (end, 4, UnknownValue1), // c, no change (not an alias of b)
+ (end, 5, NullValue) // d, no change
)) testNullness(a, m, insn, index, nullness)
}
@@ -210,11 +226,11 @@ class NullnessAnalyzerTest extends ClearAfterClass {
val trim = "INVOKEVIRTUAL java/lang/String.trim"
for ((insn, index, nullness) <- List(
- (instof, 1, Unknown), // a after INSTANCEOF
- (instof, 2, Unknown), // x after INSTANCEOF
- (tost, 1, NotNull),
- (tost, 2, NotNull),
- (trim, 3, NotNull) // receiver at `trim`
+ (instof, 1, UnknownValue1), // a after INSTANCEOF
+ (instof, 2, UnknownValue1), // x after INSTANCEOF
+ (tost, 1, NotNullValue),
+ (tost, 2, NotNullValue),
+ (trim, 3, NotNullValue) // receiver at `trim`
)) testNullness(a, m, insn, index, nullness)
}
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala
index 941a167114..4835eb3cdf 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala
@@ -26,6 +26,7 @@ object ProdConsAnalyzerTest extends ClearAfterClass.Clearable {
class ProdConsAnalyzerTest extends ClearAfterClass {
ClearAfterClass.stateToClear = ProdConsAnalyzerTest
val noOptCompiler = ProdConsAnalyzerTest.noOptCompiler
+ import noOptCompiler.genBCode.bTypes.backendUtils._
def prodToString(producer: AbstractInsnNode) = producer match {
case p: InitialProducer => p.toString
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala
index 9fda034a04..efd88f10c3 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala
@@ -6,6 +6,7 @@ import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.Test
import scala.collection.generic.Clearable
+import scala.collection.immutable.IntMap
import scala.tools.asm.Opcodes._
import org.junit.Assert._
@@ -21,25 +22,59 @@ import AsmUtils._
import BackendReporting._
import scala.collection.convert.decorateAsScala._
+import scala.tools.testing.ClearAfterClass
-@RunWith(classOf[JUnit4])
-class CallGraphTest {
- val compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:inline-global -Yopt-warnings")
- import compiler.genBCode.bTypes._
+object CallGraphTest extends ClearAfterClass.Clearable {
+ var compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:inline-global -Yopt-warnings")
+ def clear(): Unit = { compiler = null }
// allows inspecting the caches after a compilation run
- val notPerRun: List[Clearable] = List(classBTypeFromInternalName, byteCodeRepository.classes, callGraph.callsites)
+ val notPerRun: List[Clearable] = List(
+ compiler.genBCode.bTypes.classBTypeFromInternalName,
+ compiler.genBCode.bTypes.byteCodeRepository.compilingClasses,
+ compiler.genBCode.bTypes.byteCodeRepository.parsedClasses,
+ compiler.genBCode.bTypes.callGraph.callsites)
notPerRun foreach compiler.perRunCaches.unrecordCache
+}
+
+@RunWith(classOf[JUnit4])
+class CallGraphTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = CallGraphTest
+
+ val compiler = CallGraphTest.compiler
+ import compiler.genBCode.bTypes._
+ import callGraph._
- def compile(code: String, allowMessage: StoreReporter#Info => Boolean): List[ClassNode] = {
- notPerRun.foreach(_.clear())
- compileClasses(compiler)(code, allowMessage = allowMessage)
+ def compile(code: String, allowMessage: StoreReporter#Info => Boolean = _ => false): List[ClassNode] = {
+ CallGraphTest.notPerRun.foreach(_.clear())
+ compileClasses(compiler)(code, allowMessage = allowMessage).map(c => byteCodeRepository.classNode(c.name).get)
}
+ def getMethods(c: ClassNode, p: String => Boolean) = c.methods.iterator.asScala.filter(m => p(m.name)).toList.sortBy(_.name)
+ def getMethod(c: ClassNode, name: String) = getMethods(c, _ == name).head
+
def callsInMethod(methodNode: MethodNode): List[MethodInsnNode] = methodNode.instructions.iterator.asScala.collect({
case call: MethodInsnNode => call
}).toList
+ def checkCallsite(call: MethodInsnNode, callsiteMethod: MethodNode, target: MethodNode, calleeDeclClass: ClassBType,
+ safeToInline: Boolean, atInline: Boolean, atNoInline: Boolean, argInfos: IntMap[ArgInfo] = IntMap.empty) = {
+ val callsite = callGraph.callsites(callsiteMethod)(call)
+ try {
+ assert(callsite.callsiteInstruction == call)
+ assert(callsite.callsiteMethod == callsiteMethod)
+ val callee = callsite.callee.get
+ assert(callee.callee == target)
+ assert(callee.calleeDeclarationClass == calleeDeclClass)
+ assert(callee.safeToInline == safeToInline)
+ assert(callee.annotatedInline == atInline)
+ assert(callee.annotatedNoInline == atNoInline)
+ assert(callsite.argInfos == argInfos)
+ } catch {
+ case e: Throwable => println(callsite); throw e
+ }
+ }
+
@Test
def callGraphStructure(): Unit = {
val code =
@@ -83,70 +118,113 @@ class CallGraphTest {
msgCount += 1
ok exists (m.msg contains _)
}
- val List(cCls, cMod, dCls, testCls) = compile(code, checkMsg).map(c => byteCodeRepository.classNode(c.name).get)
+ val List(cCls, cMod, dCls, testCls) = compile(code, checkMsg)
assert(msgCount == 6, msgCount)
- val List(cf1, cf2, cf3, cf4, cf5, cf6, cf7) = cCls.methods.iterator.asScala.filter(_.name.startsWith("f")).toList.sortBy(_.name)
- val List(df1, df3) = dCls.methods.iterator.asScala.filter(_.name.startsWith("f")).toList.sortBy(_.name)
- val g1 = cMod.methods.iterator.asScala.find(_.name == "g1").get
- val List(t1, t2) = testCls.methods.iterator.asScala.filter(_.name.startsWith("t")).toList.sortBy(_.name)
+ val List(cf1, cf2, cf3, cf4, cf5, cf6, cf7) = getMethods(cCls, _.startsWith("f"))
+ val List(df1, df3) = getMethods(dCls, _.startsWith("f"))
+ val g1 = getMethod(cMod, "g1")
+ val List(t1, t2) = getMethods(testCls, _.startsWith("t"))
val List(cf1Call, cf2Call, cf3Call, cf4Call, cf5Call, cf6Call, cf7Call, cg1Call) = callsInMethod(t1)
val List(df1Call, df2Call, df3Call, df4Call, df5Call, df6Call, df7Call, dg1Call) = callsInMethod(t2)
- def checkCallsite(callsite: callGraph.Callsite,
- call: MethodInsnNode, callsiteMethod: MethodNode, target: MethodNode, calleeDeclClass: ClassBType,
- safeToInline: Boolean, atInline: Boolean, atNoInline: Boolean) = try {
- assert(callsite.callsiteInstruction == call)
- assert(callsite.callsiteMethod == callsiteMethod)
- val callee = callsite.callee.get
- assert(callee.callee == target)
- assert(callee.calleeDeclarationClass == calleeDeclClass)
- assert(callee.safeToInline == safeToInline)
- assert(callee.annotatedInline == atInline)
- assert(callee.annotatedNoInline == atNoInline)
-
- assert(callsite.argInfos == List()) // not defined yet
- } catch {
- case e: Throwable => println(callsite); throw e
- }
-
val cClassBType = classBTypeFromClassNode(cCls)
val cMClassBType = classBTypeFromClassNode(cMod)
val dClassBType = classBTypeFromClassNode(dCls)
- checkCallsite(callGraph.callsites(cf1Call),
- cf1Call, t1, cf1, cClassBType, false, false, false)
- checkCallsite(callGraph.callsites(cf2Call),
- cf2Call, t1, cf2, cClassBType, true, false, false)
- checkCallsite(callGraph.callsites(cf3Call),
- cf3Call, t1, cf3, cClassBType, false, true, false)
- checkCallsite(callGraph.callsites(cf4Call),
- cf4Call, t1, cf4, cClassBType, true, true, false)
- checkCallsite(callGraph.callsites(cf5Call),
- cf5Call, t1, cf5, cClassBType, false, false, true)
- checkCallsite(callGraph.callsites(cf6Call),
- cf6Call, t1, cf6, cClassBType, true, false, true)
- checkCallsite(callGraph.callsites(cf7Call),
- cf7Call, t1, cf7, cClassBType, false, true, true)
- checkCallsite(callGraph.callsites(cg1Call),
- cg1Call, t1, g1, cMClassBType, true, false, false)
-
- checkCallsite(callGraph.callsites(df1Call),
- df1Call, t2, df1, dClassBType, false, true, false)
- checkCallsite(callGraph.callsites(df2Call),
- df2Call, t2, cf2, cClassBType, true, false, false)
- checkCallsite(callGraph.callsites(df3Call),
- df3Call, t2, df3, dClassBType, true, false, false)
- checkCallsite(callGraph.callsites(df4Call),
- df4Call, t2, cf4, cClassBType, true, true, false)
- checkCallsite(callGraph.callsites(df5Call),
- df5Call, t2, cf5, cClassBType, false, false, true)
- checkCallsite(callGraph.callsites(df6Call),
- df6Call, t2, cf6, cClassBType, true, false, true)
- checkCallsite(callGraph.callsites(df7Call),
- df7Call, t2, cf7, cClassBType, false, true, true)
- checkCallsite(callGraph.callsites(dg1Call),
- dg1Call, t2, g1, cMClassBType, true, false, false)
+ checkCallsite(cf1Call, t1, cf1, cClassBType, false, false, false)
+ checkCallsite(cf2Call, t1, cf2, cClassBType, true, false, false)
+ checkCallsite(cf3Call, t1, cf3, cClassBType, false, true, false)
+ checkCallsite(cf4Call, t1, cf4, cClassBType, true, true, false)
+ checkCallsite(cf5Call, t1, cf5, cClassBType, false, false, true)
+ checkCallsite(cf6Call, t1, cf6, cClassBType, true, false, true)
+ checkCallsite(cf7Call, t1, cf7, cClassBType, false, true, true)
+ checkCallsite(cg1Call, t1, g1, cMClassBType, true, false, false)
+
+ checkCallsite(df1Call, t2, df1, dClassBType, false, true, false)
+ checkCallsite(df2Call, t2, cf2, cClassBType, true, false, false)
+ checkCallsite(df3Call, t2, df3, dClassBType, true, false, false)
+ checkCallsite(df4Call, t2, cf4, cClassBType, true, true, false)
+ checkCallsite(df5Call, t2, cf5, cClassBType, false, false, true)
+ checkCallsite(df6Call, t2, cf6, cClassBType, true, false, true)
+ checkCallsite(df7Call, t2, cf7, cClassBType, false, true, true)
+ checkCallsite(dg1Call, t2, g1, cMClassBType, true, false, false)
+ }
+
+ @Test
+ def callerSensitiveNotSafeToInline(): Unit = {
+ val code =
+ """class C {
+ | def m = java.lang.Class.forName("C")
+ |}
+ """.stripMargin
+ val List(c) = compile(code)
+ val m = getMethod(c, "m")
+ val List(fn) = callsInMethod(m)
+ val forNameMeth = byteCodeRepository.methodNode("java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;").get._1
+ val classTp = classBTypeFromInternalName("java/lang/Class")
+ val r = callGraph.callsites(m)(fn)
+ checkCallsite(fn, m, forNameMeth, classTp, safeToInline = false, atInline = false, atNoInline = false)
+ }
+
+ /**
+ * NOTE: if this test fails for you when running within the IDE, it's probably because you're
+ * using 2.12.0-M2 for compilining within the IDE, which doesn't add SAM information to the
+ * InlineInfo attribute. So the InlineInfo in the classfile for Function1 doesn't say that
+ * it's a SAM type. The test passes when running with ant (which does a full bootstrap).
+ */
+ @Test
+ def checkArgInfos(): Unit = {
+ val code =
+ """abstract class C {
+ | def h(f: Int => Int): Int = f(1)
+ | def t1 = h(x => x + 1)
+ | def t2(i: Int, f: Int => Int, z: Int) = h(f) + i - z
+ | def t3(f: Int => Int) = h(x => f(x + 1))
+ |}
+ |abstract class D {
+ | def iAmASam(x: Int): Int
+ | def selfSamCall = iAmASam(10)
+ |}
+ |""".stripMargin
+ val List(c, d) = compile(code)
+
+ def callIn(m: String) = callGraph.callsites.find(_._1.name == m).get._2.values.head
+ val t1h = callIn("t1")
+ assertEquals(t1h.argInfos.toList, List((1, FunctionLiteral)))
+
+ val t2h = callIn("t2")
+ assertEquals(t2h.argInfos.toList, List((1, ForwardedParam(2))))
+
+ val t3h = callIn("t3")
+ assertEquals(t3h.argInfos.toList, List((1, FunctionLiteral)))
+
+ val selfSamCall = callIn("selfSamCall")
+ assertEquals(selfSamCall.argInfos.toList, List((0,ForwardedParam(0))))
+ }
+
+ @Test
+ def argInfoAfterInlining(): Unit = {
+ val code =
+ """class C {
+ | def foo(f: Int => Int) = f(1) // not inlined
+ | @inline final def bar(g: Int => Int) = foo(g) // forwarded param 1
+ | @inline final def baz = foo(x => x + 1) // literal
+ |
+ | def t1 = bar(x => x + 1) // call to foo should have argInfo literal
+ | def t2(x: Int, f: Int => Int) = x + bar(f) // call to foo should have argInfo forwarded param 2
+ | def t3 = baz // call to foo should have argInfo literal
+ | def someFun: Int => Int = null
+ | def t4(x: Int) = x + bar(someFun) // call to foo has empty argInfo
+ |}
+ """.stripMargin
+
+ compile(code)
+ def callIn(m: String) = callGraph.callsites.find(_._1.name == m).get._2.values.head
+ assertEquals(callIn("t1").argInfos.toList, List((1, FunctionLiteral)))
+ assertEquals(callIn("t2").argInfos.toList, List((1, ForwardedParam(2))))
+ assertEquals(callIn("t3").argInfos.toList, List((1, FunctionLiteral)))
+ assertEquals(callIn("t4").argInfos.toList, Nil)
}
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala
new file mode 100644
index 0000000000..735bbefbbb
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala
@@ -0,0 +1,73 @@
+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.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.io._
+import scala.tools.nsc.reporters.StoreReporter
+import scala.tools.testing.AssertUtil._
+
+import CodeGenTools._
+import scala.tools.partest.ASMConverters
+import ASMConverters._
+import AsmUtils._
+
+import BackendReporting._
+
+import scala.collection.convert.decorateAsScala._
+import scala.tools.testing.ClearAfterClass
+
+object ClosureOptimizerTest extends ClearAfterClass.Clearable {
+ var compiler = newCompiler(extraArgs = "-Yopt:l:classpath -Yopt-warnings:_")
+ def clear(): Unit = { compiler = null }
+}
+
+@RunWith(classOf[JUnit4])
+class ClosureOptimizerTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = ClosureOptimizerTest
+
+ val compiler = ClosureOptimizerTest.compiler
+
+ @Test
+ def nothingTypedClosureBody(): Unit = {
+ val code =
+ """abstract class C {
+ | def isEmpty: Boolean
+ | @inline final def getOrElse[T >: C](f: => T) = if (isEmpty) f else this
+ | def t = getOrElse(throw new Error(""))
+ |}
+ """.stripMargin
+
+ val List(c) = compileClasses(compiler)(code)
+ val t = c.methods.asScala.toList.find(_.name == "t").get
+ val List(bodyCall) = findInstr(t, "INVOKESTATIC C.C$$$anonfun$1 ()Lscala/runtime/Nothing$")
+ assert(bodyCall.getNext.getOpcode == ATHROW)
+ }
+
+ @Test
+ def nullTypedClosureBody(): Unit = {
+ val code =
+ """abstract class C {
+ | def isEmpty: Boolean
+ | @inline final def getOrElse[T >: C](f: => T) = if (isEmpty) f else this
+ | def t = getOrElse(null)
+ |}
+ """.stripMargin
+
+ val List(c) = compileClasses(compiler)(code)
+ val t = c.methods.asScala.toList.find(_.name == "t").get
+ val List(bodyCall) = findInstr(t, "INVOKESTATIC C.C$$$anonfun$1 ()Lscala/runtime/Null$")
+ assert(bodyCall.getNext.getOpcode == POP)
+ assert(bodyCall.getNext.getNext.getOpcode == ACONST_NULL)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/CompactLocalVariablesTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/CompactLocalVariablesTest.scala
index 76492cfa23..cd298f822a 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/CompactLocalVariablesTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/CompactLocalVariablesTest.scala
@@ -17,8 +17,8 @@ class CompactLocalVariablesTest {
// recurse-unreachable-jumps is required for eliminating catch blocks, in the first dce round they
// are still live.only after eliminating the empty handler the catch blocks become unreachable.
- val methodOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code,compact-locals")
- val noCompactVarsCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code")
+ val methodOptCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:unreachable-code,compact-locals")
+ val noCompactVarsCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:unreachable-code")
@Test
def compactUnused(): Unit = {
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala
index 57088bdd2f..c25933e63e 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala
@@ -22,12 +22,17 @@ object InlineInfoTest extends ClearAfterClass.Clearable {
var compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:classpath")
def clear(): Unit = { compiler = null }
- def notPerRun: List[Clearable] = List(compiler.genBCode.bTypes.classBTypeFromInternalName, compiler.genBCode.bTypes.byteCodeRepository.classes)
+ def notPerRun: List[Clearable] = List(
+ compiler.genBCode.bTypes.classBTypeFromInternalName,
+ compiler.genBCode.bTypes.byteCodeRepository.compilingClasses,
+ compiler.genBCode.bTypes.byteCodeRepository.parsedClasses)
notPerRun foreach compiler.perRunCaches.unrecordCache
}
@RunWith(classOf[JUnit4])
-class InlineInfoTest {
+class InlineInfoTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = InlineInfoTest
+
val compiler = InlineInfoTest.compiler
def compile(code: String) = {
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala
index 029caa995c..5f7a6d0633 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala
@@ -13,7 +13,6 @@ import org.junit.Assert._
import scala.tools.asm.tree._
import scala.tools.asm.tree.analysis._
-import scala.tools.nsc.backend.jvm.opt.BytecodeUtils.AsmAnalyzer
import scala.tools.nsc.io._
import scala.tools.nsc.reporters.StoreReporter
import scala.tools.testing.AssertUtil._
@@ -32,7 +31,8 @@ object InlineWarningTest extends ClearAfterClass.Clearable {
val argsNoWarn = "-Ybackend:GenBCode -Yopt:l:classpath"
val args = argsNoWarn + " -Yopt-warnings"
var compiler = newCompiler(extraArgs = args)
- def clear(): Unit = { compiler = null }
+ var compilerWarnAll = newCompiler(extraArgs = argsNoWarn + " -Yopt-warnings:_")
+ def clear(): Unit = { compiler = null; compilerWarnAll = null }
}
@RunWith(classOf[JUnit4])
@@ -40,8 +40,9 @@ class InlineWarningTest extends ClearAfterClass {
ClearAfterClass.stateToClear = InlineWarningTest
val compiler = InlineWarningTest.compiler
+ val compilerWarnAll = InlineWarningTest.compilerWarnAll
- def compile(scalaCode: String, javaCode: List[(String, String)] = Nil, allowMessage: StoreReporter#Info => Boolean = _ => false): List[ClassNode] = {
+ def compile(scalaCode: String, javaCode: List[(String, String)] = Nil, allowMessage: StoreReporter#Info => Boolean = _ => false, compiler: Global = compiler): List[ClassNode] = {
compileClasses(compiler)(scalaCode, javaCode, allowMessage)
}
@@ -172,6 +173,33 @@ class InlineWarningTest extends ClearAfterClass {
}
@Test
+ def dontWarnWhenNotIlnineAnnotated(): Unit = {
+ val code =
+ """class M {
+ | final def f(t: Int => Int) = {
+ | @noinline def nested = 0
+ | nested + t(1)
+ | }
+ | def t = f(x => x + 1)
+ |}
+ |
+ |class N {
+ | def t(a: M) = a.f(x => x + 1)
+ |}
+ """.stripMargin
+ compile(code, allowMessage = _ => false) // no warnings allowed
+
+ val warn =
+ """M::f(Lscala/Function1;)I could not be inlined:
+ |The callee M::f(Lscala/Function1;)I contains the instruction INVOKESPECIAL M.nested$1 ()I
+ |that would cause an IllegalAccessError when inlined into class N""".stripMargin
+
+ var c = 0
+ compile(code, compiler = compilerWarnAll, allowMessage = i => { c += 1; i.msg contains warn })
+ assert(c == 1, c)
+ }
+
+ @Test
def cannotMixStrictfp(): Unit = {
val code =
"""import annotation.strictfp
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 7ed0e13226..f1be44a094 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala
@@ -67,7 +67,7 @@ class InlinerIllegalAccessTest extends ClearAfterClass {
check(dClass, assertEmpty)
check(eClass, assertEmpty) // C is public, so accessible in E
- byteCodeRepository.classes.clear()
+ byteCodeRepository.parsedClasses.clear()
classBTypeFromInternalName.clear()
cClass.access &= ~ACC_PUBLIC // ftw
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 0309bb97cc..1108a37266 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
@@ -6,14 +6,14 @@ import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.Test
import scala.collection.generic.Clearable
+import scala.collection.immutable.IntMap
import scala.collection.mutable.ListBuffer
-import scala.reflect.internal.util.BatchSourceFile
+import scala.reflect.internal.util.{NoPosition, 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.AsmAnalyzer
import scala.tools.nsc.io._
import scala.tools.nsc.reporters.StoreReporter
import scala.tools.testing.AssertUtil._
@@ -33,7 +33,11 @@ object InlinerTest extends ClearAfterClass.Clearable {
var compiler = newCompiler(extraArgs = args)
// allows inspecting the caches after a compilation run
- def notPerRun: List[Clearable] = List(compiler.genBCode.bTypes.classBTypeFromInternalName, compiler.genBCode.bTypes.byteCodeRepository.classes, compiler.genBCode.bTypes.callGraph.callsites)
+ def notPerRun: List[Clearable] = List(
+ compiler.genBCode.bTypes.classBTypeFromInternalName,
+ compiler.genBCode.bTypes.byteCodeRepository.compilingClasses,
+ compiler.genBCode.bTypes.byteCodeRepository.parsedClasses,
+ compiler.genBCode.bTypes.callGraph.callsites)
notPerRun foreach compiler.perRunCaches.unrecordCache
def clear(): Unit = { compiler = null }
@@ -64,10 +68,15 @@ class InlinerTest extends ClearAfterClass {
val compiler = InlinerTest.compiler
import compiler.genBCode.bTypes._
+ import compiler.genBCode.bTypes.backendUtils._
def compile(scalaCode: String, javaCode: List[(String, String)] = Nil, allowMessage: StoreReporter#Info => Boolean = _ => false): List[ClassNode] = {
InlinerTest.notPerRun.foreach(_.clear())
compileClasses(compiler)(scalaCode, javaCode, allowMessage)
+ // Use the class nodes stored in the byteCodeRepository. The ones returned by compileClasses are not the same,
+ // these are created new from the classfile byte array. They are completely separate instances which cannot
+ // be used to look up methods / callsites in the callGraph hash maps for example.
+ byteCodeRepository.compilingClasses.valuesIterator.toList.sortBy(_.name)
}
def checkCallsite(callsite: callGraph.Callsite, callee: MethodNode) = {
@@ -79,8 +88,23 @@ class InlinerTest extends ClearAfterClass {
assert(callsite.callee.get.callee == callee, callsite.callee.get.callee.name)
}
+ def makeInlineRequest( callsiteInstruction: MethodInsnNode, callsiteMethod: MethodNode, callsiteClass: ClassBType,
+ callee: MethodNode, calleeDeclarationClass: ClassBType,
+ callsiteStackHeight: Int, receiverKnownNotNull: Boolean,
+ post: List[inlinerHeuristics.PostInlineRequest] = Nil) = inlinerHeuristics.InlineRequest(
+ callsite = callGraph.Callsite(
+ callsiteInstruction = callsiteInstruction,
+ callsiteMethod = callsiteMethod,
+ callsiteClass = callsiteClass,
+ callee = Right(callGraph.Callee(callee = callee, calleeDeclarationClass = calleeDeclarationClass, safeToInline = true, safeToRewrite = false, annotatedInline = false, annotatedNoInline = false, samParamTypes = IntMap.empty, calleeInfoWarning = None)),
+ argInfos = IntMap.empty,
+ callsiteStackHeight = callsiteStackHeight,
+ receiverKnownNotNull = receiverKnownNotNull,
+ callsitePosition = NoPosition),
+ post = post)
+
// inline first invocation of f into g in class C
- def inlineTest(code: String, mod: ClassNode => Unit = _ => ()): (MethodNode, Option[CannotInlineWarning]) = {
+ def inlineTest(code: String, mod: ClassNode => Unit = _ => ()): (MethodNode, List[CannotInlineWarning]) = {
val List(cls) = compile(code)
mod(cls)
val clsBType = classBTypeFromParsedClassfile(cls.name)
@@ -90,15 +114,17 @@ class InlinerTest extends ClearAfterClass {
val analyzer = new AsmAnalyzer(g, clsBType.internalName)
- val r = inliner.inline(
- fCall,
- analyzer.frameAt(fCall).getStackSize,
- g,
- clsBType,
- f,
- clsBType,
- receiverKnownNotNull = true,
- keepLineNumbers = true)
+ val request = makeInlineRequest(
+ callsiteInstruction = fCall,
+ callsiteMethod = g,
+ callsiteClass = clsBType,
+ callee = f,
+ calleeDeclarationClass = clsBType,
+ callsiteStackHeight = analyzer.frameAt(fCall).getStackSize,
+ receiverKnownNotNull = true
+ )
+
+ val r = inliner.inline(request)
(g, r)
}
@@ -170,7 +196,7 @@ class InlinerTest extends ClearAfterClass {
val f = cls.methods.asScala.find(_.name == "f").get
f.access |= ACC_SYNCHRONIZED
})
- assert(can.get.isInstanceOf[SynchronizedMethod], can)
+ assert(can.length == 1 && can.head.isInstanceOf[SynchronizedMethod], can)
}
@Test
@@ -196,7 +222,7 @@ class InlinerTest extends ClearAfterClass {
|}
""".stripMargin
val (_, r) = inlineTest(code)
- assert(r.get.isInstanceOf[MethodWithHandlerCalledOnNonEmptyStack], r)
+ assert(r.length == 1 && r.head.isInstanceOf[MethodWithHandlerCalledOnNonEmptyStack], r)
}
@Test
@@ -228,17 +254,19 @@ class InlinerTest extends ClearAfterClass {
val analyzer = new AsmAnalyzer(h, dTp.internalName)
- val r = inliner.inline(
- gCall,
- analyzer.frameAt(gCall).getStackSize,
- h,
- dTp,
- g,
- cTp,
- receiverKnownNotNull = true,
- keepLineNumbers = true)
+ val request = makeInlineRequest(
+ callsiteInstruction = gCall,
+ callsiteMethod = h,
+ callsiteClass = dTp,
+ callee = g,
+ calleeDeclarationClass = cTp,
+ callsiteStackHeight = analyzer.frameAt(gCall).getStackSize,
+ receiverKnownNotNull = true
+ )
+
+ val r = inliner.inline(request)
- assert(r.get.isInstanceOf[IllegalAccessInstruction], r)
+ assert(r.length == 1 && r.head.isInstanceOf[IllegalAccessInstruction], r)
}
@Test
@@ -273,7 +301,7 @@ class InlinerTest extends ClearAfterClass {
assert(gIns contains invokeG, gIns) // f is inlined into g, g invokes itself recursively
assert(callGraph.callsites.size == 3, callGraph.callsites)
- for (callsite <- callGraph.callsites.values if methods.contains(callsite.callsiteMethod)) {
+ for (callsite <- callGraph.callsites.valuesIterator.flatMap(_.valuesIterator) if methods.contains(callsite.callsiteMethod)) {
checkCallsite(callsite, g)
}
}
@@ -295,8 +323,8 @@ class InlinerTest extends ClearAfterClass {
assert(gIns.count(_ == invokeG) == 2, gIns)
assert(hIns.count(_ == invokeG) == 2, hIns)
- assert(callGraph.callsites.size == 7, callGraph.callsites)
- for (callsite <- callGraph.callsites.values if methods.contains(callsite.callsiteMethod)) {
+ assert(callGraph.callsites.valuesIterator.flatMap(_.valuesIterator).size == 7, callGraph.callsites)
+ for (callsite <- callGraph.callsites.valuesIterator.flatMap(_.valuesIterator) if methods.contains(callsite.callsiteMethod)) {
checkCallsite(callsite, g)
}
}
@@ -336,7 +364,7 @@ class InlinerTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compile(code)
- assert(callGraph.callsites.values exists (_.callsiteInstruction.name == "clone"))
+ assert(callGraph.callsites.valuesIterator.flatMap(_.valuesIterator) exists (_.callsiteInstruction.name == "clone"))
}
@Test
@@ -383,16 +411,17 @@ class InlinerTest extends ClearAfterClass {
val integerClassBType = classBTypeFromInternalName("java/lang/Integer")
val lowestOneBitMethod = byteCodeRepository.methodNode(integerClassBType.internalName, "lowestOneBit", "(I)I").get._1
- val r = inliner.inline(
- callsiteIns,
- analyzer.frameAt(callsiteIns).getStackSize,
- f,
- clsBType,
- lowestOneBitMethod,
- integerClassBType,
- receiverKnownNotNull = false,
- keepLineNumbers = false)
-
+ val request = makeInlineRequest(
+ callsiteInstruction = callsiteIns,
+ callsiteMethod = f,
+ callsiteClass = clsBType,
+ callee = lowestOneBitMethod,
+ calleeDeclarationClass = integerClassBType,
+ callsiteStackHeight = analyzer.frameAt(callsiteIns).getStackSize,
+ receiverKnownNotNull = false
+ )
+
+ val r = inliner.inline(request)
assert(r.isEmpty, r)
val ins = instructionsFromMethod(f)
@@ -991,4 +1020,118 @@ class InlinerTest extends ClearAfterClass {
assert(2 == t.collect({case Ldc(_, "hai!") => }).size) // twice the body of f
assert(1 == t.collect({case Jump(IFNONNULL, _) => }).size) // one single null check
}
+
+ @Test
+ def inlineIndyLambda(): Unit = {
+ val code =
+ """object M {
+ | @inline def m(s: String) = {
+ | val f = (x: String) => x.trim
+ | f(s)
+ | }
+ |}
+ |class C {
+ | @inline final def m(s: String) = {
+ | val f = (x: String) => x.trim
+ | f(s)
+ | }
+ | def t1 = m("foo")
+ | def t2 = M.m("bar")
+ |}
+ """.stripMargin
+
+ val List(c, _, _) = compile(code)
+
+ val t1 = getSingleMethod(c, "t1")
+ assert(t1.instructions exists {
+ case _: InvokeDynamic => true
+ case _ => false
+ })
+ // the indy call is inlined into t, and the closure elimination rewrites the closure invocation to the body method
+ assertInvoke(t1, "C", "C$$$anonfun$2")
+
+ val t2 = getSingleMethod(c, "t2")
+ assert(t2.instructions exists {
+ case _: InvokeDynamic => true
+ case _ => false
+ })
+ assertInvoke(t2, "M$", "M$$$anonfun$1")
+ }
+
+ @Test
+ def inlinePostRequests(): Unit = {
+ val code =
+ """class C {
+ | final def f = 10
+ | final def g = f + 19
+ | final def h = g + 29
+ |}
+ """.stripMargin
+
+ val List(c) = compile(code)
+
+ val cTp = classBTypeFromParsedClassfile(c.name)
+
+ val f = c.methods.asScala.find(_.name == "f").get
+ val g = c.methods.asScala.find(_.name == "g").get
+ val h = c.methods.asScala.find(_.name == "h").get
+
+ val gCall = h.instructions.iterator.asScala.collect({
+ case m: MethodInsnNode if m.name == "g" => m
+ }).next()
+ val fCall = g.instructions.iterator.asScala.collect({
+ case m: MethodInsnNode if m.name == "f" => m
+ }).next()
+
+ val analyzer = new AsmAnalyzer(h, cTp.internalName)
+
+ val request = makeInlineRequest(
+ callsiteInstruction = gCall,
+ callsiteMethod = h,
+ callsiteClass = cTp,
+ callee = g,
+ calleeDeclarationClass = cTp,
+ callsiteStackHeight = analyzer.frameAt(gCall).getStackSize,
+ receiverKnownNotNull = false,
+ post = List(inlinerHeuristics.PostInlineRequest(fCall, Nil))
+ )
+
+ val r = 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)
+ }
+
+ /**
+ * NOTE: if this test fails for you when running within the IDE, it's probably because you're
+ * using 2.12.0-M2 for compilining within the IDE, which doesn't add SAM information to the
+ * InlineInfo attribute. So the InlineInfo in the classfile for Function1 doesn't say that
+ * it's a SAM type. The test passes when running with ant (which does a full bootstrap).
+ */
+ @Test
+ def inlineHigherOrder(): Unit = {
+ val code =
+ """class C {
+ | final def h(f: Int => Int): Int = f(0)
+ | def t1 = h(x => x + 1)
+ | def t2 = {
+ | val fun = (x: Int) => x + 1
+ | h(fun)
+ | }
+ | def t3(f: Int => Int) = h(f)
+ | def t4(f: Int => Int) = {
+ | val fun = f
+ | h(fun)
+ | }
+ | def t5 = h(Map(0 -> 10)) // not currently inlined
+ |}
+ """.stripMargin
+
+ val List(c) = compile(code)
+ assertInvoke(getSingleMethod(c, "t1"), "C", "C$$$anonfun$1")
+ assertInvoke(getSingleMethod(c, "t2"), "C", "C$$$anonfun$2")
+ assertInvoke(getSingleMethod(c, "t3"), "scala/Function1", "apply$mcII$sp")
+ assertInvoke(getSingleMethod(c, "t4"), "scala/Function1", "apply$mcII$sp")
+ assertInvoke(getSingleMethod(c, "t5"), "C", "h")
+ }
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala
index 5ef2458c0a..8d910629ca 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOpts.scala
@@ -16,7 +16,7 @@ import ASMConverters._
import scala.tools.testing.ClearAfterClass
object MethodLevelOpts extends ClearAfterClass.Clearable {
- var methodOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:method")
+ var methodOptCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:method")
def clear(): Unit = { methodOptCompiler = null }
}
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 f8e887426b..c07d1fe3c4 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
@@ -9,20 +9,26 @@ import scala.tools.asm.Opcodes._
import org.junit.Assert._
import CodeGenTools._
+import scala.tools.asm.tree.ClassNode
import scala.tools.nsc.backend.jvm.BTypes.{MethodInlineInfo, InlineInfo}
import scala.tools.partest.ASMConverters
import ASMConverters._
import scala.collection.convert.decorateAsScala._
+import scala.tools.testing.ClearAfterClass
-object ScalaInlineInfoTest {
+object ScalaInlineInfoTest extends ClearAfterClass.Clearable {
var compiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:none")
def clear(): Unit = { compiler = null }
}
@RunWith(classOf[JUnit4])
-class ScalaInlineInfoTest {
+class ScalaInlineInfoTest extends ClearAfterClass {
+ ClearAfterClass.stateToClear = ScalaInlineInfoTest
+
val compiler = newCompiler()
+ def inlineInfo(c: ClassNode): InlineInfo = c.attrs.asScala.collect({ case a: InlineInfoAttribute => a.inlineInfo }).head
+
@Test
def traitMembersInlineInfo(): Unit = {
val code =
@@ -58,10 +64,11 @@ class ScalaInlineInfoTest {
""".stripMargin
val cs @ List(t, tl, to, tCls) = compileClasses(compiler)(code)
- val List(info) = t.attrs.asScala.collect({ case a: InlineInfoAttribute => a.inlineInfo }).toList
- val expect = InlineInfo(
+ val info = inlineInfo(t)
+ val expect = InlineInfo (
None, // self type
false, // final class
+ None, // not a sam
Map(
("O()LT$O$;", MethodInlineInfo(true, false,false,false)),
("T$$super$toString()Ljava/lang/String;",MethodInlineInfo(false,false,false,false)),
@@ -82,4 +89,43 @@ class ScalaInlineInfoTest {
)
assert(info == expect, info)
}
+
+ @Test
+ def inlineInfoSam(): Unit = {
+ val code =
+ """abstract class C {
+ | def f = 0
+ | def g(x: Int): Int
+ | val foo = "hi"
+ |}
+ |abstract class D {
+ | val biz: Int
+ |}
+ |trait T {
+ | def h(a: String): Int
+ |}
+ |abstract class E extends T {
+ | def hihi(x: Int) = x
+ |}
+ |class F extends T {
+ | def h(a: String) = 0
+ |}
+ |trait U {
+ | def conc() = 10
+ | def nullary: Int
+ |}
+ """.stripMargin
+ val cs = compileClasses(compiler)(code)
+ val sams = cs.map(c => (c.name, inlineInfo(c).sam))
+ assertEquals(sams,
+ List(
+ ("C",Some("g(I)I")),
+ ("D",None),
+ ("E",Some("h(Ljava/lang/String;)I")),
+ ("F",None),
+ ("T",Some("h(Ljava/lang/String;)I")),
+ ("U",None),
+ ("U$class",None)))
+
+ }
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
index 902af7b7fa..86c8baa3c6 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
@@ -18,18 +18,14 @@ import scala.tools.testing.ClearAfterClass
object UnreachableCodeTest extends ClearAfterClass.Clearable {
// jvm-1.6 enables emitting stack map frames, which impacts the code generation wrt dead basic blocks,
// see comment in BCodeBodyBuilder
- var methodOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:method")
- var dceCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code")
- var 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. note that this flag triggers a deprecation warning
- var noOptNoFramesCompiler = newCompiler(extraArgs = "-target:jvm-1.5 -Ybackend:GenBCode -Yopt:l:none -deprecation")
+ var methodOptCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:method")
+ var dceCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:unreachable-code")
+ var noOptCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:none")
def clear(): Unit = {
methodOptCompiler = null
dceCompiler = null
noOptCompiler = null
- noOptNoFramesCompiler = null
}
}
@@ -40,11 +36,10 @@ class UnreachableCodeTest extends ClearAfterClass {
val methodOptCompiler = UnreachableCodeTest.methodOptCompiler
val dceCompiler = UnreachableCodeTest.dceCompiler
val noOptCompiler = UnreachableCodeTest.noOptCompiler
- val noOptNoFramesCompiler = UnreachableCodeTest.noOptNoFramesCompiler
def assertEliminateDead(code: (Instruction, Boolean)*): Unit = {
val method = genMethod()(code.map(_._1): _*)
- LocalOptImpls.removeUnreachableCodeImpl(method, "C")
+ dceCompiler.genBCode.bTypes.localOpt.removeUnreachableCodeImpl(method, "C")
val nonEliminated = instructionsFromMethod(method)
val expectedLive = code.filter(_._2).map(_._1).toList
assertSameCode(nonEliminated, expectedLive)
@@ -152,11 +147,6 @@ class UnreachableCodeTest extends ClearAfterClass {
// 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 warn = "target:jvm-1.5 is deprecated"
- val noDceNoFrames = singleMethodInstructions(noOptNoFramesCompiler)(code, allowMessage = _.msg contains warn)
- assertSameCode(noDceNoFrames.dropNonOp, List(Op(ICONST_1), Op(IRETURN), Op(ICONST_2), Op(IRETURN)))
}
@Test
diff --git a/test/junit/scala/tools/nsc/settings/ScalaVersionTest.scala b/test/junit/scala/tools/nsc/settings/ScalaVersionTest.scala
index 77a2da828e..3717f80362 100644
--- a/test/junit/scala/tools/nsc/settings/ScalaVersionTest.scala
+++ b/test/junit/scala/tools/nsc/settings/ScalaVersionTest.scala
@@ -13,6 +13,57 @@ class ScalaVersionTest {
@Test def versionUnparse() {
val v = "2.11.3"
- assertEquals(ScalaVersion(v).unparse, v)
+ assertEquals(v, ScalaVersion(v).unparse)
+ assertEquals("2.11.3-RC4", ScalaVersion("2.11.3-rc4").unparse)
+ }
+
+ // SI-9167
+ @Test def `version parses with rigor`() {
+ import settings.{ SpecificScalaVersion => V }
+ import ScalaVersion._
+
+ // no-brainers
+ assertEquals(V(2,11,7,Final), ScalaVersion("2.11.7"))
+ assertEquals(V(2,11,7,Final), ScalaVersion("2.11.7-FINAL"))
+ assertEquals(V(2,11,7,Milestone(3)), ScalaVersion("2.11.7-M3"))
+ assertEquals(V(2,11,7,RC(3)), ScalaVersion("2.11.7-RC3"))
+ assertEquals(V(2,11,7,Development("devbuild")), ScalaVersion("2.11.7-devbuild"))
+
+ // partial-brainers
+ assertEquals(V(2,11,7,Milestone(3)), ScalaVersion("2.11.7-m3"))
+ assertEquals(V(2,11,7,RC(3)), ScalaVersion("2.11.7-rc3"))
+ assertEquals(V(2,11,7,Development("maybegood")), ScalaVersion("2.11.7-maybegood"))
+ assertEquals(V(2,11,7,Development("RCCola")), ScalaVersion("2.11.7-RCCola"))
+ assertEquals(V(2,11,7,Development("RC1.5")), ScalaVersion("2.11.7-RC1.5"))
+ assertEquals(V(2,11,7,Development("")), ScalaVersion("2.11.7-"))
+ assertEquals(V(2,11,7,Development("0.5")), ScalaVersion("2.11.7-0.5"))
+ assertEquals(V(2,11,7,Development("devbuild\nSI-9167")), ScalaVersion("2.11.7-devbuild\nSI-9167"))
+ assertEquals(V(2,11,7,Development("final")), ScalaVersion("2.11.7-final"))
+
+ // oh really
+ assertEquals(NoScalaVersion, ScalaVersion("none"))
+ assertEquals(AnyScalaVersion, ScalaVersion("any"))
+
+ assertThrows[NumberFormatException] { ScalaVersion("2.11.7.2") }
+ assertThrows[NumberFormatException] { ScalaVersion("2.11.7.beta") }
+ assertThrows[NumberFormatException] { ScalaVersion("2.x.7") }
+ assertThrows[NumberFormatException] { ScalaVersion("2.-11.7") }
+ assertThrows[NumberFormatException] { ScalaVersion("2. ") }
+ assertThrows[NumberFormatException] { ScalaVersion("2.1 .7") }
+ assertThrows[NumberFormatException] { ScalaVersion("2.") }
+ assertThrows[NumberFormatException] { ScalaVersion("2..") }
+ assertThrows[NumberFormatException] { ScalaVersion("2...") }
+ assertThrows[NumberFormatException] { ScalaVersion("2-") }
+ assertThrows[NumberFormatException] { ScalaVersion("2-.") } // scalacheck territory
+ assertThrows[NumberFormatException] { ScalaVersion("any.7") }
+
+ assertThrows[NumberFormatException] ( ScalaVersion("2.11-ok"), _ ==
+ "Bad version (2.11-ok) not major[.minor[.revision[-suffix]]]" )
+
+ }
+
+ // SI-9377
+ @Test def `missing version is as good as none`() {
+ assertEquals(NoScalaVersion, ScalaVersion(""))
}
}
diff --git a/test/junit/scala/tools/nsc/settings/SettingsTest.scala b/test/junit/scala/tools/nsc/settings/SettingsTest.scala
index 96f83c4c2f..a0015f4465 100644
--- a/test/junit/scala/tools/nsc/settings/SettingsTest.scala
+++ b/test/junit/scala/tools/nsc/settings/SettingsTest.scala
@@ -172,12 +172,12 @@ class SettingsTest {
assert(residual.isEmpty)
assertTrue(s.source.value == ScalaVersion(expected))
}
- check(expected = "2.11.0") // default
+ check(expected = "2.12.0") // default
check(expected = "2.11.0", "-Xsource:2.11")
check(expected = "2.10", "-Xsource:2.10.0")
check(expected = "2.12", "-Xsource:2.12")
assertThrows[IllegalArgumentException](check(expected = "2.11", "-Xsource"), _ == "-Xsource requires an argument, the syntax is -Xsource:<version>")
assertThrows[IllegalArgumentException](check(expected = "2.11", "-Xsource", "2.11"), _ == "-Xsource requires an argument, the syntax is -Xsource:<version>")
- assertThrows[IllegalArgumentException](check(expected = "2.11", "-Xsource:2.invalid"), _ contains "There was a problem parsing 2.invalid")
+ assertThrows[IllegalArgumentException](check(expected = "2.11", "-Xsource:2.invalid"), _ contains "Bad version (2.invalid)")
}
}
diff --git a/test/junit/scala/tools/nsc/symtab/FlagsTest.scala b/test/junit/scala/tools/nsc/symtab/FlagsTest.scala
index fc0e8b0f6b..08a37fcb3c 100644
--- a/test/junit/scala/tools/nsc/symtab/FlagsTest.scala
+++ b/test/junit/scala/tools/nsc/symtab/FlagsTest.scala
@@ -86,4 +86,11 @@ class FlagsTest {
assertEquals(withFlagMask(AllFlags)(sym.setFlag(lateFlags).flags), lateFlags | lateable)
}
+
+ @Test
+ def javaClassMirrorAnnotationFlag(): Unit = {
+ import scala.reflect.runtime.universe._
+ val dep = typeOf[java.lang.Deprecated].typeSymbol
+ assertTrue(dep.isJavaAnnotation && dep.isJava)
+ }
}
diff --git a/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala b/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala
index f0f20acf07..365901c4d6 100644
--- a/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala
+++ b/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala
@@ -119,7 +119,9 @@ class SymbolTableForUnitTesting extends SymbolTable {
}
phasesArray
}
- lazy val treeInfo: scala.reflect.internal.TreeInfo{val global: SymbolTableForUnitTesting.this.type} = ???
+ lazy val treeInfo = new scala.reflect.internal.TreeInfo {
+ val global: SymbolTableForUnitTesting.this.type = SymbolTableForUnitTesting.this
+ }
val currentFreshNameCreator = new reflect.internal.util.FreshNameCreator