diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2014-01-29 15:21:18 +0100 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2014-01-29 15:30:15 +0100 |
commit | 0eefa77be199b1d8186a7a6978f8e01132f35cd9 (patch) | |
tree | 1a57eea6a9f6dab2b51b073e5ffbf9e70a434e88 | |
parent | 1e9dcc2ed603e7179b7b5eee9212e73f773b02fd (diff) | |
download | scala-0eefa77be199b1d8186a7a6978f8e01132f35cd9.tar.gz scala-0eefa77be199b1d8186a7a6978f8e01132f35cd9.tar.bz2 scala-0eefa77be199b1d8186a7a6978f8e01132f35cd9.zip |
SI-8205 Avoid long, slow march to AIIOBE in SourceFile#lineContent
Fixing a regression from SI-8015.
The failure mode is kind of amusing: a while loop in `lineToString`
would count all the way to `Int.MaxValue`, and integer overflow
would foil a bounds check when looking for the 'LF' in 'CR'-'LF'.
Given that we're not a style checker to enforce that source files
end in a new-line, this commit accounts for EOF, and fixed the
overflow problem too.
A JUnit test exercises the bug and a few other variations of
`lineContent`.
While i was in the neighbourhood, I opted for a more efficient
means to slice out that line.
-rw-r--r-- | src/reflect/scala/reflect/internal/util/SourceFile.scala | 6 | ||||
-rw-r--r-- | test/junit/scala/reflect/internal/util/SourceFileTest.scala | 33 |
2 files changed, 36 insertions, 3 deletions
diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala index 9866b043bb..add072aa71 100644 --- a/src/reflect/scala/reflect/internal/util/SourceFile.scala +++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala @@ -40,8 +40,8 @@ abstract class SourceFile { def lineToString(index: Int): String = { val start = lineToOffset(index) var end = start - while (!isEndOfLine(end)) end += 1 - content.slice(start, end) mkString "" + while (!isEndOfLine(end) && end <= length) end += 1 + new String(content, start, end - start) } @tailrec @@ -136,7 +136,7 @@ class BatchSourceFile(val file : AbstractFile, val content0: Array[Char]) extend private def charAtIsEOL(idx: Int)(p: Char => Boolean) = { // don't identify the CR in CR LF as a line break, since LF will do. - def notCRLF0 = content(idx) != CR || idx + 1 >= length || content(idx + 1) != LF + def notCRLF0 = content(idx) != CR || !content.isDefinedAt(idx + 1) || content(idx + 1) != LF idx < length && notCRLF0 && p(content(idx)) } diff --git a/test/junit/scala/reflect/internal/util/SourceFileTest.scala b/test/junit/scala/reflect/internal/util/SourceFileTest.scala new file mode 100644 index 0000000000..bbd5685ef7 --- /dev/null +++ b/test/junit/scala/reflect/internal/util/SourceFileTest.scala @@ -0,0 +1,33 @@ +package scala.reflect.internal.util + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(classOf[JUnit4]) +class SourceFileTest { + def lineContentOf(code: String, offset: Int) = + Position.offset(new BatchSourceFile("", code), offset).lineContent + + @Test + def si8205_overflow(): Unit = { + val file = new BatchSourceFile("", "code no newline") + // the bug in lineToString counted until MaxValue, and the AIOOBE came from here + assertFalse(file.isEndOfLine(Int.MaxValue)) + } + + @Test + def si8205_lineToString(): Unit = { + assertEquals("", lineContentOf("", 0)) + assertEquals("abc", lineContentOf("abc", 0)) + assertEquals("abc", lineContentOf("abc", 3)) + assertEquals("code no newline", lineContentOf("code no newline", 1)) + assertEquals("", lineContentOf("\n", 0)) + assertEquals("abc", lineContentOf("abc\ndef", 0)) + assertEquals("abc", lineContentOf("abc\ndef", 3)) + assertEquals("def", lineContentOf("abc\ndef", 4)) + assertEquals("def", lineContentOf("abc\ndef", 6)) + assertEquals("def", lineContentOf("abc\ndef\n", 7)) + } +} |