summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrzegorz Kossakowski <grzegorz.kossakowski@gmail.com>2015-02-23 17:33:15 +0100
committerGrzegorz Kossakowski <grzegorz.kossakowski@gmail.com>2015-02-23 17:33:15 +0100
commitb2f5101ff99213b667a1f525e617904f82b68c01 (patch)
treeafea41027d69e44cdf7bcc9e4d737b356f80f4c9
parent47be353b7d55e2c025779caa8dcad6f65b187148 (diff)
parentb4e3becbf348bad95f48da5a94f4ddb4272b2891 (diff)
downloadscala-b2f5101ff99213b667a1f525e617904f82b68c01.tar.gz
scala-b2f5101ff99213b667a1f525e617904f82b68c01.tar.bz2
scala-b2f5101ff99213b667a1f525e617904f82b68c01.zip
Merge pull request #4307 from som-snytt/issue/4339-2.10-b
SI-4339 Backpatch event errors and attr fix
-rw-r--r--bincompat-forward.whitelist.conf8
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala4
-rw-r--r--src/library/scala/xml/parsing/MarkupParserCommon.scala3
-rw-r--r--src/library/scala/xml/parsing/TokenTests.scala56
-rwxr-xr-xsrc/library/scala/xml/pull/XMLEventReader.scala9
-rw-r--r--test/files/jvm/unittest_xml.scala4
-rw-r--r--test/files/run/t4339.check3
-rw-r--r--test/files/run/t4339.scala35
8 files changed, 80 insertions, 42 deletions
diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf
index c9cf4f74b9..1c532889c2 100644
--- a/bincompat-forward.whitelist.conf
+++ b/bincompat-forward.whitelist.conf
@@ -187,6 +187,14 @@ filter {
{
matchName="scala.xml.dtd.ElementValidator.scala$xml$dtd$ElementValidator$$find$2"
problemName=IncompatibleMethTypeProblem
+ },
+ {
+ matchName="scala.xml.pull.ExceptionEvent"
+ problemName=MissingClassProblem
+ },
+ {
+ matchName="scala.xml.pull.ExceptionEvent$"
+ problemName=MissingClassProblem
}
]
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 8d295a28d0..3ee7941995 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -367,7 +367,9 @@ trait Scanners extends ScannersCommon {
val last = if (charOffset >= 2) buf(charOffset - 2) else ' '
nextChar()
last match {
- case ' ' | '\t' | '\n' | '{' | '(' | '>' if isNameStart(ch) || ch == '!' || ch == '?' =>
+ // exclude valid xml names that happen to be Scala operator chars
+ case ' ' | '\t' | '\n' | '{' | '(' | '>' if (isNameStart(ch) && ch != ':' && !isSpecial(ch))
+ || ch == '!' || ch == '?' =>
token = XMLSTART
case _ =>
// Console.println("found '<', but last is '"+in.last+"'"); // DEBUG
diff --git a/src/library/scala/xml/parsing/MarkupParserCommon.scala b/src/library/scala/xml/parsing/MarkupParserCommon.scala
index da640484e0..4dbe5aed9d 100644
--- a/src/library/scala/xml/parsing/MarkupParserCommon.scala
+++ b/src/library/scala/xml/parsing/MarkupParserCommon.scala
@@ -58,8 +58,9 @@ private[scala] trait MarkupParserCommon extends TokenTests {
@param endCh either `'` or `"`
*/
def xAttributeValue(endCh: Char): String = {
+ require(endCh == '\'' || endCh == '"', s"Expected single or double quote, found $endCh")
val buf = new StringBuilder
- while (ch != endCh) {
+ while (ch != endCh && !eof) {
// well-formedness constraint
if (ch == '<') return errorAndResult("'<' not allowed in attrib value", "")
else if (ch == SU) truncatedError("")
diff --git a/src/library/scala/xml/parsing/TokenTests.scala b/src/library/scala/xml/parsing/TokenTests.scala
index c9cafaeea1..ce7accbd98 100644
--- a/src/library/scala/xml/parsing/TokenTests.scala
+++ b/src/library/scala/xml/parsing/TokenTests.scala
@@ -30,43 +30,25 @@ trait TokenTests {
def isAlpha(c: Char) = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
def isAlphaDigit(c: Char) = isAlpha(c) || (c >= '0' && c <= '9')
- /** {{{
- * NameChar ::= Letter | Digit | '.' | '-' | '_' | ':'
- * | CombiningChar | Extender
- * }}}
- * See [4] and Appendix B of XML 1.0 specification.
- */
- def isNameChar(ch: Char) = {
- import java.lang.Character._
- // The constants represent groups Mc, Me, Mn, Lm, and Nd.
-
- isNameStart(ch) || (getType(ch).toByte match {
- case COMBINING_SPACING_MARK |
- ENCLOSING_MARK | NON_SPACING_MARK |
- MODIFIER_LETTER | DECIMAL_DIGIT_NUMBER => true
- case _ => ".-:" contains ch
- })
- }
-
- /** {{{
- * NameStart ::= ( Letter | '_' )
- * }}}
- * where Letter means in one of the Unicode general
- * categories `{ Ll, Lu, Lo, Lt, Nl }`.
- *
- * We do not allow a name to start with `:`.
- * See [3] and Appendix B of XML 1.0 specification
- */
- def isNameStart(ch: Char) = {
- import java.lang.Character._
-
- getType(ch).toByte match {
- case LOWERCASE_LETTER |
- UPPERCASE_LETTER | OTHER_LETTER |
- TITLECASE_LETTER | LETTER_NUMBER => true
- case _ => ch == '_'
- }
- }
+ def isNameChar(c: Char): Boolean = (
+ isNameStart(c) ||
+ (c >= '0' && c <= '9') ||
+ c == '-' ||
+ c == '.' ||
+ c == 0xB7 ||
+ (c >= 0x300 && c <= 0x36F) ||
+ (c >= 0x203F && c <= 0x2040)
+ )
+ def isNameStart(c: Char): Boolean = (
+ if (c < 0x00C0) isAlpha(c) || c == ':' || c == '_'
+ else if (c < 0x0300) c != 0xD7 && c != 0xF7
+ else if (c < 0x2000) c >= 0x370 && c != 0x37E
+ else if (c < 0x3001) c == 0x200C || c == 0x200D || (0x2070 to 0x218F contains c) ||
+ (0x2C00 to 0x2FEF contains c)
+ else if (c < 0xD800) true
+ else if (c < 0x10000) (0xF900 to 0xFDCF contains c) || (0xFDF0 to 0xFFFD contains c)
+ else false // codepoint < 0xF0000
+ )
/** {{{
* Name ::= ( Letter | '_' ) (NameChar)*
diff --git a/src/library/scala/xml/pull/XMLEventReader.scala b/src/library/scala/xml/pull/XMLEventReader.scala
index 428c305055..26572c9946 100755
--- a/src/library/scala/xml/pull/XMLEventReader.scala
+++ b/src/library/scala/xml/pull/XMLEventReader.scala
@@ -91,12 +91,16 @@ extends scala.collection.AbstractIterator[XMLEvent]
override def run() {
curInput = input
- interruptibly { this.initialize.document() }
- setEvent(POISON)
+ try interruptibly { this.initialize.document() }
+ catch { case e:Exception => setEvent(ExceptionEvent(e)) }
+ finally setEvent(POISON)
}
}
}
+// An internal class used to propagate exception from helper threads to API end users.
+private case class ExceptionEvent(exception:Exception) extends XMLEvent
+
// An iterator designed for one or more producers to generate
// elements, and a single consumer to iterate. Iteration will continue
// until closeIterator() is called, after which point producers
@@ -141,6 +145,7 @@ trait ProducerConsumerIterator[T >: Null] extends Iterator[T] {
def next() = {
if (eos) throw new NoSuchElementException("ProducerConsumerIterator")
if (buffer == null) fillBuffer
+ if (buffer.isInstanceOf[ExceptionEvent]) throw buffer.asInstanceOf[ExceptionEvent].exception
drainBuffer
}
diff --git a/test/files/jvm/unittest_xml.scala b/test/files/jvm/unittest_xml.scala
index 106334e625..9ffd459fde 100644
--- a/test/files/jvm/unittest_xml.scala
+++ b/test/files/jvm/unittest_xml.scala
@@ -62,7 +62,9 @@ object Test {
object UtilityTest {
def run() {
assert(Utility.isNameStart('b'))
- assert(!Utility.isNameStart(':'))
+
+ // no longer: this was a convenience for the implementation, not to spec.
+ //assert(!Utility.isNameStart(':'))
val x = <foo>
<toomuchws/>
diff --git a/test/files/run/t4339.check b/test/files/run/t4339.check
new file mode 100644
index 0000000000..46eabbe42b
--- /dev/null
+++ b/test/files/run/t4339.check
@@ -0,0 +1,3 @@
+Saw failure: scala.xml.parsing.FatalError: expected closing tag of foo
+EvElemStart(null,foo, bar="baz/&gt;",)
+Saw failure: scala.xml.parsing.FatalError: expected closing tag of foo
diff --git a/test/files/run/t4339.scala b/test/files/run/t4339.scala
new file mode 100644
index 0000000000..bb8b847ec3
--- /dev/null
+++ b/test/files/run/t4339.scala
@@ -0,0 +1,35 @@
+
+import scala.util.{ Try, Success, Failure }
+import xml._
+import scala.io.Source.fromString
+import java.io.PrintStream
+
+object Test extends App {
+
+ def quietSource(text: String) = new io.Source {
+ override protected val iter = io.Source fromString text
+ override def report(pos: Int, msg: String, out: PrintStream) = ()
+ }
+ def reading(text: String)(f: pull.XMLEventReader => Unit): Unit = {
+ val r = new pull.XMLEventReader(quietSource(text))
+ try f(r)
+ finally r.stop()
+ }
+ def trying(body: => Unit): Unit =
+ Try (body) match {
+ case Success(_) => Console println "Expected failure"
+ case Failure(e) => Console println s"Saw failure: $e"
+ }
+
+ val problematic = """<foo bar="baz/>"""
+
+ trying (
+ parsing.ConstructingParser.fromSource(quietSource(problematic), false).document.docElem
+ )
+
+ trying (
+ reading(problematic) { r =>
+ while (r.hasNext) println(r.next())
+ }
+ )
+}