summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/CompilationUnits.scala14
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala19
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala24
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala13
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala2
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala18
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/xml/MarkupParserCommon.scala255
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/parser/xml/Utility.scala176
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugin.scala7
-rw-r--r--src/compiler/scala/tools/nsc/plugins/PluginDescription.scala63
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala26
-rw-r--r--src/library/scala/Predef.scala8
-rw-r--r--src/library/scala/runtime/ScalaRunTime.scala7
-rw-r--r--src/library/scala/sys/process/Process.scala17
-rw-r--r--src/library/scala/sys/process/package.scala5
-rwxr-xr-xsrc/library/scala/xml/Elem.scala23
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala6
-rw-r--r--src/reflect/scala/reflect/internal/Mirrors.scala3
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Trees.scala2
20 files changed, 593 insertions, 97 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala
index 15d365ab8c..b52e6fdf57 100644
--- a/src/compiler/scala/tools/nsc/CompilationUnits.scala
+++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala
@@ -34,6 +34,20 @@ trait CompilationUnits { self: Global =>
/** the content of the compilation unit in tree form */
var body: Tree = EmptyTree
+ /** The position of the first xml literal encountered while parsing this compilation unit.
+ * NoPosition if there were none. Write-once.
+ */
+ private[this] var _firstXmlPos: Position = NoPosition
+
+ /** Record that we encountered XML. Should only be called once. */
+ protected[nsc] def encounteredXml(pos: Position) = _firstXmlPos = pos
+
+ /** Does this unit contain XML? */
+ def hasXml = _firstXmlPos ne NoPosition
+
+ /** Position of first XML literal in this unit. */
+ def firstXmlPos = _firstXmlPos
+
def exists = source != NoSourceFile && source != null
/** Note: depends now contains toplevel classes.
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 692afbac66..c28a6ba337 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -19,11 +19,20 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
import global._
import definitions._
- /** Builds a fully attributed wildcard import node.
+ /** Builds a fully attributed, synthetic wildcard import node.
*/
- def mkWildcardImport(pkg: Symbol): Import = {
- assert(pkg ne null, this)
- val qual = gen.mkAttributedStableRef(pkg)
+ def mkWildcardImport(pkg: Symbol): Import =
+ mkImportFromSelector(pkg, ImportSelector.wildList)
+
+ /** Builds a fully attributed, synthetic import node.
+ * import `qualSym`.{`name` => `toName`}
+ */
+ def mkImport(qualSym: Symbol, name: Name, toName: Name): Import =
+ mkImportFromSelector(qualSym, ImportSelector(name, 0, toName, 0) :: Nil)
+
+ private def mkImportFromSelector(qualSym: Symbol, selector: List[ImportSelector]): Import = {
+ assert(qualSym ne null, this)
+ val qual = gen.mkAttributedStableRef(qualSym)
val importSym = (
NoSymbol
newImport NoPosition
@@ -31,7 +40,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
setInfo analyzer.ImportType(qual)
)
val importTree = (
- Import(qual, ImportSelector.wildList)
+ Import(qual, selector)
setSymbol importSym
setType NoType
)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
index 832a9bf63e..d3f495f280 100755
--- a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
@@ -10,8 +10,7 @@ import scala.collection.mutable
import mutable.{ Buffer, ArrayBuffer, ListBuffer }
import scala.util.control.ControlThrowable
import scala.tools.nsc.util.CharArrayReader
-import scala.xml.TextBuffer
-import scala.xml.parsing.MarkupParserCommon
+import scala.tools.nsc.ast.parser.xml.{MarkupParserCommon, Utility}
import scala.reflect.internal.Chars.{ SU, LF }
// XXX/Note: many/most of the functions in here are almost direct cut and pastes
@@ -42,7 +41,7 @@ trait MarkupParsers {
import global._
class MarkupParser(parser: SourceFileParser, final val preserveWS: Boolean) extends MarkupParserCommon {
-
+ import Utility.{ isNameStart, isSpace }
import Tokens.{ LBRACE, RBRACE }
type PositionType = Position
@@ -172,12 +171,21 @@ trait MarkupParsers {
xTakeUntil(handle.comment, () => r2p(start, start, curOffset), "-->")
}
- def appendText(pos: Position, ts: Buffer[Tree], txt: String) {
- val toAppend =
- if (preserveWS) Seq(txt)
- else TextBuffer.fromString(txt).toText map (_.text)
+ def appendText(pos: Position, ts: Buffer[Tree], txt: String): Unit = {
+ def append(t: String) = ts append handle.text(pos, t)
+
+ if (preserveWS) append(txt)
+ else {
+ val sb = new StringBuilder()
- toAppend foreach (t => ts append handle.text(pos, t))
+ txt foreach { c =>
+ if (!isSpace(c)) sb append c
+ else if (sb.isEmpty || !isSpace(sb.last)) sb append ' '
+ }
+
+ val trimmed = sb.toString.trim
+ if (!trimmed.isEmpty) append(trimmed)
+ }
}
/** adds entity/character to ts as side-effect
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index fc532f5d44..ef5872986c 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -166,13 +166,20 @@ self =>
def syntaxError(offset: Int, msg: String): Unit = throw new MalformedInput(offset, msg)
def incompleteInputError(msg: String): Unit = throw new MalformedInput(source.content.length - 1, msg)
- /** the markup parser */
- lazy val xmlp = new MarkupParser(this, preserveWS = true)
-
object symbXMLBuilder extends SymbolicXMLBuilder(this, preserveWS = true) { // DEBUG choices
val global: self.global.type = self.global
}
+ /** the markup parser
+ * The first time this lazy val is accessed, we assume we were trying to parse an xml literal.
+ * The current position is recorded for later error reporting if it turns out
+ * that we don't have the xml library on the compilation classpath.
+ */
+ private[this] lazy val xmlp = {
+ currentUnit.encounteredXml(o2p(in.offset))
+ new MarkupParser(this, preserveWS = true)
+ }
+
def xmlLiteral() : Tree = xmlp.xLiteral
def xmlLiteralPattern() : Tree = xmlp.xLiteralPattern
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 80b6ad3cc7..2dca39f7a3 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -12,7 +12,7 @@ import Tokens._
import scala.annotation.{ switch, tailrec }
import scala.collection.{ mutable, immutable }
import mutable.{ ListBuffer, ArrayBuffer }
-import scala.xml.Utility.{ isNameStart }
+import scala.tools.nsc.ast.parser.xml.Utility.isNameStart
import scala.language.postfixOps
/** See Parsers.scala / ParsersCommon for some explanation of ScannersCommon.
diff --git a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala
index f326212d5b..1abc0c860c 100755
--- a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala
@@ -7,8 +7,6 @@ package scala.tools.nsc
package ast.parser
import scala.collection.{ mutable, immutable }
-import scala.xml.{ EntityRef, Text }
-import scala.xml.XML.{ xmlns }
import symtab.Flags.MUTABLE
import scala.reflect.internal.util.StringOps.splitWhere
@@ -143,14 +141,12 @@ abstract class SymbolicXMLBuilder(p: Parsers#Parser, preserveWS: Boolean) {
(buf map convertToTextPat).toList
def parseAttribute(pos: Position, s: String): Tree = {
- val ts = scala.xml.Utility.parseAttributeValue(s) map {
- case Text(s) => text(pos, s)
- case EntityRef(s) => entityRef(pos, s)
- }
- ts.length match {
- case 0 => gen.mkNil
- case 1 => ts.head
- case _ => makeXMLseq(pos, ts.toList)
+ import xml.Utility.parseAttributeValue
+
+ parseAttributeValue(s, text(pos, _), entityRef(pos, _)) match {
+ case Nil => gen.mkNil
+ case t :: Nil => t
+ case ts => makeXMLseq(pos, ts.toList)
}
}
@@ -198,7 +194,7 @@ abstract class SymbolicXMLBuilder(p: Parsers#Parser, preserveWS: Boolean) {
/* Extract all the namespaces from the attribute map. */
val namespaces: List[Tree] =
- for (z <- attrMap.keys.toList ; if z startsWith xmlns) yield {
+ for (z <- attrMap.keys.toList ; if z startsWith "xmlns") yield {
val ns = splitPrefix(z) match {
case (Some(_), rest) => rest
case _ => null
diff --git a/src/compiler/scala/tools/nsc/ast/parser/xml/MarkupParserCommon.scala b/src/compiler/scala/tools/nsc/ast/parser/xml/MarkupParserCommon.scala
new file mode 100644
index 0000000000..f6cfb64ed8
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/ast/parser/xml/MarkupParserCommon.scala
@@ -0,0 +1,255 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.tools.nsc.ast.parser.xml
+
+/** This is not a public trait - it contains common code shared
+ * between the library level XML parser and the compiler's.
+ * All members should be accessed through those.
+ */
+private[scala] trait MarkupParserCommon {
+ import Utility._
+ import scala.reflect.internal.Chars.SU
+
+ protected def unreachable = scala.sys.error("Cannot be reached.")
+
+ // type HandleType // MarkupHandler, SymbolicXMLBuilder
+ type InputType // Source, CharArrayReader
+ type PositionType // Int, Position
+ type ElementType // NodeSeq, Tree
+ type NamespaceType // NamespaceBinding, Any
+ type AttributesType // (MetaData, NamespaceBinding), mutable.Map[String, Tree]
+
+ def mkAttributes(name: String, pscope: NamespaceType): AttributesType
+ def mkProcInstr(position: PositionType, name: String, text: String): ElementType
+
+ /** parse a start or empty tag.
+ * [40] STag ::= '<' Name { S Attribute } [S]
+ * [44] EmptyElemTag ::= '<' Name { S Attribute } [S]
+ */
+ protected def xTag(pscope: NamespaceType): (String, AttributesType) = {
+ val name = xName
+ xSpaceOpt()
+
+ (name, mkAttributes(name, pscope))
+ }
+
+ /** '<?' ProcInstr ::= Name [S ({Char} - ({Char}'>?' {Char})]'?>'
+ *
+ * see [15]
+ */
+ def xProcInstr: ElementType = {
+ val n = xName
+ xSpaceOpt()
+ xTakeUntil(mkProcInstr(_, n, _), () => tmppos, "?>")
+ }
+
+ /** attribute value, terminated by either `'` or `"`. value may not contain `<`.
+ @param endCh either `'` or `"`
+ */
+ def xAttributeValue(endCh: Char): String = {
+ val buf = new StringBuilder
+ while (ch != endCh) {
+ // well-formedness constraint
+ if (ch == '<') return errorAndResult("'<' not allowed in attrib value", "")
+ else if (ch == SU) truncatedError("")
+ else buf append ch_returning_nextch
+ }
+ ch_returning_nextch
+ // @todo: normalize attribute value
+ buf.toString
+ }
+
+ def xAttributeValue(): String = {
+ val str = xAttributeValue(ch_returning_nextch)
+ // well-formedness constraint
+ normalizeAttributeValue(str)
+ }
+
+ private def takeUntilChar(it: Iterator[Char], end: Char): String = {
+ val buf = new StringBuilder
+ while (it.hasNext) it.next() match {
+ case `end` => return buf.toString
+ case ch => buf append ch
+ }
+ scala.sys.error("Expected '%s'".format(end))
+ }
+
+ /** [42] '<' xmlEndTag ::= '<' '/' Name S? '>'
+ */
+ def xEndTag(startName: String) {
+ xToken('/')
+ if (xName != startName)
+ errorNoEnd(startName)
+
+ xSpaceOpt()
+ xToken('>')
+ }
+
+ /** actually, Name ::= (Letter | '_' | ':') (NameChar)* but starting with ':' cannot happen
+ * Name ::= (Letter | '_') (NameChar)*
+ *
+ * see [5] of XML 1.0 specification
+ *
+ * pre-condition: ch != ':' // assured by definition of XMLSTART token
+ * post-condition: name does neither start, nor end in ':'
+ */
+ def xName: String = {
+ if (ch == SU)
+ truncatedError("")
+ else if (!isNameStart(ch))
+ return errorAndResult("name expected, but char '%s' cannot start a name" format ch, "")
+
+ val buf = new StringBuilder
+
+ do buf append ch_returning_nextch
+ while (isNameChar(ch))
+
+ if (buf.last == ':') {
+ reportSyntaxError( "name cannot end in ':'" )
+ buf.toString dropRight 1
+ }
+ else buf.toString
+ }
+
+ private def attr_unescape(s: String) = s match {
+ case "lt" => "<"
+ case "gt" => ">"
+ case "amp" => "&"
+ case "apos" => "'"
+ case "quot" => "\""
+ case "quote" => "\""
+ case _ => "&" + s + ";"
+ }
+
+ /** Replaces only character references right now.
+ * see spec 3.3.3
+ */
+ private def normalizeAttributeValue(attval: String): String = {
+ val buf = new StringBuilder
+ val it = attval.iterator.buffered
+
+ while (it.hasNext) buf append (it.next() match {
+ case ' ' | '\t' | '\n' | '\r' => " "
+ case '&' if it.head == '#' => it.next() ; xCharRef(it)
+ case '&' => attr_unescape(takeUntilChar(it, ';'))
+ case c => c
+ })
+
+ buf.toString
+ }
+
+ /** CharRef ::= "&#" '0'..'9' {'0'..'9'} ";"
+ * | "&#x" '0'..'9'|'A'..'F'|'a'..'f' { hexdigit } ";"
+ *
+ * see [66]
+ */
+ def xCharRef(ch: () => Char, nextch: () => Unit): String =
+ Utility.parseCharRef(ch, nextch, reportSyntaxError _, truncatedError _)
+
+ def xCharRef(it: Iterator[Char]): String = {
+ var c = it.next()
+ Utility.parseCharRef(() => c, () => { c = it.next() }, reportSyntaxError _, truncatedError _)
+ }
+
+ def xCharRef: String = xCharRef(() => ch, () => nextch())
+
+ /** Create a lookahead reader which does not influence the input */
+ def lookahead(): BufferedIterator[Char]
+
+ /** The library and compiler parsers had the interesting distinction of
+ * different behavior for nextch (a function for which there are a total
+ * of two plausible behaviors, so we know the design space was fully
+ * explored.) One of them returned the value of nextch before the increment
+ * and one of them the new value. So to unify code we have to at least
+ * temporarily abstract over the nextchs.
+ */
+ def ch: Char
+ def nextch(): Unit
+ protected def ch_returning_nextch: Char
+ def eof: Boolean
+
+ // def handle: HandleType
+ var tmppos: PositionType
+
+ def xHandleError(that: Char, msg: String): Unit
+ def reportSyntaxError(str: String): Unit
+ def reportSyntaxError(pos: Int, str: String): Unit
+
+ def truncatedError(msg: String): Nothing
+ def errorNoEnd(tag: String): Nothing
+
+ protected def errorAndResult[T](msg: String, x: T): T = {
+ reportSyntaxError(msg)
+ x
+ }
+
+ def xToken(that: Char) {
+ if (ch == that) nextch()
+ else xHandleError(that, "'%s' expected instead of '%s'".format(that, ch))
+ }
+ def xToken(that: Seq[Char]) { that foreach xToken }
+
+ /** scan [S] '=' [S]*/
+ def xEQ() = { xSpaceOpt(); xToken('='); xSpaceOpt() }
+
+ /** skip optional space S? */
+ def xSpaceOpt() = while (isSpace(ch) && !eof) nextch()
+
+ /** scan [3] S ::= (#x20 | #x9 | #xD | #xA)+ */
+ def xSpace() =
+ if (isSpace(ch)) { nextch(); xSpaceOpt() }
+ else xHandleError(ch, "whitespace expected")
+
+ /** Apply a function and return the passed value */
+ def returning[T](x: T)(f: T => Unit): T = { f(x); x }
+
+ /** Execute body with a variable saved and restored after execution */
+ def saving[A, B](getter: A, setter: A => Unit)(body: => B): B = {
+ val saved = getter
+ try body
+ finally setter(saved)
+ }
+
+ /** Take characters from input stream until given String "until"
+ * is seen. Once seen, the accumulated characters are passed
+ * along with the current Position to the supplied handler function.
+ */
+ protected def xTakeUntil[T](
+ handler: (PositionType, String) => T,
+ positioner: () => PositionType,
+ until: String): T =
+ {
+ val sb = new StringBuilder
+ val head = until.head
+ val rest = until.tail
+
+ while (true) {
+ if (ch == head && peek(rest))
+ return handler(positioner(), sb.toString)
+ else if (ch == SU)
+ truncatedError("") // throws TruncatedXMLControl in compiler
+
+ sb append ch
+ nextch()
+ }
+ unreachable
+ }
+
+ /** Create a non-destructive lookahead reader and see if the head
+ * of the input would match the given String. If yes, return true
+ * and drop the entire String from input; if no, return false
+ * and leave input unchanged.
+ */
+ private def peek(lookingFor: String): Boolean =
+ (lookahead() take lookingFor.length sameElements lookingFor.iterator) && {
+ // drop the chars from the real reader (all lookahead + orig)
+ (0 to lookingFor.length) foreach (_ => nextch())
+ true
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/xml/Utility.scala b/src/compiler/scala/tools/nsc/ast/parser/xml/Utility.scala
new file mode 100755
index 0000000000..39e4831af2
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/ast/parser/xml/Utility.scala
@@ -0,0 +1,176 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.tools.nsc.ast.parser.xml
+
+import scala.collection.mutable
+
+
+/**
+ * The `Utility` object provides utility functions for processing instances
+ * of bound and not bound XML classes, as well as escaping text nodes.
+ *
+ * @author Burak Emir
+ */
+object Utility {
+ import scala.reflect.internal.Chars.SU
+
+ private val unescMap = Map(
+ "lt" -> '<',
+ "gt" -> '>',
+ "amp" -> '&',
+ "quot" -> '"',
+ "apos" -> '\''
+ )
+
+ /**
+ * Appends unescaped string to `s`, `amp` becomes `&amp;`,
+ * `lt` becomes `&lt;` etc..
+ *
+ * @return `'''null'''` if `ref` was not a predefined entity.
+ */
+ private final def unescape(ref: String, s: StringBuilder): StringBuilder =
+ ((unescMap get ref) map (s append _)).orNull
+
+ def parseAttributeValue[T](value: String, text: String => T, entityRef: String => T): List[T] = {
+ val sb = new StringBuilder
+ var rfb: StringBuilder = null
+ val nb = new mutable.ListBuffer[T]()
+
+ val it = value.iterator
+ while (it.hasNext) {
+ var c = it.next()
+ // entity! flush buffer into text node
+ if (c == '&') {
+ c = it.next()
+ if (c == '#') {
+ c = it.next()
+ val theChar = parseCharRef ({ ()=> c },{ () => c = it.next() },{s => throw new RuntimeException(s)}, {s => throw new RuntimeException(s)})
+ sb.append(theChar)
+ }
+ else {
+ if (rfb eq null) rfb = new StringBuilder()
+ rfb append c
+ c = it.next()
+ while (c != ';') {
+ rfb.append(c)
+ c = it.next()
+ }
+ val ref = rfb.toString()
+ rfb.clear()
+ unescape(ref,sb) match {
+ case null =>
+ if (!sb.isEmpty) { // flush buffer
+ nb += text(sb.toString())
+ sb.clear()
+ }
+ nb += entityRef(ref) // add entityref
+ case _ =>
+ }
+ }
+ }
+ else sb append c
+ }
+
+ if(!sb.isEmpty) // flush buffer
+ nb += text(sb.toString())
+
+ nb.toList
+ }
+
+ /**
+ * {{{
+ * CharRef ::= "&amp;#" '0'..'9' {'0'..'9'} ";"
+ * | "&amp;#x" '0'..'9'|'A'..'F'|'a'..'f' { hexdigit } ";"
+ * }}}
+ * See [66]
+ */
+ def parseCharRef(ch: () => Char, nextch: () => Unit, reportSyntaxError: String => Unit, reportTruncatedError: String => Unit): String = {
+ val hex = (ch() == 'x') && { nextch(); true }
+ val base = if (hex) 16 else 10
+ var i = 0
+ while (ch() != ';') {
+ ch() match {
+ case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' =>
+ i = i * base + ch().asDigit
+ case 'a' | 'b' | 'c' | 'd' | 'e' | 'f'
+ | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' =>
+ if (! hex)
+ reportSyntaxError("hex char not allowed in decimal char ref\n" +
+ "Did you mean to write &#x ?")
+ else
+ i = i * base + ch().asDigit
+ case SU =>
+ reportTruncatedError("")
+ case _ =>
+ reportSyntaxError("character '" + ch() + "' not allowed in char ref\n")
+ }
+ nextch()
+ }
+ new String(Array(i), 0, 1)
+ }
+
+ /** {{{
+ * (#x20 | #x9 | #xD | #xA)
+ * }}} */
+ final def isSpace(ch: Char): Boolean = ch match {
+ case '\u0009' | '\u000A' | '\u000D' | '\u0020' => true
+ case _ => false
+ }
+ /** {{{
+ * (#x20 | #x9 | #xD | #xA)+
+ * }}} */
+ final def isSpace(cs: Seq[Char]): Boolean = cs.nonEmpty && (cs forall isSpace)
+
+ /** {{{
+ * 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 == '_'
+ }
+ }
+
+ /** {{{
+ * Name ::= ( Letter | '_' ) (NameChar)*
+ * }}}
+ * See [5] of XML 1.0 specification.
+ */
+ def isName(s: String) =
+ s.nonEmpty && isNameStart(s.head) && (s.tail forall isNameChar)
+
+}
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
index a584a4ed5d..4fd6ba7d9d 100644
--- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala
+++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
@@ -14,7 +14,6 @@ import java.util.zip.ZipException
import scala.collection.mutable.ListBuffer
import scala.util.{ Try, Success, Failure }
-import scala.xml.XML
/** Information about a plugin loaded from a jar file.
*
@@ -81,14 +80,14 @@ object Plugin {
private def loadDescriptionFromJar(jarp: Path): Try[PluginDescription] = {
// XXX Return to this once we have more ARM support
def read(is: Option[InputStream]) = is match {
- case None => throw new RuntimeException(s"Missing $PluginXML in $jarp")
- case _ => PluginDescription fromXML (XML load is.get)
+ case None => throw new RuntimeException(s"Missing $PluginXML in $jarp")
+ case Some(is) => PluginDescription.fromXML(is)
}
Try(new Jar(jarp.jfile).withEntryStream(PluginXML)(read))
}
private def loadDescriptionFromFile(f: Path): Try[PluginDescription] =
- Try(XML loadFile f.jfile) map (PluginDescription fromXML _)
+ Try(PluginDescription.fromXML(new java.io.FileInputStream(f.jfile)))
type AnyClass = Class[_]
diff --git a/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala b/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala
index 27693d1a45..bf78c93fcc 100644
--- a/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala
+++ b/src/compiler/scala/tools/nsc/plugins/PluginDescription.scala
@@ -6,57 +6,50 @@
package scala.tools.nsc
package plugins
-import scala.xml.Node
+import scala.reflect.internal.util.StringContextStripMarginOps
/** A description of a compiler plugin, suitable for serialization
* to XML for inclusion in the plugin's .jar file.
*
* @author Lex Spoon
* @version 1.0, 2007-5-21
- * @param name A short name of the plugin, used to identify it in
- * various contexts. The phase defined by the plugin
- * should have the same name.
- * @param classname The name of the main Plugin class.
+ * @author Adriaan Moors
+ * @version 2.0, 2013
+ * @param name A short name of the plugin, used to identify it in
+ * various contexts. The phase defined by the plugin
+ * should have the same name.
+ * @param classname The name of the main Plugin class.
*/
case class PluginDescription(name: String, classname: String) {
-
- /** An XML representation of this description. It can be
- * read back using `PluginDescription.fromXML`.
+ /** An XML representation of this description.
* It should be stored inside the jar archive file.
*/
- def toXML: Node = {
- <plugin>
- <name>{name}</name>
- <classname>{classname}</classname>
- </plugin>
- }
+ def toXML: String =
+ sm"""<plugin>
+ | <name>${name}</name>
+ | <classname>${classname}</classname>
+ |</plugin>"""
}
/** Utilities for the PluginDescription class.
*
- * @author Lex Spoon
- * @version 1.0, 2007-5-21
+ * @author Lex Spoon
+ * @version 1.0, 2007-5-21
+ * @author Adriaan Moors
+ * @version 2.0, 2013
*/
object PluginDescription {
+ private def text(ns: org.w3c.dom.NodeList): String =
+ if (ns.getLength == 1) ns.item(0).getTextContent.trim
+ else throw new RuntimeException("Bad plugin descriptor.")
+
+ def fromXML(xml: java.io.InputStream): PluginDescription = {
+ import javax.xml.parsers.DocumentBuilderFactory
+ val root = DocumentBuilderFactory.newInstance.newDocumentBuilder.parse(xml).getDocumentElement
+ root.normalize()
+ if (root.getNodeName != "plugin")
+ throw new RuntimeException("Plugin descriptor root element must be <plugin>.")
- def fromXML(xml: Node): PluginDescription = {
- // extract one field
- def getField(field: String): Option[String] = {
- val text = (xml \\ field).text.trim
- if (text == "") None else Some(text)
- }
- def extracted = {
- val name = "name"
- val claas = "classname"
- val vs = Map(name -> getField(name), claas -> getField(claas))
- if (vs.values exists (_.isEmpty)) fail()
- else PluginDescription(name = vs(name).get, classname = vs(claas).get)
- }
- def fail() = throw new RuntimeException("Bad plugin descriptor.")
- // check the top-level tag
- xml match {
- case <plugin>{_*}</plugin> => extracted
- case _ => fail()
- }
+ PluginDescription(text(root.getElementsByTagName("name")), text(root.getElementsByTagName("classname")))
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 61967d4cee..1f4ff7cc2d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -16,7 +16,7 @@ import scala.reflect.internal.util.shortClassOfInstance
*/
trait Contexts { self: Analyzer =>
import global._
- import definitions.{ JavaLangPackage, ScalaPackage, PredefModule }
+ import definitions.{ JavaLangPackage, ScalaPackage, PredefModule, ScalaXmlTopScope, ScalaXmlPackage }
import ContextMode._
object NoContext
@@ -93,9 +93,31 @@ trait Contexts { self: Analyzer =>
else RootImports.completeList
}
+
def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, erasedTypes: Boolean = false): Context = {
val rootImportsContext = (startContext /: rootImports(unit))((c, sym) => c.make(gen.mkWildcardImport(sym)))
- val c = rootImportsContext.make(tree, unit = unit)
+
+ // there must be a scala.xml package when xml literals were parsed in this unit
+ if (unit.hasXml && ScalaXmlPackage == NoSymbol)
+ unit.error(unit.firstXmlPos, "XML literals may only be used if the package scala.xml is present in the compilation classpath.")
+
+ // TODO: remove the def below and drop `|| predefDefinesDollarScope` in the condition for `contextWithXML`
+ // as soon as 2.11.0-M4 is released and used as STARR (and $scope is no longer defined in Predef)
+ // Until then, to allow compiling quick with pre-2.11.0-M4 STARR,
+ // which relied on Predef defining `val $scope`, we've left it in place.
+ // Since the new scheme also imports $scope (as an alias for scala.xml.TopScope),
+ // we must check whether it is still there and not import the alias to avoid ambiguity.
+ // (All of this is only necessary to compile the full quick stage with STARR.
+ // if using locker, Predef.$scope is no longer needed.)
+ def predefDefinesDollarScope = definitions.getMemberIfDefined(PredefModule, nme.dollarScope) != NoSymbol
+
+ // hack for the old xml library (detected by looking for scala.xml.TopScope, which needs to be in scope as $scope)
+ // import scala.xml.{TopScope => $scope}
+ val contextWithXML =
+ if (!unit.hasXml || ScalaXmlTopScope == NoSymbol || predefDefinesDollarScope) rootImportsContext
+ else rootImportsContext.make(gen.mkImport(ScalaXmlPackage, nme.TopScope, nme.dollarScope))
+
+ val c = contextWithXML.make(tree, unit = unit)
if (erasedTypes) c.setThrowErrors() else c.setReportErrors()
c(EnrichmentEnabled | ImplicitsEnabled) = !erasedTypes
c
diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala
index 5ba38600b6..a188602543 100644
--- a/src/library/scala/Predef.scala
+++ b/src/library/scala/Predef.scala
@@ -134,7 +134,13 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef {
@inline def implicitly[T](implicit e: T) = e // for summoning implicit values from the nether world -- TODO: when dependent method types are on by default, give this result type `e.type`, so that inliner has better chance of knowing which method to inline in calls like `implicitly[MatchingStrategy[Option]].zero`
@inline def locally[T](x: T): T = x // to communicate intent and avoid unmoored statements
- // Apparently needed for the xml library
+ // TODO: remove `val $scope = ...` as soon as 2.11.0-M4 is released and used as STARR
+ // As it has a '$' in its name, we don't have to deprecate first.
+ // The compiler now aliases `scala.xml.TopScope` to `$scope` (unless Predef.$scope is still there).
+ // This definition left in place for older compilers and to compile quick with pre-2.11.0-M4 STARR.
+ // In principle we don't need it to compile library/reflect/compiler (there's no xml left there),
+ // so a new locker can be built without this definition, and locker can build quick
+ // (partest, scaladoc still require xml).
val $scope = scala.xml.TopScope
// errors and asserts -------------------------------------------------
diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala
index 3a85207235..c049de3a28 100644
--- a/src/library/scala/runtime/ScalaRunTime.scala
+++ b/src/library/scala/runtime/ScalaRunTime.scala
@@ -15,7 +15,6 @@ import scala.collection.immutable.{ StringLike, NumericRange, List, Stream, Nil,
import scala.collection.generic.{ Sorted }
import scala.reflect.{ ClassTag, classTag }
import scala.util.control.ControlThrowable
-import scala.xml.{ Node, MetaData }
import java.lang.{ Class => jClass }
import java.lang.Double.doubleToLongBits
@@ -268,11 +267,10 @@ object ScalaRunTime {
}
def isScalaClass(x: AnyRef) = packageOf(x) startsWith "scala."
def isScalaCompilerClass(x: AnyRef) = packageOf(x) startsWith "scala.tools.nsc."
+ def isXmlClass(x: AnyRef) = packageOf(x) startsWith "scala.xml."
// When doing our own iteration is dangerous
def useOwnToString(x: Any) = x match {
- // Node extends NodeSeq extends Seq[Node] and MetaData extends Iterable[MetaData]
- case _: Node | _: MetaData => true
// Range/NumericRange have a custom toString to avoid walking a gazillion elements
case _: Range | _: NumericRange[_] => true
// Sorted collections to the wrong thing (for us) on iteration - ticket #3493
@@ -281,10 +279,11 @@ object ScalaRunTime {
case _: StringLike[_] => true
// Don't want to evaluate any elements in a view
case _: TraversableView[_, _] => true
+ // Node extends NodeSeq extends Seq[Node] and MetaData extends Iterable[MetaData] -> catch those and more by isXmlClass(x)
// Don't want to a) traverse infinity or b) be overly helpful with peoples' custom
// collections which may have useful toString methods - ticket #3710
// or c) print AbstractFiles which are somehow also Iterable[AbstractFile]s.
- case x: Traversable[_] => !x.hasDefiniteSize || !isScalaClass(x) || isScalaCompilerClass(x)
+ case x: Traversable[_] => !x.hasDefiniteSize || !isScalaClass(x) || isScalaCompilerClass(x) || isXmlClass(x)
// Otherwise, nothing could possibly go wrong
case _ => false
}
diff --git a/src/library/scala/sys/process/Process.scala b/src/library/scala/sys/process/Process.scala
index 402183a1f0..dcd06c89e9 100644
--- a/src/library/scala/sys/process/Process.scala
+++ b/src/library/scala/sys/process/Process.scala
@@ -127,15 +127,6 @@ trait ProcessCreation {
*/
def apply(url: URL): URLBuilder = new URLImpl(url)
- /** Creates a [[scala.sys.process.ProcessBuilder]] from a Scala XML Element.
- * This can be used as a way to template strings.
- *
- * @example {{{
- * apply(<x> {dxPath.absolutePath} --dex --output={classesDexPath.absolutePath} {classesMinJarPath.absolutePath}</x>)
- * }}}
- */
- def apply(command: scala.xml.Elem): ProcessBuilder = apply(command.text.trim)
-
/** Creates a [[scala.sys.process.ProcessBuilder]] from a `Boolean`. This can be
* to force an exit value.
*/
@@ -220,14 +211,6 @@ trait ProcessImplicits {
*/
implicit def urlToProcess(url: URL): URLBuilder = apply(url)
- /** Implicitly convert a [[scala.xml.Elem]] into a
- * [[scala.sys.process.ProcessBuilder]]. This is done by obtaining the text
- * elements of the element, trimming spaces, and then converting the result
- * from string to a process. Importantly, tags are completely ignored, so
- * they cannot be used to separate parameters.
- */
- implicit def xmlToProcess(command: scala.xml.Elem): ProcessBuilder = apply(command)
-
/** Implicitly convert a `String` into a [[scala.sys.process.ProcessBuilder]]. */
implicit def stringToProcess(command: String): ProcessBuilder = apply(command)
diff --git a/src/library/scala/sys/process/package.scala b/src/library/scala/sys/process/package.scala
index 902543665f..d68cd004f8 100644
--- a/src/library/scala/sys/process/package.scala
+++ b/src/library/scala/sys/process/package.scala
@@ -80,10 +80,7 @@ package scala.sys {
* spaces -- no escaping of spaces is possible -- or out of a
* [[scala.collection.Seq]], where the first element represents the command
* name, and the remaining elements are arguments to it. In this latter case,
- * arguments may contain spaces. One can also implicitly convert
- * [[scala.xml.Elem]] and `java.lang.ProcessBuilder` into a `ProcessBuilder`.
- * In the introductory example, the strings were converted into
- * `ProcessBuilder` implicitly.
+ * arguments may contain spaces.
*
* To further control what how the process will be run, such as specifying
* the directory in which it will be run, see the factories on
diff --git a/src/library/scala/xml/Elem.scala b/src/library/scala/xml/Elem.scala
index 4200b7046a..484cf98744 100755
--- a/src/library/scala/xml/Elem.scala
+++ b/src/library/scala/xml/Elem.scala
@@ -35,8 +35,31 @@ object Elem {
case _: SpecialNode | _: Group => None
case _ => Some((n.prefix, n.label, n.attributes, n.scope, n.child))
}
+
+ import scala.sys.process._
+ /** Implicitly convert a [[scala.xml.Elem]] into a
+ * [[scala.sys.process.ProcessBuilder]]. This is done by obtaining the text
+ * elements of the element, trimming spaces, and then converting the result
+ * from string to a process. Importantly, tags are completely ignored, so
+ * they cannot be used to separate parameters.
+ */
+ @deprecated("To create a scala.sys.process.Process from an xml.Elem, please use Process(elem.text.trim).", "2.11.0")
+ implicit def xmlToProcess(command: scala.xml.Elem): ProcessBuilder = Process(command.text.trim)
+
+ @deprecated("To create a scala.sys.process.Process from an xml.Elem, please use Process(elem.text.trim).", "2.11.0")
+ implicit def processXml(p: Process.type) = new {
+ /** Creates a [[scala.sys.process.ProcessBuilder]] from a Scala XML Element.
+ * This can be used as a way to template strings.
+ *
+ * @example {{{
+ * apply(<x> {dxPath.absolutePath} --dex --output={classesDexPath.absolutePath} {classesMinJarPath.absolutePath}</x>)
+ * }}}
+ */
+ def apply(command: Elem): ProcessBuilder = Process(command.text.trim)
+ }
}
+
/** The case class `Elem` extends the `Node` class,
* providing an immutable data object representing an XML element.
*
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 4f2b7e2642..3470b05495 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -16,7 +16,7 @@ import scala.reflect.api.{Universe => ApiUniverse}
trait Definitions extends api.StandardDefinitions {
self: SymbolTable =>
- import rootMirror.{getModule, getPackage, getClassByName, getRequiredClass, getRequiredModule, getClassIfDefined, getModuleIfDefined, getPackageObject, getPackageObjectIfDefined, requiredClass, requiredModule}
+ import rootMirror.{getModule, getPackage, getClassByName, getRequiredClass, getRequiredModule, getClassIfDefined, getModuleIfDefined, getPackageObject, getPackageIfDefined, getPackageObjectIfDefined, requiredClass, requiredModule}
object definitions extends DefinitionsClass
@@ -471,6 +471,10 @@ trait Definitions extends api.StandardDefinitions {
def methodCache_find = getMemberMethod(MethodCacheClass, nme.find_)
def methodCache_add = getMemberMethod(MethodCacheClass, nme.add_)
+ // XML
+ lazy val ScalaXmlTopScope = getModuleIfDefined("scala.xml.TopScope")
+ lazy val ScalaXmlPackage = getPackageIfDefined("scala.xml")
+
// scala.reflect
lazy val ReflectPackage = requiredModule[scala.reflect.`package`.type]
lazy val ReflectApiPackage = getPackageObjectIfDefined("scala.reflect.api") // defined in scala-reflect.jar, so we need to be careful
diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala
index bf38c3bf1e..6ed9de8e20 100644
--- a/src/reflect/scala/reflect/internal/Mirrors.scala
+++ b/src/reflect/scala/reflect/internal/Mirrors.scala
@@ -176,6 +176,9 @@ trait Mirrors extends api.Mirrors {
def getPackage(fullname: TermName): ModuleSymbol =
ensurePackageSymbol(fullname.toString, getModuleOrClass(fullname), allowModules = true)
+ def getPackageIfDefined(fullname: TermName): Symbol =
+ wrapMissing(getPackage(fullname))
+
@deprecated("Use getPackage", "2.11.0") def getRequiredPackage(fullname: String): ModuleSymbol =
getPackage(newTermNameCached(fullname))
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 81fffc833c..30aaaa1f3a 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -599,6 +599,7 @@ trait StdNames {
val currentMirror: NameType = "currentMirror"
val delayedInit: NameType = "delayedInit"
val delayedInitArg: NameType = "delayedInit$body"
+ val dollarScope: NameType = "$scope"
val drop: NameType = "drop"
val elem: NameType = "elem"
val emptyValDef: NameType = "emptyValDef"
@@ -684,6 +685,7 @@ trait StdNames {
val thisPrefix : NameType = "thisPrefix"
val toArray: NameType = "toArray"
val toObjectArray : NameType = "toObjectArray"
+ val TopScope: NameType = "TopScope"
val toString_ : NameType = "toString"
val toTypeConstructor: NameType = "toTypeConstructor"
val tpe : NameType = "tpe"
diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala
index 8781423a6d..ceb3b383d7 100644
--- a/src/reflect/scala/reflect/internal/Trees.scala
+++ b/src/reflect/scala/reflect/internal/Trees.scala
@@ -327,7 +327,7 @@ trait Trees extends api.Trees { self: SymbolTable =>
case class ImportSelector(name: Name, namePos: Int, rename: Name, renamePos: Int) extends ImportSelectorApi
object ImportSelector extends ImportSelectorExtractor {
val wild = ImportSelector(nme.WILDCARD, -1, null, -1)
- val wildList = List(wild)
+ val wildList = List(wild) // OPT This list is shared for performance.
}
case class Import(expr: Tree, selectors: List[ImportSelector])