summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bincompat-backward.whitelist.conf5
-rw-r--r--bincompat-forward.whitelist.conf8
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala2
-rw-r--r--src/library/scala/collection/mutable/HashMap.scala31
-rw-r--r--src/library/scala/concurrent/SyncVar.scala4
-rw-r--r--src/library/scala/sys/process/ProcessBuilderImpl.scala14
-rw-r--r--src/library/scala/sys/process/ProcessImpl.scala34
-rw-r--r--src/reflect/scala/reflect/internal/util/SourceFile.scala23
-rw-r--r--test/benchmarks/README.md7
-rw-r--r--test/benchmarks/build.sbt4
-rw-r--r--test/benchmarks/project/plugins.sbt2
-rw-r--r--test/benchmarks/src/main/scala/scala/collection/immutable/VectorMapBenchmark.scala32
-rw-r--r--test/benchmarks/src/main/scala/scala/collection/mutable/HashMapBenchmark.scala70
-rw-r--r--test/files/run/reflection-mem-typecheck.scala28
-rw-r--r--test/junit/scala/reflect/internal/util/SourceFileTest.scala19
-rw-r--r--test/junit/scala/sys/process/PipedProcessTest.scala (renamed from test/junit/scala/sys/process/t7350.scala)1
-rw-r--r--test/junit/scala/sys/process/ProcessTest.scala25
17 files changed, 238 insertions, 71 deletions
diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf
index bb94f4be6c..af80bedf5b 100644
--- a/bincompat-backward.whitelist.conf
+++ b/bincompat-backward.whitelist.conf
@@ -12,7 +12,10 @@ filter {
{
matchName="scala.reflect.runtime.SymbolLoaders#TopClassCompleter.this"
problemName=IncompatibleMethTypeProblem
+ },
+ {
+ matchName="scala.sys.process.ProcessImpl#CompoundProcess.getExitValue"
+ problemName=DirectMissingMethodProblem
}
-
]
}
diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf
index 705fa031ab..541268e50e 100644
--- a/bincompat-forward.whitelist.conf
+++ b/bincompat-forward.whitelist.conf
@@ -12,6 +12,14 @@ filter {
{
matchName="scala.reflect.runtime.SymbolLoaders#TopClassCompleter.this"
problemName=IncompatibleMethTypeProblem
+ },
+ {
+ matchName="scala.sys.process.ProcessImpl#CompoundProcess.futureValue"
+ problemName=DirectMissingMethodProblem
+ },
+ {
+ matchName="scala.sys.process.ProcessImpl#CompoundProcess.futureThread"
+ problemName=DirectMissingMethodProblem
}
]
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index c73ea54c3d..8f0625e58c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -60,7 +60,7 @@ trait Contexts { self: Analyzer =>
private lazy val allImportInfos =
mutable.Map[CompilationUnit, List[ImportInfo]]() withDefaultValue Nil
- def warnUnusedImports(unit: CompilationUnit) = {
+ def warnUnusedImports(unit: CompilationUnit) = if (!unit.isJava) {
for (imps <- allImportInfos.remove(unit)) {
for (imp <- imps.reverse.distinct) {
val used = allUsedSelectors(imp)
diff --git a/src/library/scala/collection/mutable/HashMap.scala b/src/library/scala/collection/mutable/HashMap.scala
index eab4202353..11ff1f0893 100644
--- a/src/library/scala/collection/mutable/HashMap.scala
+++ b/src/library/scala/collection/mutable/HashMap.scala
@@ -72,6 +72,37 @@ extends AbstractMap[A, B]
else Some(e.value)
}
+ override def getOrElseUpdate(key: A, defaultValue: => B): B = {
+ val i = index(elemHashCode(key))
+ val entry = findEntry(key, i)
+ if (entry != null) entry.value
+ else addEntry(createNewEntry(key, defaultValue), i)
+ }
+
+ /* inlined HashTable.findEntry0 to preserve its visibility */
+ private[this] def findEntry(key: A, h: Int): Entry = {
+ var e = table(h).asInstanceOf[Entry]
+ while (notFound(key, e))
+ e = e.next
+ e
+ }
+ private[this] def notFound(key: A, e: Entry): Boolean = (e != null) && !elemEquals(e.key, key)
+
+ /* inlined HashTable.addEntry0 to preserve its visibility */
+ private[this] def addEntry(e: Entry, h: Int): B = {
+ if (tableSize >= threshold) addEntry(e)
+ else addEntry0(e, h)
+ e.value
+ }
+
+ /* extracted to make addEntry inlinable */
+ private[this] def addEntry0(e: Entry, h: Int) {
+ e.next = table(h).asInstanceOf[Entry]
+ table(h) = e
+ tableSize += 1
+ nnSizeMapAdd(h)
+ }
+
override def put(key: A, value: B): Option[B] = {
val e = findOrAddEntry(key, value)
if (e eq null) None
diff --git a/src/library/scala/concurrent/SyncVar.scala b/src/library/scala/concurrent/SyncVar.scala
index 5fabf553bd..0e534a9b22 100644
--- a/src/library/scala/concurrent/SyncVar.scala
+++ b/src/library/scala/concurrent/SyncVar.scala
@@ -91,7 +91,7 @@ class SyncVar[A] {
// [Heather] the reason why: it doesn't take into consideration
// whether or not the SyncVar is already defined. So, set has been
// deprecated in order to eventually be able to make "setting" private
- @deprecated("use `put` instead, as `set` is potentially error-prone", "2.10.0")
+ @deprecated("use `put` to ensure a value cannot be overwritten without a corresponding `take`", "2.10.0")
// NOTE: Used by SBT 0.13.0-M2 and below
def set(x: A): Unit = setVal(x)
@@ -111,7 +111,7 @@ class SyncVar[A] {
// [Heather] the reason why: it doesn't take into consideration
// whether or not the SyncVar is already defined. So, unset has been
// deprecated in order to eventually be able to make "unsetting" private
- @deprecated("use `take` instead, as `unset` is potentially error-prone", "2.10.0")
+ @deprecated("use `take` to ensure a value is never discarded", "2.10.0")
// NOTE: Used by SBT 0.13.0-M2 and below
def unset(): Unit = synchronized {
isDefined = false
diff --git a/src/library/scala/sys/process/ProcessBuilderImpl.scala b/src/library/scala/sys/process/ProcessBuilderImpl.scala
index eef140c16a..0df2e648e0 100644
--- a/src/library/scala/sys/process/ProcessBuilderImpl.scala
+++ b/src/library/scala/sys/process/ProcessBuilderImpl.scala
@@ -53,12 +53,14 @@ private[process] trait ProcessBuilderImpl {
override def run(io: ProcessIO): Process = {
val success = new SyncVar[Boolean]
- success put false
- val t = Spawn({
- runImpl(io)
- success.put(true)
- }, io.daemonizeThreads)
-
+ def go(): Unit = {
+ var ok = false
+ try {
+ runImpl(io)
+ ok = true
+ } finally success.put(ok)
+ }
+ val t = Spawn(go(), io.daemonizeThreads)
new ThreadProcess(t, success)
}
}
diff --git a/src/library/scala/sys/process/ProcessImpl.scala b/src/library/scala/sys/process/ProcessImpl.scala
index 6da0dee056..8a0002b316 100644
--- a/src/library/scala/sys/process/ProcessImpl.scala
+++ b/src/library/scala/sys/process/ProcessImpl.scala
@@ -86,17 +86,20 @@ private[process] trait ProcessImpl {
private[process] abstract class CompoundProcess extends BasicProcess {
def isAlive() = processThread.isAlive()
def destroy() = destroyer()
- def exitValue() = getExitValue._2() getOrElse scala.sys.error("No exit code: process destroyed.")
- def start() = getExitValue
+ def exitValue() = futureValue() getOrElse scala.sys.error("No exit code: process destroyed.")
+ def start() = { futureThread ;() }
- protected lazy val (processThread, getExitValue, destroyer) = {
+ protected lazy val (processThread, (futureThread, futureValue), destroyer) = {
val code = new SyncVar[Option[Int]]()
- code.put(None)
- val thread = Spawn(code.put(runAndExitValue()))
+ val thread = Spawn {
+ var value: Option[Int] = None
+ try value = runAndExitValue()
+ finally code.put(value)
+ }
(
thread,
- Future { thread.join(); code.get },
+ Future(code.get), // thread.join()
() => thread.interrupt()
)
}
@@ -215,13 +218,15 @@ private[process] trait ProcessImpl {
}
/** A thin wrapper around a java.lang.Process. `ioThreads` are the Threads created to do I/O.
- * The implementation of `exitValue` waits until these threads die before returning. */
+ * The implementation of `exitValue` waits until these threads die before returning.
+ */
private[process] class DummyProcess(action: => Int) extends Process {
- private[this] val exitCode = Future(action)
- override def isAlive() = exitCode._1.isAlive()
- override def exitValue() = exitCode._2()
+ private[this] val (thread, value) = Future(action)
+ override def isAlive() = thread.isAlive()
+ override def exitValue() = value()
override def destroy() { }
}
+
/** A thin wrapper around a java.lang.Process. `outputThreads` are the Threads created to read from the
* output and error streams of the process. `inputThread` is the Thread created to write to the input stream of
* the process.
@@ -245,11 +250,8 @@ private[process] trait ProcessImpl {
}
}
private[process] final class ThreadProcess(thread: Thread, success: SyncVar[Boolean]) extends Process {
- override def isAlive() = thread.isAlive()
- override def exitValue() = {
- thread.join()
- if (success.get) 0 else 1
- }
- override def destroy() { thread.interrupt() }
+ override def isAlive() = thread.isAlive()
+ override def exitValue() = if (success.get) 0 else 1 // thread.join()
+ override def destroy() = thread.interrupt()
}
}
diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala
index a2642628a4..64b6972298 100644
--- a/src/reflect/scala/reflect/internal/util/SourceFile.scala
+++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala
@@ -154,18 +154,23 @@ class BatchSourceFile(val file : AbstractFile, content0: Array[Char]) extends So
case _ => false
}
- def calculateLineIndices(cs: Array[Char]) = {
- val buf = new ArrayBuffer[Int]
- buf += 0
- for (i <- 0 until cs.length) if (isAtEndOfLine(i)) buf += i + 1
- buf += cs.length // sentinel, so that findLine below works smoother
- buf.toArray
+ private lazy val lineIndices: Array[Int] = {
+ def calculateLineIndices(cs: Array[Char]) = {
+ val buf = new ArrayBuffer[Int]
+ buf += 0
+ for (i <- 0 until cs.length) if (isAtEndOfLine(i)) buf += i + 1
+ buf += cs.length // sentinel, so that findLine below works smoother
+ buf.toArray
+ }
+ calculateLineIndices(content)
}
- private lazy val lineIndices: Array[Int] = calculateLineIndices(content)
- def lineToOffset(index : Int): Int = lineIndices(index)
+ def lineToOffset(index: Int): Int = {
+ val offset = lineIndices(index)
+ if (offset < length) offset else throw new IndexOutOfBoundsException(index.toString)
+ }
- private var lastLine = 0
+ private[this] var lastLine = 0
/** Convert offset to line in this source file.
* Lines are numbered from 0.
diff --git a/test/benchmarks/README.md b/test/benchmarks/README.md
index 370d610bc4..6c77b83605 100644
--- a/test/benchmarks/README.md
+++ b/test/benchmarks/README.md
@@ -5,9 +5,7 @@ that makes use of the [SBT plugin](https://github.com/ktoso/sbt-jmh) for [JMH](h
## Running a benchmark
-The benchmarks require first building Scala into `../../build/pack` with `ant`.
-If you want to build with `sbt dist/mkPack` instead,
-you'll need to change `scalaHome` in this project.
+The benchmarks require first building Scala into `../../build/pack`.
You'll then need to know the fully-qualified name of the benchmark runner class.
The benchmarking classes are organized under `src/main/scala`,
@@ -18,8 +16,7 @@ Using this example, one would simply run
jmh:runMain scala.collection.mutable.OpenHashMapRunner
-in SBT.
-SBT should be run _from this directory_.
+in SBT, run _from this directory_ (`test/benchmarks`).
The JMH results can be found under `target/jmh-results/`.
`target` gets deleted on an SBT `clean`,
diff --git a/test/benchmarks/build.sbt b/test/benchmarks/build.sbt
index fb05fb2c99..ef603e18b3 100644
--- a/test/benchmarks/build.sbt
+++ b/test/benchmarks/build.sbt
@@ -1,5 +1,5 @@
scalaHome := Some(file("../../build/pack"))
-scalaVersion := "2.12.0-dev"
+scalaVersion := "2.12.1-dev"
scalacOptions ++= Seq("-feature", "-opt:l:classpath")
lazy val root = (project in file(".")).
@@ -7,5 +7,5 @@ lazy val root = (project in file(".")).
settings(
name := "test-benchmarks",
version := "0.0.1",
- libraryDependencies += "org.openjdk.jol" % "jol-core" % "0.4"
+ libraryDependencies += "org.openjdk.jol" % "jol-core" % "0.6"
)
diff --git a/test/benchmarks/project/plugins.sbt b/test/benchmarks/project/plugins.sbt
index aa49ad9872..1b79ce888c 100644
--- a/test/benchmarks/project/plugins.sbt
+++ b/test/benchmarks/project/plugins.sbt
@@ -1,2 +1,2 @@
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
-addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.16")
+addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.17")
diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/VectorMapBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorMapBenchmark.scala
new file mode 100644
index 0000000000..61e621dcdf
--- /dev/null
+++ b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorMapBenchmark.scala
@@ -0,0 +1,32 @@
+package scala.collection.immutable
+
+import org.openjdk.jmh.annotations._
+import org.openjdk.jmh.infra._
+import org.openjdk.jmh.runner.IterationType
+import benchmark._
+import java.util.concurrent.TimeUnit
+
+@BenchmarkMode(Array(Mode.AverageTime))
+@Fork(2)
+@Threads(1)
+@Warmup(iterations = 10)
+@Measurement(iterations = 10)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Benchmark)
+class VectorMapBenchmark {
+ @Param(Array("10", "100", "1000"))
+ var size: Int = _
+
+ var values: Vector[Any] = _
+
+ @Setup(Level.Trial) def initKeys(): Unit = {
+ values = (0 to size).map(i => (i % 4) match {
+ case 0 => i.toString
+ case 1 => i.toChar
+ case 2 => i.toDouble
+ case 3 => i.toInt
+ }).toVector
+ }
+
+ @Benchmark def groupBy = values.groupBy(_.getClass)
+}
diff --git a/test/benchmarks/src/main/scala/scala/collection/mutable/HashMapBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/mutable/HashMapBenchmark.scala
new file mode 100644
index 0000000000..3f01d154e9
--- /dev/null
+++ b/test/benchmarks/src/main/scala/scala/collection/mutable/HashMapBenchmark.scala
@@ -0,0 +1,70 @@
+package scala.collection.mutable
+
+import org.openjdk.jmh.annotations._
+import org.openjdk.jmh.infra._
+import org.openjdk.jmh.runner.IterationType
+import benchmark._
+import java.util.concurrent.TimeUnit
+
+import scala.collection.mutable
+
+@BenchmarkMode(Array(Mode.AverageTime))
+@Fork(2)
+@Threads(1)
+@Warmup(iterations = 10)
+@Measurement(iterations = 10)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Benchmark)
+class HashMapBenchmark {
+ @Param(Array("10", "100", "1000"))
+ var size: Int = _
+
+ var existingKeys: Array[Any] = _
+ var missingKeys: Array[Any] = _
+
+ @Setup(Level.Trial) def initKeys(): Unit = {
+ existingKeys = (0 to size).map(i => (i % 4) match {
+ case 0 => i.toString
+ case 1 => i.toChar
+ case 2 => i.toDouble
+ case 3 => i.toInt
+ }).toArray
+ missingKeys = (size to 2 * size).toArray
+ }
+
+ var map = new mutable.HashMap[Any, Any]
+
+ @Setup(Level.Invocation) def initializeMutable = existingKeys.foreach(v => map.put(v, v))
+
+ @TearDown(Level.Invocation) def tearDown = map.clear()
+
+ @Benchmark def getOrElseUpdate(bh: Blackhole): Unit = {
+ var i = 0;
+ while (i < size) {
+ bh.consume(map.getOrElseUpdate(existingKeys(i), -1))
+ bh.consume(map.getOrElseUpdate(missingKeys(i), -1))
+ i += 1
+ }
+ }
+
+ @Benchmark def get(bh: Blackhole): Unit = {
+ var i = 0;
+ while (i < size) {
+ bh.consume(map.get(existingKeys(i), -1))
+ bh.consume(map.get(missingKeys(i), -1))
+ i += 1
+ }
+ }
+
+ @Benchmark def put(bh: Blackhole): Any = {
+ var map = new mutable.HashMap[Any, Any]
+
+ var i = 0;
+ while (i < size) {
+ map.put(existingKeys(i), i)
+ i += 1
+ }
+
+ map
+ }
+}
diff --git a/test/files/run/reflection-mem-typecheck.scala b/test/files/run/reflection-mem-typecheck.scala
deleted file mode 100644
index 93ec1c937a..0000000000
--- a/test/files/run/reflection-mem-typecheck.scala
+++ /dev/null
@@ -1,28 +0,0 @@
-import scala.tools.partest.MemoryTest
-
-trait A { type T <: A }
-trait B { type T <: B }
-
-object Test extends MemoryTest {
- lazy val tb = {
- import scala.reflect.runtime.universe._
- import scala.reflect.runtime.{currentMirror => cm}
- import scala.tools.reflect.ToolBox
- cm.mkToolBox()
- }
-
- // I'm not sure this is a great way to test for memory leaks,
- // since we're also testing how good the JVM's GC is, and this is not easily reproduced between machines/over time
- override def maxDelta = 12
- override def calcsPerIter = 8
- override def calc() {
- var snippet = """
- trait A { type T <: A }
- trait B { type T <: B }
- def foo[T](x: List[T]) = x
- foo(List(new A {}, new B {}))
- """.trim
- snippet = snippet + "\n" + (List.fill(50)(snippet.split("\n").last) mkString "\n")
- tb.typecheck(tb.parse(snippet))
- }
-} \ No newline at end of file
diff --git a/test/junit/scala/reflect/internal/util/SourceFileTest.scala b/test/junit/scala/reflect/internal/util/SourceFileTest.scala
index cad23eba14..2f2029ad2d 100644
--- a/test/junit/scala/reflect/internal/util/SourceFileTest.scala
+++ b/test/junit/scala/reflect/internal/util/SourceFileTest.scala
@@ -5,6 +5,8 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import scala.tools.testing.AssertUtil._
+
@RunWith(classOf[JUnit4])
class SourceFileTest {
def lineContentOf(code: String, offset: Int) =
@@ -57,4 +59,21 @@ class SourceFileTest {
assertEquals("def", lineContentOf("abc\r\ndef", 8))
assertEquals("def", lineContentOf("abc\r\ndef\r\n", 9))
}
+
+ @Test def si9885_lineToOffset(): Unit = {
+ val text = "a\nb\nc\n"
+ val f = new BatchSourceFile("batch", text)
+ assertThrows[IndexOutOfBoundsException] {
+ f.lineToOffset(3)
+ }
+ assertEquals(4, f.lineToOffset(2))
+
+ val p = Position.offset(f, text.length - 1)
+ val q = Position.offset(f, f.lineToOffset(p.line - 1))
+ assertEquals(p.line, q.line)
+ assertEquals(p.column, q.column + 1)
+ assertThrows[IndexOutOfBoundsException] {
+ Position.offset(f, f.lineToOffset(p.line))
+ }
+ }
}
diff --git a/test/junit/scala/sys/process/t7350.scala b/test/junit/scala/sys/process/PipedProcessTest.scala
index 9fdcac8ccc..53f053e9aa 100644
--- a/test/junit/scala/sys/process/t7350.scala
+++ b/test/junit/scala/sys/process/PipedProcessTest.scala
@@ -12,6 +12,7 @@ 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.
+// SI-7350, SI-8768
@RunWith(classOf[JUnit4])
class PipedProcessTest {
diff --git a/test/junit/scala/sys/process/ProcessTest.scala b/test/junit/scala/sys/process/ProcessTest.scala
new file mode 100644
index 0000000000..f6d779c2c8
--- /dev/null
+++ b/test/junit/scala/sys/process/ProcessTest.scala
@@ -0,0 +1,25 @@
+package scala.sys.process
+
+import java.io.ByteArrayInputStream
+// should test from outside the package to ensure implicits work
+//import scala.sys.process._
+import scala.util.Properties._
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import org.junit.Assert.assertEquals
+
+@RunWith(classOf[JUnit4])
+class ProcessTest {
+ private def testily(body: => Unit) = if (!isWin) body
+ @Test def t10007(): Unit = testily {
+ val res = ("cat" #< new ByteArrayInputStream("lol".getBytes)).!!
+ assertEquals("lol\n", res)
+ }
+ // test non-hanging
+ @Test def t10055(): Unit = testily {
+ val res = ("cat" #< ( () => -1 ) ).!
+ assertEquals(0, res)
+ }
+}