summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSeth Tisue <seth@tisue.net>2016-10-26 10:16:02 -0700
committerGitHub <noreply@github.com>2016-10-26 10:16:02 -0700
commit92db1dcc7f9bf7161bb986060d7151f86d5fca66 (patch)
treec4f5feff25613920a031b52285e3f19126d4e59b
parenteff0aabb7824513498a4081072845001dbe4897f (diff)
parent10336958aba9b8af5f9127a4dc21c0899836ff8d (diff)
downloadscala-92db1dcc7f9bf7161bb986060d7151f86d5fca66.tar.gz
scala-92db1dcc7f9bf7161bb986060d7151f86d5fca66.tar.bz2
scala-92db1dcc7f9bf7161bb986060d7151f86d5fca66.zip
Merge pull request #5276 from som-snytt/issue/9750
SI-9750 scala.util.Properties.isJavaAtLeast works with JDK9
-rw-r--r--src/compiler/scala/tools/nsc/util/StackTracing.scala44
-rw-r--r--src/library/scala/util/Properties.scala66
-rw-r--r--test/junit/scala/util/SpecVersionTest.scala102
3 files changed, 140 insertions, 72 deletions
diff --git a/src/compiler/scala/tools/nsc/util/StackTracing.scala b/src/compiler/scala/tools/nsc/util/StackTracing.scala
index 0765bb923f..c6749a13f3 100644
--- a/src/compiler/scala/tools/nsc/util/StackTracing.scala
+++ b/src/compiler/scala/tools/nsc/util/StackTracing.scala
@@ -8,7 +8,7 @@ private[util] trait StackTracing extends Any {
/** Format a stack trace, returning the prefix consisting of frames that satisfy
* a given predicate.
- * The format is similar to the typical case described in the JavaDoc
+ * The format is similar to the typical case described in the Javadoc
* for [[java.lang.Throwable#printStackTrace]].
* If a stack trace is truncated, it will be followed by a line of the form
* `... 3 elided`, by analogy to the lines `... 3 more` which indicate
@@ -19,25 +19,18 @@ private[util] trait StackTracing extends Any {
def stackTracePrefixString(e: Throwable)(p: StackTraceElement => Boolean): String = {
import collection.mutable.{ ArrayBuffer, ListBuffer }
import compat.Platform.EOL
- import scala.util.Properties.isJavaAtLeast
-
- val sb = ListBuffer.empty[String]
type TraceRelation = String
val Self = new TraceRelation("")
val CausedBy = new TraceRelation("Caused by: ")
val Suppressed = new TraceRelation("Suppressed: ")
- val suppressable = isJavaAtLeast("1.7")
-
- def clazz(e: Throwable) = e.getClass.getName
+ def clazz(e: Throwable): String = e.getClass.getName
def because(e: Throwable): String = e.getCause match { case null => null ; case c => header(c) }
def msg(e: Throwable): String = e.getMessage match { case null => because(e) ; case s => s }
def txt(e: Throwable): String = msg(e) match { case null => "" ; case s => s": $s" }
def header(e: Throwable): String = s"${clazz(e)}${txt(e)}"
- val indent = "\u0020\u0020"
-
val seen = new ArrayBuffer[Throwable](16)
def unseen(t: Throwable) = {
def inSeen = seen exists (_ eq t)
@@ -46,28 +39,25 @@ private[util] trait StackTracing extends Any {
interesting
}
+ val sb = ListBuffer.empty[String]
+
+ // format the stack trace, skipping the shared trace
def print(e: Throwable, r: TraceRelation, share: Array[StackTraceElement], indents: Int): Unit = if (unseen(e)) {
val trace = e.getStackTrace
- val frames = (
- if (share.nonEmpty) {
- val spare = share.reverseIterator
- val trimmed = trace.reverse dropWhile (spare.hasNext && spare.next == _)
- trimmed.reverse
- } else trace
- )
- val prefix = frames takeWhile p
- val margin = indent * indents
- val indented = margin + indent
+ val frames = if (share.isEmpty) trace else {
+ val spare = share.reverseIterator
+ val trimmed = trace.reverse dropWhile (spare.hasNext && spare.next == _)
+ trimmed.reverse
+ }
+ val prefix = frames takeWhile p
+ val margin = " " * indents
+ val indent = margin + " "
sb append s"${margin}${r}${header(e)}"
- prefix foreach (f => sb append s"${indented}at $f")
- if (frames.size < trace.size) sb append s"$indented... ${trace.size - frames.size} more"
- if (r == Self && prefix.size < frames.size) sb append s"$indented... ${frames.size - prefix.size} elided"
+ prefix foreach (f => sb append s"${margin} at $f")
+ if (frames.size < trace.size) sb append s"${margin} ... ${trace.size - frames.size} more"
+ if (r == Self && prefix.size < frames.size) sb append s"${margin} ... ${frames.size - prefix.size} elided"
print(e.getCause, CausedBy, trace, indents)
- if (suppressable) {
- import scala.language.reflectiveCalls
- type Suppressing = { def getSuppressed(): Array[Throwable] }
- for (s <- e.asInstanceOf[Suppressing].getSuppressed) print(s, Suppressed, frames, indents + 1)
- }
+ e.getSuppressed foreach (t => print(t, Suppressed, frames, indents + 1))
}
print(e, Self, share = Array.empty, indents = 0)
diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala
index fc2302d148..9d62bfe6ef 100644
--- a/src/library/scala/util/Properties.scala
+++ b/src/library/scala/util/Properties.scala
@@ -168,29 +168,61 @@ private[scala] trait PropertiesTrait {
/** Compares the given specification version to the specification version of the platform.
*
- * @param version a specification version of the form "major.minor"
- * @return `true` iff the specification version of the current runtime
- * is equal to or higher than the version denoted by the given string.
- * @throws NumberFormatException if the given string is not a version string
+ * @param version a specification version number (legacy forms acceptable)
+ * @return `true` if the specification version of the current runtime
+ * is equal to or higher than the version denoted by the given string.
+ * @throws NumberFormatException if the given string is not a version string
*
- * @example {{{
- * // In this example, the runtime's Java specification is assumed to be at version 1.7.
- * isJavaAtLeast("1.6") // true
- * isJavaAtLeast("1.7") // true
- * isJavaAtLeast("1.8") // false
- * }}}
+ * @example {{{
+ * // In this example, the runtime's Java specification is assumed to be at version 8.
+ * isJavaAtLeast("1.8") // true
+ * isJavaAtLeast("8") // true
+ * isJavaAtLeast("9") // false
+ * isJavaAtLeast("9.1") // false
+ * isJavaAtLeast("1.9") // throws
+ * }}}
*/
def isJavaAtLeast(version: String): Boolean = {
- def parts(x: String) = {
- val i = x.indexOf('.')
- if (i < 0) throw new NumberFormatException("Not a version: " + x)
- (x.substring(0, i), x.substring(i+1, x.length))
+ def versionOf(s: String, depth: Int): (Int, String) =
+ s.indexOf('.') match {
+ case 0 =>
+ (-2, s.substring(1))
+ case 1 if depth == 0 && s.charAt(0) == '1' =>
+ val r0 = s.substring(2)
+ val (v, r) = versionOf(r0, 1)
+ val n = if (v > 8 || r0.isEmpty) -2 else v // accept 1.8, not 1.9 or 1.
+ (n, r)
+ case -1 =>
+ val n = if (!s.isEmpty) s.toInt else if (depth == 0) -2 else 0
+ (n, "")
+ case i =>
+ val r = s.substring(i + 1)
+ val n = if (depth < 2 && r.isEmpty) -2 else s.substring(0, i).toInt
+ (n, r)
+ }
+ def compareVersions(s: String, v: String, depth: Int): Int = {
+ if (depth >= 3) 0
+ else {
+ val (sn, srest) = versionOf(s, depth)
+ val (vn, vrest) = versionOf(v, depth)
+ if (vn < 0) -2
+ else if (sn < vn) -1
+ else if (sn > vn) 1
+ else compareVersions(srest, vrest, depth + 1)
+ }
+ }
+ compareVersions(javaSpecVersion, version, 0) match {
+ case -2 => throw new NumberFormatException(s"Not a version: $version")
+ case i => i >= 0
}
- val (v, _v) = parts(version)
- val (s, _s) = parts(javaSpecVersion)
- s.toInt >= v.toInt && _s.toInt >= _v.toInt
}
+ /** Tests whether the major version of the platform specification is at least the given value.
+ *
+ * @param version a major version number
+ */
+ def isJavaAtLeast(version: Int): Boolean = isJavaAtLeast(version.toString)
+
// provide a main method so version info can be obtained by running this
def main(args: Array[String]) {
val writer = new PrintWriter(Console.err, true)
diff --git a/test/junit/scala/util/SpecVersionTest.scala b/test/junit/scala/util/SpecVersionTest.scala
index e3e7a978f2..4639389dd9 100644
--- a/test/junit/scala/util/SpecVersionTest.scala
+++ b/test/junit/scala/util/SpecVersionTest.scala
@@ -6,13 +6,16 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import scala.tools.testing.AssertUtil._
+
/** The java version property uses the spec version
- * and must work for all "major.minor" and fail otherwise.
+ * and must work for legacy "major.minor" and plain version_number,
+ * and fail otherwise.
*/
@RunWith(classOf[JUnit4])
class SpecVersionTest {
- val sut = new PropertiesTrait {
- override def javaSpecVersion = "1.7"
+ class TestProperties(versionAt: String) extends PropertiesTrait {
+ override def javaSpecVersion = versionAt
override protected def pickJarBasedOn: Class[_] = ???
override protected def propCategory: String = "test"
@@ -21,37 +24,80 @@ class SpecVersionTest {
override lazy val scalaProps = new java.util.Properties
}
+ @Test
+ def comparesJDK9Correctly(): Unit = {
+ val sut9 = new TestProperties("9")
+ assert(sut9 isJavaAtLeast "1")
+ assert(sut9 isJavaAtLeast "1.5")
+ assert(sut9 isJavaAtLeast "5")
+ assert(sut9 isJavaAtLeast "1.8")
+ assert(sut9 isJavaAtLeast "8")
+ assert(sut9 isJavaAtLeast "9")
+ assert(sut9.isJavaAtLeast(9))
+ assertFalse(sut9.isJavaAtLeast(10))
+ assertFalse(sut9.isJavaAtLeast("10"))
+ }
+
// SI-7265
@Test
def comparesCorrectly(): Unit = {
- assert(sut isJavaAtLeast "1.5")
- assert(sut isJavaAtLeast "1.6")
- assert(sut isJavaAtLeast "1.7")
- assert(!(sut isJavaAtLeast "1.8"))
- assert(!(sut isJavaAtLeast "1.71"))
- }
- @Test(expected = classOf[NumberFormatException])
- def badVersion(): Unit = {
- sut isJavaAtLeast "1.a"
+ val sut7 = new TestProperties("1.7")
+ assert(sut7 isJavaAtLeast "1")
+ assert(sut7 isJavaAtLeast "1.5")
+ assert(sut7 isJavaAtLeast "5")
+ assert(sut7 isJavaAtLeast "1.6")
+ assert(sut7 isJavaAtLeast "1.7")
+ assert(sut7.isJavaAtLeast(7))
+ assertFalse(sut7.isJavaAtLeast(9))
+ assertFalse(sut7 isJavaAtLeast "1.8")
+ assertFalse(sut7 isJavaAtLeast "9")
+ assertFalse(sut7 isJavaAtLeast "10")
}
- @Test(expected = classOf[NumberFormatException])
- def missingVersion(): Unit = {
- sut isJavaAtLeast "1"
- }
- @Test(expected = classOf[NumberFormatException])
- def noVersion(): Unit = {
- sut isJavaAtLeast ""
+
+ @Test def variousBadVersionStrings(): Unit = {
+ val sut = new TestProperties("9")
+ assertThrows[NumberFormatException](sut.isJavaAtLeast("1.9"), _ == "Not a version: 1.9")
+ assertThrows[NumberFormatException](sut.isJavaAtLeast("1."))
+ assertThrows[NumberFormatException](sut.isJavaAtLeast("1.8."))
+ assertThrows[NumberFormatException](sut.isJavaAtLeast("1.a"))
+ assertThrows[NumberFormatException](sut.isJavaAtLeast(""))
+ assertThrows[NumberFormatException](sut.isJavaAtLeast("."))
+ assertThrows[NumberFormatException](sut.isJavaAtLeast(".."))
+ assertThrows[NumberFormatException](sut.isJavaAtLeast(".5"))
+ assertThrows[NumberFormatException](sut.isJavaAtLeast("9-ea")) //version number, not version string
}
- @Test(expected = classOf[NumberFormatException])
- def dotOnly(): Unit = {
- sut isJavaAtLeast "."
+
+ @Test def `spec has minor or more`(): Unit = {
+ val sut = new TestProperties("9.2.5")
+ assert(sut.isJavaAtLeast(9))
+ assert(sut.isJavaAtLeast("9"))
+ assert(sut.isJavaAtLeast("9.0.1"))
+ assert(sut.isJavaAtLeast("9.2.1"))
+ assert(sut.isJavaAtLeast("8.3.1"))
+ assert(sut.isJavaAtLeast("8.3.1.1.1"))
+ assertFalse(sut.isJavaAtLeast("9.3.1"))
+ assertFalse(sut.isJavaAtLeast("10.3.1"))
}
- @Test(expected = classOf[NumberFormatException])
- def leadingDot(): Unit = {
- sut isJavaAtLeast ".5"
+
+ @Test def `compares only major minor security`(): Unit = {
+ val sut = new TestProperties("9.2.5.1.2.3")
+ assert(sut.isJavaAtLeast(9))
+ assert(sut.isJavaAtLeast("9"))
+ assert(sut.isJavaAtLeast("9.0.1"))
+ assert(sut.isJavaAtLeast("9.2.5.9.9.9"))
+ assertFalse(sut.isJavaAtLeast("9.2.6"))
}
- @Test(expected = classOf[NumberFormatException])
- def notASpec(): Unit = {
- sut isJavaAtLeast "1.7.1"
+
+ @Test def `futurely proofed`(): Unit = {
+ val sut = new TestProperties("10.2.5")
+ assert(sut.isJavaAtLeast(9))
+ assert(sut.isJavaAtLeast(10))
+ assert(sut.isJavaAtLeast("9"))
+ assert(sut.isJavaAtLeast("9.0.1"))
+ assert(sut.isJavaAtLeast("9.2.1"))
+ assert(sut.isJavaAtLeast("8.3.1"))
+ assert(sut.isJavaAtLeast("8.3.1.1.1"))
+ assert(sut.isJavaAtLeast("9.3.1"))
+ assertFalse(sut.isJavaAtLeast("10.3.1"))
}
}