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

Side by Side Diff: pkg/js_ast/lib/src/builder.dart

Issue 1520033002: js_ast: Better escaping of strings in preparation for utf8 file encoding (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Created 5 years 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 unified diff | Download patch
OLDNEW
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698