| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Check the validity of string literals. | 5 // Check the validity of string literals. |
| 6 | 6 |
| 7 library stringvalidator; | 7 library stringvalidator; |
| 8 | 8 |
| 9 import "dart2jslib.dart"; | 9 import "dart2jslib.dart"; |
| 10 import "tree/tree.dart"; | 10 import "tree/tree.dart"; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 if (isFirst) leftQuote = quoting.leftQuoteLength; | 38 if (isFirst) leftQuote = quoting.leftQuoteLength; |
| 39 if (isLast) rightQuote = quoting.rightQuoteLength; | 39 if (isLast) rightQuote = quoting.rightQuoteLength; |
| 40 SourceString content = source.copyWithoutQuotes(leftQuote, rightQuote); | 40 SourceString content = source.copyWithoutQuotes(leftQuote, rightQuote); |
| 41 return validateString(token, | 41 return validateString(token, |
| 42 token.charOffset + leftQuote, | 42 token.charOffset + leftQuote, |
| 43 content, | 43 content, |
| 44 quoting); | 44 quoting); |
| 45 } | 45 } |
| 46 | 46 |
| 47 static StringQuoting quotingFromString(SourceString sourceString) { | 47 static StringQuoting quotingFromString(SourceString sourceString) { |
| 48 Iterator<int> source = sourceString.iterator(); | 48 Iterator<int> source = sourceString.iterator; |
| 49 bool raw = false; | 49 bool raw = false; |
| 50 int quoteLength = 1; | 50 int quoteLength = 1; |
| 51 int quoteChar = source.next(); | 51 source.moveNext(); |
| 52 if (identical(quoteChar, $r)) { | 52 int quoteChar = source.current; |
| 53 if (quoteChar == $r) { |
| 53 raw = true; | 54 raw = true; |
| 54 quoteChar = source.next(); | 55 source.moveNext(); |
| 56 quoteChar = source.current; |
| 55 } | 57 } |
| 56 assert(quoteChar == $SQ || quoteChar == $DQ); | 58 assert(quoteChar == $SQ || quoteChar == $DQ); |
| 57 // String has at least one quote. Check it if has three. | 59 // String has at least one quote. Check it if has three. |
| 58 // If it only have two, the string must be an empty string literal, | 60 // If it only have two, the string must be an empty string literal, |
| 59 // and end after the second quote. | 61 // and end after the second quote. |
| 60 bool multiline = false; | 62 bool multiline = false; |
| 61 if (source.hasNext && source.next() == quoteChar && source.hasNext) { | 63 if (source.moveNext() && source.current == quoteChar && source.moveNext()) { |
| 62 int code = source.next(); | 64 int code = source.current; |
| 63 assert(code == quoteChar); // If not, there is a bug in the parser. | 65 assert(code == quoteChar); // If not, there is a bug in the parser. |
| 64 quoteLength = 3; | 66 quoteLength = 3; |
| 65 // Check if a multiline string starts with a newline (CR, LF or CR+LF). | 67 // Check if a multiline string starts with a newline (CR, LF or CR+LF). |
| 66 if (source.hasNext) { | 68 if (source.moveNext()) { |
| 67 code = source.next(); | 69 code = source.current; |
| 68 if (code == $CR) { | 70 if (code == $CR) { |
| 69 quoteLength += 1; | 71 quoteLength += 1; |
| 70 if (source.hasNext && source.next() == $LF) { | 72 if (source.moveNext() && source.current == $LF) { |
| 71 quoteLength += 1; | 73 quoteLength += 1; |
| 72 } | 74 } |
| 73 } else if (code == $LF) { | 75 } else if (code == $LF) { |
| 74 quoteLength += 1; | 76 quoteLength += 1; |
| 75 } | 77 } |
| 76 } | 78 } |
| 77 } | 79 } |
| 78 return StringQuoting.getQuoting(quoteChar, raw, quoteLength); | 80 return StringQuoting.getQuoting(quoteChar, raw, quoteLength); |
| 79 } | 81 } |
| 80 | 82 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 92 StringQuoting quoting) { | 94 StringQuoting quoting) { |
| 93 // We need to check for invalid x and u escapes, for line | 95 // We need to check for invalid x and u escapes, for line |
| 94 // terminators in non-multiline strings, and for invalid Unicode | 96 // terminators in non-multiline strings, and for invalid Unicode |
| 95 // scalar values (either directly or as u-escape values). We also check | 97 // scalar values (either directly or as u-escape values). We also check |
| 96 // for unpaired UTF-16 surrogates. | 98 // for unpaired UTF-16 surrogates. |
| 97 int length = 0; | 99 int length = 0; |
| 98 int index = startOffset; | 100 int index = startOffset; |
| 99 bool containsEscape = false; | 101 bool containsEscape = false; |
| 100 bool previousWasLeadSurrogate = false; | 102 bool previousWasLeadSurrogate = false; |
| 101 bool invalidUtf16 = false; | 103 bool invalidUtf16 = false; |
| 102 for(Iterator<int> iter = string.iterator(); iter.hasNext; length++) { | 104 for(HasNextIterator<int> iter = new HasNextIterator(string.iterator); |
| 105 iter.hasNext; |
| 106 length++) { |
| 103 index++; | 107 index++; |
| 104 int code = iter.next(); | 108 int code = iter.next(); |
| 105 if (code == $BACKSLASH) { | 109 if (code == $BACKSLASH) { |
| 106 if (quoting.raw) continue; | 110 if (quoting.raw) continue; |
| 107 containsEscape = true; | 111 containsEscape = true; |
| 108 if (!iter.hasNext) { | 112 if (!iter.hasNext) { |
| 109 stringParseError("Incomplete escape sequence",token, index); | 113 stringParseError("Incomplete escape sequence",token, index); |
| 110 return null; | 114 return null; |
| 111 } | 115 } |
| 112 index++; | 116 index++; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 123 stringParseError("Invalid character in escape sequence", | 127 stringParseError("Invalid character in escape sequence", |
| 124 token, index); | 128 token, index); |
| 125 return null; | 129 return null; |
| 126 } | 130 } |
| 127 } | 131 } |
| 128 // A two-byte hex escape can't generate an invalid value. | 132 // A two-byte hex escape can't generate an invalid value. |
| 129 continue; | 133 continue; |
| 130 } else if (code == $u) { | 134 } else if (code == $u) { |
| 131 int escapeStart = index - 1; | 135 int escapeStart = index - 1; |
| 132 index++; | 136 index++; |
| 133 code = iter.next(); | 137 code = iter.hasNext ? iter.next() : 0; |
| 134 int value = 0; | 138 int value = 0; |
| 135 if (code == $OPEN_CURLY_BRACKET) { | 139 if (code == $OPEN_CURLY_BRACKET) { |
| 136 // expect 1-6 hex digits. | 140 // expect 1-6 hex digits. |
| 137 int count = 0; | 141 int count = 0; |
| 138 index++; | 142 while (iter.hasNext) { |
| 139 code = iter.next(); | 143 code = iter.next(); |
| 140 do { | 144 index++; |
| 145 if (code == $CLOSE_CURLY_BRACKET) { |
| 146 break; |
| 147 } |
| 141 if (!isHexDigit(code)) { | 148 if (!isHexDigit(code)) { |
| 142 stringParseError("Invalid character in escape sequence", | 149 stringParseError("Invalid character in escape sequence", |
| 143 token, index); | 150 token, index); |
| 144 return null; | 151 return null; |
| 145 } | 152 } |
| 146 count++; | 153 count++; |
| 147 value = value * 16 + hexDigitValue(code); | 154 value = value * 16 + hexDigitValue(code); |
| 148 index++; | 155 } |
| 149 code = iter.next(); | 156 if (code != $CLOSE_CURLY_BRACKET || count == 0 || count > 6) { |
| 150 } while (code != $CLOSE_CURLY_BRACKET); | 157 int errorPosition = index - count; |
| 151 if (count > 6) { | 158 if (count > 6) errorPosition += 6; |
| 152 stringParseError("Invalid character in escape sequence", | 159 stringParseError("Invalid character in escape sequence", |
| 153 token, index - (count - 6)); | 160 token, errorPosition); |
| 154 return null; | 161 return null; |
| 155 } | 162 } |
| 156 } else { | 163 } else { |
| 157 // Expect four hex digits, including the one just read. | 164 // Expect four hex digits, including the one just read. |
| 158 for (int i = 0; i < 4; i++) { | 165 for (int i = 0; i < 4; i++) { |
| 159 if (i > 0) { | 166 if (i > 0) { |
| 160 index++; | 167 if (iter.hasNext) { |
| 161 code = iter.next(); | 168 index++; |
| 169 code = iter.next(); |
| 170 } else { |
| 171 code = 0; |
| 172 } |
| 162 } | 173 } |
| 163 if (!isHexDigit(code)) { | 174 if (!isHexDigit(code)) { |
| 164 stringParseError("Invalid character in escape sequence", | 175 stringParseError("Invalid character in escape sequence", |
| 165 token, index); | 176 token, index); |
| 166 return null; | 177 return null; |
| 167 } | 178 } |
| 168 value = value * 16 + hexDigitValue(code); | 179 value = value * 16 + hexDigitValue(code); |
| 169 } | 180 } |
| 170 } | 181 } |
| 171 code = value; | 182 code = value; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 192 return null; | 203 return null; |
| 193 } | 204 } |
| 194 // String literal successfully validated. | 205 // String literal successfully validated. |
| 195 if (quoting.raw || !containsEscape) { | 206 if (quoting.raw || !containsEscape) { |
| 196 // A string without escapes could just as well have been raw. | 207 // A string without escapes could just as well have been raw. |
| 197 return new DartString.rawString(string, length); | 208 return new DartString.rawString(string, length); |
| 198 } | 209 } |
| 199 return new DartString.escapedString(string, length); | 210 return new DartString.escapedString(string, length); |
| 200 } | 211 } |
| 201 } | 212 } |
| OLD | NEW |