| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 // Utilities for building JS ASTs at runtime. Contains a builder class | 5 // Utilities for building JS ASTs at runtime. Contains a builder class |
| 6 // and a parser that parses part of the language. | 6 // and a parser that parses part of the language. |
| 7 | 7 |
| 8 part of js_ast; | 8 part of js_ast; |
| 9 | 9 |
| 10 | 10 |
| (...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 */ | 292 */ |
| 293 Template expressionTemplateYielding(Node ast) { | 293 Template expressionTemplateYielding(Node ast) { |
| 294 return new Template.withExpressionResult(ast); | 294 return new Template.withExpressionResult(ast); |
| 295 } | 295 } |
| 296 | 296 |
| 297 Template statementTemplateYielding(Node ast) { | 297 Template statementTemplateYielding(Node ast) { |
| 298 return new Template.withStatementResult(ast); | 298 return new Template.withStatementResult(ast); |
| 299 } | 299 } |
| 300 | 300 |
| 301 /// Creates a literal js string from [value]. | 301 /// Creates a literal js string from [value]. |
| 302 LiteralString escapedString(String value) { | 302 LiteralString _legacyEscapedString(String value) { |
| 303 // Start by escaping the backslashes. | 303 // Start by escaping the backslashes. |
| 304 String escaped = value.replaceAll('\\', '\\\\'); | 304 String escaped = value.replaceAll('\\', '\\\\'); |
| 305 // Do not escape unicode characters and ' because they are allowed in the | 305 // Do not escape unicode characters and ' because they are allowed in the |
| 306 // string literal anyway. | 306 // string literal anyway. |
| 307 escaped = escaped.replaceAllMapped(new RegExp('\n|"|\b|\t|\v'), (match) { | 307 escaped = escaped.replaceAllMapped(new RegExp('\n|"|\b|\t|\v|\r'), (match) { |
| 308 switch (match.group(0)) { | 308 switch (match.group(0)) { |
| 309 case "\n" : return r"\n"; | 309 case "\n" : return r"\n"; |
| 310 case "\"" : return r'\"'; | 310 case "\"" : return r'\"'; |
| 311 case "\b" : return r"\b"; | 311 case "\b" : return r"\b"; |
| 312 case "\t" : return r"\t"; | 312 case "\t" : return r"\t"; |
| 313 case "\f" : return r"\f"; | 313 case "\f" : return r"\f"; |
| 314 case "\r" : return r"\r"; |
| 314 case "\v" : return r"\v"; | 315 case "\v" : return r"\v"; |
| 315 } | 316 } |
| 316 }); | 317 }); |
| 317 LiteralString result = string(escaped); | 318 LiteralString result = string(escaped); |
| 318 // We don't escape ' under the assumption that the string is wrapped | 319 // We don't escape ' under the assumption that the string is wrapped |
| 319 // into ". Verify that assumption. | 320 // into ". Verify that assumption. |
| 320 assert(result.value.codeUnitAt(0) == '"'.codeUnitAt(0)); | 321 assert(result.value.codeUnitAt(0) == '"'.codeUnitAt(0)); |
| 321 return result; | 322 return result; |
| 322 } | 323 } |
| 323 | 324 |
| 324 /// Creates a literal js string from [value]. | 325 /// Creates a literal js string from [value]. |
| 326 LiteralString escapedString(String value, |
| 327 {bool utf8: false, bool ascii: false}) { |
| 328 if (utf8 == false && ascii == false) return _legacyEscapedString(value); |
| 329 if (utf8 && ascii) throw new ArgumentError('Cannot be both UTF8 and ASCII'); |
| 330 |
| 331 int singleQuotes = 0; |
| 332 int doubleQuotes = 0; |
| 333 int otherEscapes = 0; |
| 334 int unpairedSurrogates = 0; |
| 335 |
| 336 for (int rune in value.runes) { |
| 337 if (rune == charCodes.$BACKSLASH) { |
| 338 ++otherEscapes; |
| 339 } else if (rune == charCodes.$SQ) { |
| 340 ++singleQuotes; |
| 341 } else if (rune == charCodes.$DQ) { |
| 342 ++doubleQuotes; |
| 343 } else if (rune == charCodes.$LF || rune == charCodes.$CR || |
| 344 rune == charCodes.$LS || rune == charCodes.$PS) { |
| 345 // Line terminators. |
| 346 ++otherEscapes; |
| 347 } else if (rune == charCodes.$BS || rune == charCodes.$TAB || |
| 348 rune == charCodes.$VTAB || rune == charCodes.$FF) { |
| 349 ++otherEscapes; |
| 350 } else if (_isUnpairedSurrogate(rune)) { |
| 351 ++unpairedSurrogates; |
| 352 } else { |
| 353 if (ascii && (rune < charCodes.$SPACE || rune >= charCodes.$DEL)) { |
| 354 ++otherEscapes; |
| 355 } |
| 356 } |
| 357 } |
| 358 |
| 359 LiteralString finish(String quote, String contents) { |
| 360 return new LiteralString('$quote$contents$quote'); |
| 361 } |
| 362 |
| 363 if (otherEscapes == 0 && unpairedSurrogates == 0) { |
| 364 if (doubleQuotes == 0) return finish('"', value); |
| 365 if (singleQuotes == 0) return finish("'", value); |
| 366 } |
| 367 |
| 368 bool useSingleQuotes = singleQuotes < doubleQuotes; |
| 369 |
| 370 StringBuffer sb = new StringBuffer(); |
| 371 |
| 372 for (int rune in value.runes) { |
| 373 String escape = _irregularEscape(rune, useSingleQuotes); |
| 374 if (escape != null) { |
| 375 sb.write(escape); |
| 376 continue; |
| 377 } |
| 378 if (rune == charCodes.$LS || |
| 379 rune == charCodes.$PS || |
| 380 _isUnpairedSurrogate(rune) || |
| 381 ascii && (rune < charCodes.$SPACE || rune >= charCodes.$DEL)) { |
| 382 if (rune < 0x100) { |
| 383 sb.write(r'\x'); |
| 384 sb.write(rune.toRadixString(16).padLeft(2, '0')); |
| 385 } else if (rune < 0x10000) { |
| 386 sb.write(r'\u'); |
| 387 sb.write(rune.toRadixString(16).padLeft(4, '0')); |
| 388 } else { |
| 389 // Not all browsers accept the ES6 \u{zzzzzz} encoding, so emit two |
| 390 // surrogate pairs. |
| 391 var bits = rune - 0x10000; |
| 392 var leading = 0xD800 | (bits >> 10); |
| 393 var trailing = 0xDC00 | (bits & 0x3ff); |
| 394 sb.write(r'\u'); |
| 395 sb.write(leading.toRadixString(16)); |
| 396 sb.write(r'\u'); |
| 397 sb.write(trailing.toRadixString(16)); |
| 398 } |
| 399 } else { |
| 400 sb.writeCharCode(rune); |
| 401 } |
| 402 } |
| 403 |
| 404 return finish(useSingleQuotes ? "'" : '"', sb.toString()); |
| 405 } |
| 406 |
| 407 static bool _isUnpairedSurrogate(int code) => (code & 0xFFFFF800) == 0xD800; |
| 408 |
| 409 static String _irregularEscape(int code, bool useSingleQuotes) { |
| 410 switch (code) { |
| 411 case charCodes.$SQ: return useSingleQuotes ? r"\'" : r"'"; |
| 412 case charCodes.$DQ: return useSingleQuotes ? r'"' : r'\"'; |
| 413 case charCodes.$BACKSLASH: return r'\\'; |
| 414 case charCodes.$BS: return r'\b'; |
| 415 case charCodes.$TAB: return r'\t'; |
| 416 case charCodes.$LF: return r'\n'; |
| 417 case charCodes.$VTAB: return r'\v'; |
| 418 case charCodes.$FF: return r'\f'; |
| 419 case charCodes.$CR: return r'\r'; |
| 420 } |
| 421 return null; |
| 422 } |
| 423 |
| 424 /// Creates a literal js string from [value]. |
| 325 /// | 425 /// |
| 326 /// Note that this function only puts quotes around [value]. It does not do | 426 /// Note that this function only puts quotes around [value]. It does not do |
| 327 /// any escaping, so use only when you can guarantee that [value] does not | 427 /// any escaping, so use only when you can guarantee that [value] does not |
| 328 /// contain newlines or backslashes. For escaping the string use | 428 /// contain newlines or backslashes. For escaping the string use |
| 329 /// [escapedString]. | 429 /// [escapedString]. |
| 330 LiteralString string(String value) => new LiteralString('"$value"'); | 430 LiteralString string(String value) => new LiteralString('"$value"'); |
| 331 | 431 |
| 332 /// Creates an instance of [LiteralString] from [value]. | 432 /// Creates an instance of [LiteralString] from [value]. |
| 333 /// | 433 /// |
| 334 /// Does not add quotes or do any escaping. | 434 /// Does not add quotes or do any escaping. |
| (...skipping 1094 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1429 | 1529 |
| 1430 class _InterleaveIterable extends IterableBase { | 1530 class _InterleaveIterable extends IterableBase { |
| 1431 Iterable<Node> source; | 1531 Iterable<Node> source; |
| 1432 Node separator; | 1532 Node separator; |
| 1433 | 1533 |
| 1434 _InterleaveIterable(this.source, this.separator); | 1534 _InterleaveIterable(this.source, this.separator); |
| 1435 | 1535 |
| 1436 Iterator<Node> get iterator { | 1536 Iterator<Node> get iterator { |
| 1437 return new _InterleaveIterator(source.iterator, separator); | 1537 return new _InterleaveIterator(source.iterator, separator); |
| 1438 } | 1538 } |
| 1439 } | 1539 } |
| OLD | NEW |