Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(931)

Unified Diff: frog/leg/string_validator.dart

Issue 9271037: Inserted string validation as separate task in compiler. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: frog/leg/string_validator.dart
diff --git a/frog/leg/string_validator.dart b/frog/leg/string_validator.dart
new file mode 100644
index 0000000000000000000000000000000000000000..7815d36b64ab5bf6b64d0479a13cb105d87cd412
--- /dev/null
+++ b/frog/leg/string_validator.dart
@@ -0,0 +1,196 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Checks a tree structure for whether the string literals are valid.
+
+class StringValidatorTask extends CompilerTask {
+ // Validator has no state except the compiler, so we just create one
+ // per task.
+ final StringValidator validator;
+
+ StringValidatorTask(Compiler compiler)
+ : super(compiler),
+ validator = new StringValidator(compiler);
+
+ void validate(Node tree) {
+ tree.accept(validator);
+ }
+}
+
+class StringValidator extends AbstractVisitor {
+ final Compiler compiler;
+
+ StringValidator(this.compiler);
+
+ void visitNode(Node node) {
+ node.visitChildren(this);
+ }
+
+ visitLiteralString(LiteralString node) {
+ // This is a string literal where the source contains both start and
+ // end quotes.
+ // String literals that are part of string interpolations are
+ // handled there to account for their varying degree of quotedness.
+ SourceString source = node.value;
+ StringQuoting quoting = quotingFromString(source);
+ int leftQuote = quoting.leftQuoteLength;
+ int rightQuote = quoting.rightQuoteLength;
+ SourceString content = source.copyWithoutQuotes(leftQuote, rightQuote);
+ validateString(node, node.token.charOffset + leftQuote, content, quoting);
+ }
+
+ // String interpolation validates the immediate strings and traverses
+ // the sub-expressions.
+ visitStringInterpolation(StringInterpolation node) {
+ LiteralString literalString = node.string;
+ SourceString source = literalString.value;
+ StringQuoting quoting = quotingFromString(source);
+ int offset = literalString.token.charOffset + quoting.leftQuoteLength;
+ SourceString string = source.copyWithoutQuotes(quoting.leftQuoteLength, 0);
+ for (StringInterpolationPart part in node.parts) {
+ validateString(literalString, offset, string, quoting);
+ part.expression.accept(this);
+ literalString = part.string;
+ offset = literalString.token.charOffset;
+ string = literalString.value;
+ }
+ string = string.copyWithoutQuotes(0, quoting.rightQuoteLength);
+ validateString(literalString, offset, string, quoting);
+ }
+
+ static StringQuoting quotingFromString(SourceString sourceString) {
+ Iterator<int> source = sourceString.iterator();
+ bool raw = false;
+ int quoteChar = source.next();
+ if (quoteChar == $AT) {
+ raw = true;
+ quoteChar = source.next();
+ }
+ assert(quoteChar === $SQ || quoteChar === $DQ);
+ // String has one quote. Check it if has three.
karlklose 2012/01/25 08:47:26 one -> at least one
Lasse Reichstein Nielsen 2012/01/26 10:14:20 Done.
+ // If it only have two, the string must be an empty string literal,
+ // and end after the second quote.
+ bool multiline = false;
+ if (source.hasNext() && source.next() == quoteChar && source.hasNext()) {
+ assert(source.next() == quoteChar);
+ multiline = true;
+ }
+ return StringQuoting.get(quoteChar, raw, multiline);
+ }
+
+ void stringParseError(String message, LiteralString node, int offset) {
+ assert(node.quotedString === null);
+ compiler.cancel("$message @ $offset", node);
+ }
+
+ /**
+ * Validates the escape sequences and special characters of a string literal.
+ * Returns the number of actual scalar values in the corresponding Dart
karlklose 2012/01/25 08:47:26 It returns whether the string is valid or not, doe
Lasse Reichstein Nielsen 2012/01/26 10:14:20 True. Old comment is old.
+ * string, or a negative value if the string is invalid.
+ */
+ bool validateString(LiteralString node,
+ int startOffset,
+ SourceString string,
+ StringQuoting quoting) {
+ // We only need to check for invalid x and u escapes, for line
+ // terminators in non-multiline strings, and for invalid Unicode
+ // scalar values (either directly or as u-escape values).
+ int length = 0;
+ int index = startOffset;
+ for(Iterator<int> iter = string.iterator(); iter.hasNext(); length++) {
+ index++;
+ int code = iter.next();
+ if (code === $BACKSLASH) {
+ if (quoting.raw) continue;
+ if (!iter.hasNext()) {
+ stringParseError("Incomplete escape sequence", node, index);
+ return false;
+ }
+ index++;
+ code = iter.next();
+ if (code === $x) {
+ for (int i = 0; i < 2; i++) {
+ if (!iter.hasNext()) {
+ stringParseError("Incomplete escape sequence", node, index);
+ return false;
+ }
+ index++;
+ code = iter.next();
+ if (!isHexDigit(code)) {
+ stringParseError("Invalid character in escape sequence",
+ node, index);
+ return false;
+ }
+ }
+ continue;
+ } else if (code === $u) {
+ int escapeStart = index - 1;
+ index++;
+ code = iter.next();
+ int value = 0;
+ if (code == $OPEN_CURLY_BRACKET) {
+ // expect 1-7 hex digits.
+ int count = 0;
+ index++;
+ code = iter.next();
+ do {
+ if (!isHexDigit(code)) {
+ stringParseError("Invalid character in escape sequence",
+ node, index);
+ return false;
+ }
+ count++;
+ value = value * 16 + hexDigitValue(code);
+ index++;
+ code = iter.next();
+ } while (code != $CLOSE_CURLY_BRACKET);
+ if (count > 7) {
+ stringParseError("Invalid character in escape sequence",
+ node, index - (count - 7));
+ return false;
+ }
+ } else {
+ // Expect four hex digits, including the one just tread.
+ for (int i = 0; i < 4; i++) {
+ if (i > 0) {
+ index++;
+ code = iter.next();
+ }
+ if (!isHexDigit(code)) {
+ stringParseError("Invalid character in escape sequence",
+ node, index);
+ return false;
+ }
+ value = value * 16 + hexDigitValue(code);
+ }
+ }
+ if (0xd800 <= value && ( value <= 0xdfff || value > 0x10ffff)) {
+ stringParseError(
+ "Invalid unicode scalar value U+${value.toRadixString(16)}",
+ node, index);
+ return false;
+ }
+ continue;
+ }
+ }
+ // This handles borth unescaped characters as well as those
+ // characters after a backslash that doesn't have a special
+ // meaning.
+ if (code >= 0xd800 && (code <= 0xdfff || code > 0x10ffff)) {
+ stringParseError(
+ "Invalid unicode scalar value U+${code.toRadixString(16)}",
+ node, index);
+ return false;
+ }
+ if (!quoting.multiline && (code === $LF || code === $CR)) {
+ stringParseError("Line terminator in single-line string",
+ node, index);
+ return false;
+ }
+ }
+ // String literal successfully validated.
+ node.quotedString = new QuotedString(string, quoting, length);
+ return true;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698