| 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 part of dart._interceptors; | 5 part of dart._interceptors; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * The interceptor class for [String]. The compiler recognizes this | 8 * The interceptor class for [String]. The compiler recognizes this |
| 9 * class as an interceptor, and changes references to [:this:] to | 9 * class as an interceptor, and changes references to [:this:] to |
| 10 * actually use the receiver of the method, which is generated as an extra | 10 * actually use the receiver of the method, which is generated as an extra |
| 11 * argument added to each member. | 11 * argument added to each member. |
| 12 */ | 12 */ |
| 13 @JsPeerInterface(name: 'String') | 13 @JsPeerInterface(name: 'String') |
| 14 class JSString extends Interceptor implements String, JSIndexable<String> { | 14 class JSString extends Interceptor implements String, JSIndexable<String> { |
| 15 const JSString(); | 15 const JSString(); |
| 16 | 16 |
| 17 int codeUnitAt(int index) { | 17 int codeUnitAt(int index) { |
| 18 if (index is !int) throw diagnoseIndexError(this, index); | 18 if (index is! int) throw diagnoseIndexError(this, index); |
| 19 if (index < 0) throw diagnoseIndexError(this, index); | 19 if (index < 0) throw diagnoseIndexError(this, index); |
| 20 if (index >= length) throw diagnoseIndexError(this, index); | 20 if (index >= length) throw diagnoseIndexError(this, index); |
| 21 return JS('int', r'#.charCodeAt(#)', this, index); | 21 return JS('int', r'#.charCodeAt(#)', this, index); |
| 22 } | 22 } |
| 23 | 23 |
| 24 Iterable<Match> allMatches(String string, [int start = 0]) { | 24 Iterable<Match> allMatches(String string, [int start = 0]) { |
| 25 checkString(string); | 25 checkString(string); |
| 26 checkInt(start); | 26 checkInt(start); |
| 27 if (0 > start || start > string.length) { | 27 if (0 > start || start > string.length) { |
| 28 throw new RangeError.range(start, 0, string.length); | 28 throw new RangeError.range(start, 0, string.length); |
| 29 } | 29 } |
| 30 return allMatchesInStringUnchecked(this, string, start); | 30 return allMatchesInStringUnchecked(this, string, start); |
| 31 } | 31 } |
| 32 | 32 |
| 33 Match matchAsPrefix(String string, [int start = 0]) { | 33 Match matchAsPrefix(String string, [int start = 0]) { |
| 34 if (start < 0 || start > string.length) { | 34 if (start < 0 || start > string.length) { |
| 35 throw new RangeError.range(start, 0, string.length); | 35 throw new RangeError.range(start, 0, string.length); |
| 36 } | 36 } |
| 37 if (start + this.length > string.length) return null; | 37 if (start + this.length > string.length) return null; |
| 38 // TODO(lrn): See if this can be optimized. | 38 // TODO(lrn): See if this can be optimized. |
| 39 for (int i = 0; i < this.length; i++) { | 39 for (int i = 0; i < this.length; i++) { |
| 40 if (string.codeUnitAt(start + i) != this.codeUnitAt(i)) { | 40 if (string.codeUnitAt(start + i) != this.codeUnitAt(i)) { |
| 41 return null; | 41 return null; |
| 42 } | 42 } |
| 43 } | 43 } |
| 44 return new StringMatch(start, string, this); | 44 return new StringMatch(start, string, this); |
| 45 } | 45 } |
| 46 | 46 |
| 47 String operator +(String other) { | 47 String operator +(String other) { |
| 48 if (other is !String) throw new ArgumentError.value(other); | 48 if (other is! String) throw new ArgumentError.value(other); |
| 49 return JS('String', r'# + #', this, other); | 49 return JS('String', r'# + #', this, other); |
| 50 } | 50 } |
| 51 | 51 |
| 52 bool endsWith(String other) { | 52 bool endsWith(String other) { |
| 53 checkString(other); | 53 checkString(other); |
| 54 int otherLength = other.length; | 54 int otherLength = other.length; |
| 55 if (otherLength > length) return false; | 55 if (otherLength > length) return false; |
| 56 return other == substring(length - otherLength); | 56 return other == substring(length - otherLength); |
| 57 } | 57 } |
| 58 | 58 |
| 59 String replaceAll(Pattern from, String to) { | 59 String replaceAll(Pattern from, String to) { |
| 60 checkString(to); | 60 checkString(to); |
| 61 return stringReplaceAllUnchecked(this, from, to); | 61 return stringReplaceAllUnchecked(this, from, to); |
| 62 } | 62 } |
| 63 | 63 |
| 64 String replaceAllMapped(Pattern from, String convert(Match match)) { | 64 String replaceAllMapped(Pattern from, String convert(Match match)) { |
| 65 return this.splitMapJoin(from, onMatch: convert); | 65 return this.splitMapJoin(from, onMatch: convert); |
| 66 } | 66 } |
| 67 | 67 |
| 68 String splitMapJoin(Pattern from, | 68 String splitMapJoin(Pattern from, |
| 69 {String onMatch(Match match), | 69 {String onMatch(Match match), String onNonMatch(String nonMatch)}) { |
| 70 String onNonMatch(String nonMatch)}) { | |
| 71 return stringReplaceAllFuncUnchecked(this, from, onMatch, onNonMatch); | 70 return stringReplaceAllFuncUnchecked(this, from, onMatch, onNonMatch); |
| 72 } | 71 } |
| 73 | 72 |
| 74 String replaceFirst(Pattern from, String to, [int startIndex = 0]) { | 73 String replaceFirst(Pattern from, String to, [int startIndex = 0]) { |
| 75 checkString(to); | 74 checkString(to); |
| 76 checkInt(startIndex); | 75 checkInt(startIndex); |
| 77 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); | 76 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); |
| 78 return stringReplaceFirstUnchecked(this, from, to, startIndex); | 77 return stringReplaceFirstUnchecked(this, from, to, startIndex); |
| 79 } | 78 } |
| 80 | 79 |
| 81 String replaceFirstMapped(Pattern from, String replace(Match match), | 80 String replaceFirstMapped(Pattern from, String replace(Match match), |
| 82 [int startIndex = 0]) { | 81 [int startIndex = 0]) { |
| 83 checkNull(replace); | 82 checkNull(replace); |
| 84 checkInt(startIndex); | 83 checkInt(startIndex); |
| 85 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); | 84 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); |
| 86 return stringReplaceFirstMappedUnchecked(this, from, replace, startIndex); | 85 return stringReplaceFirstMappedUnchecked(this, from, replace, startIndex); |
| 87 } | 86 } |
| 88 | 87 |
| 89 List<String> split(Pattern pattern) { | 88 List<String> split(Pattern pattern) { |
| 90 checkNull(pattern); | 89 checkNull(pattern); |
| 91 if (pattern is String) { | 90 if (pattern is String) { |
| 92 return JS('JSExtendableArray', r'#.split(#)', this, pattern); | 91 return JS('JSExtendableArray', r'#.split(#)', this, pattern); |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 if (endIndex > length) return false; | 145 if (endIndex > length) return false; |
| 147 return other == JS('String', r'#.substring(#, #)', this, index, endIndex); | 146 return other == JS('String', r'#.substring(#, #)', this, index, endIndex); |
| 148 } | 147 } |
| 149 return pattern.matchAsPrefix(this, index) != null; | 148 return pattern.matchAsPrefix(this, index) != null; |
| 150 } | 149 } |
| 151 | 150 |
| 152 String substring(int startIndex, [int endIndex]) { | 151 String substring(int startIndex, [int endIndex]) { |
| 153 checkInt(startIndex); | 152 checkInt(startIndex); |
| 154 if (endIndex == null) endIndex = length; | 153 if (endIndex == null) endIndex = length; |
| 155 checkInt(endIndex); | 154 checkInt(endIndex); |
| 156 if (startIndex < 0 ) throw new RangeError.value(startIndex); | 155 if (startIndex < 0) throw new RangeError.value(startIndex); |
| 157 if (startIndex > endIndex) throw new RangeError.value(startIndex); | 156 if (startIndex > endIndex) throw new RangeError.value(startIndex); |
| 158 if (endIndex > length) throw new RangeError.value(endIndex); | 157 if (endIndex > length) throw new RangeError.value(endIndex); |
| 159 return JS('String', r'#.substring(#, #)', this, startIndex, endIndex); | 158 return JS('String', r'#.substring(#, #)', this, startIndex, endIndex); |
| 160 } | 159 } |
| 161 | 160 |
| 162 String toLowerCase() { | 161 String toLowerCase() { |
| 163 return JS('String', r'#.toLowerCase()', this); | 162 return JS('String', r'#.toLowerCase()', this); |
| 164 } | 163 } |
| 165 | 164 |
| 166 String toUpperCase() { | 165 String toUpperCase() { |
| (...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 } else { | 336 } else { |
| 338 result = this; | 337 result = this; |
| 339 endIndex = _skipTrailingWhitespace(this, this.length); | 338 endIndex = _skipTrailingWhitespace(this, this.length); |
| 340 } | 339 } |
| 341 | 340 |
| 342 if (endIndex == result.length) return result; | 341 if (endIndex == result.length) return result; |
| 343 if (endIndex == 0) return ""; | 342 if (endIndex == 0) return ""; |
| 344 return JS('String', r'#.substring(#, #)', result, 0, endIndex); | 343 return JS('String', r'#.substring(#, #)', result, 0, endIndex); |
| 345 } | 344 } |
| 346 | 345 |
| 347 String operator*(int times) { | 346 String operator *(int times) { |
| 348 if (0 >= times) return ''; // Unnecessary but hoists argument type check. | 347 if (0 >= times) return ''; // Unnecessary but hoists argument type check. |
| 349 if (times == 1 || this.length == 0) return this; | 348 if (times == 1 || this.length == 0) return this; |
| 350 if (times != JS('int', '# >>> 0', times)) { | 349 if (times != JS('int', '# >>> 0', times)) { |
| 351 // times >= 2^32. We can't create a string that big. | 350 // times >= 2^32. We can't create a string that big. |
| 352 throw const OutOfMemoryError(); | 351 throw const OutOfMemoryError(); |
| 353 } | 352 } |
| 354 var result = ''; | 353 var result = ''; |
| 355 String s = this; | 354 String s = this; |
| 356 while (true) { | 355 while (true) { |
| 357 if (times & 1 == 1) result = s + result; | 356 if (times & 1 == 1) result = s + result; |
| 358 times = JS('int', '# >>> 1', times); | 357 times = JS('int', '# >>> 1', times); |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 426 throw new RangeError.range(startIndex, 0, this.length); | 425 throw new RangeError.range(startIndex, 0, this.length); |
| 427 } | 426 } |
| 428 return stringContainsUnchecked(this, other, startIndex); | 427 return stringContainsUnchecked(this, other, startIndex); |
| 429 } | 428 } |
| 430 | 429 |
| 431 bool get isEmpty => length == 0; | 430 bool get isEmpty => length == 0; |
| 432 | 431 |
| 433 bool get isNotEmpty => !isEmpty; | 432 bool get isNotEmpty => !isEmpty; |
| 434 | 433 |
| 435 int compareTo(String other) { | 434 int compareTo(String other) { |
| 436 if (other is !String) throw argumentErrorValue(other); | 435 if (other is! String) throw argumentErrorValue(other); |
| 437 return this == other ? 0 | 436 return this == other ? 0 : JS('bool', r'# < #', this, other) ? -1 : 1; |
| 438 : JS('bool', r'# < #', this, other) ? -1 : 1; | |
| 439 } | 437 } |
| 440 | 438 |
| 441 // Note: if you change this, also change the function [S]. | 439 // Note: if you change this, also change the function [S]. |
| 442 String toString() => this; | 440 String toString() => this; |
| 443 | 441 |
| 444 /** | 442 /** |
| 445 * This is the [Jenkins hash function][1] but using masking to keep | 443 * This is the [Jenkins hash function][1] but using masking to keep |
| 446 * values in SMI range. | 444 * values in SMI range. |
| 447 * | 445 * |
| 448 * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function | 446 * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function |
| 449 */ | 447 */ |
| 450 int get hashCode { | 448 int get hashCode { |
| 451 // TODO(ahe): This method shouldn't have to use JS. Update when our | 449 // TODO(ahe): This method shouldn't have to use JS. Update when our |
| 452 // optimizations are smarter. | 450 // optimizations are smarter. |
| 453 int hash = 0; | 451 int hash = 0; |
| 454 for (int i = 0; i < length; i++) { | 452 for (int i = 0; i < length; i++) { |
| 455 hash = 0x1fffffff & (hash + JS('int', r'#.charCodeAt(#)', this, i)); | 453 hash = 0x1fffffff & (hash + JS('int', r'#.charCodeAt(#)', this, i)); |
| 456 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); | 454 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); |
| 457 hash = JS('int', '# ^ (# >> 6)', hash, hash); | 455 hash = JS('int', '# ^ (# >> 6)', hash, hash); |
| 458 } | 456 } |
| 459 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | 457 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); |
| 460 hash = JS('int', '# ^ (# >> 11)', hash, hash); | 458 hash = JS('int', '# ^ (# >> 11)', hash, hash); |
| 461 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | 459 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); |
| 462 } | 460 } |
| 463 | 461 |
| 464 Type get runtimeType => String; | 462 Type get runtimeType => String; |
| 465 | 463 |
| 466 int get length => JS('int', r'#.length', this); | 464 int get length => JS('int', r'#.length', this); |
| 467 | 465 |
| 468 String operator [](int index) { | 466 String operator [](int index) { |
| 469 if (index is !int) throw diagnoseIndexError(this, index); | 467 if (index is! int) throw diagnoseIndexError(this, index); |
| 470 if (index >= length || index < 0) throw diagnoseIndexError(this, index); | 468 if (index >= length || index < 0) throw diagnoseIndexError(this, index); |
| 471 return JS('String', '#[#]', this, index); | 469 return JS('String', '#[#]', this, index); |
| 472 } | 470 } |
| 473 } | 471 } |
| OLD | NEW |