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 _interceptors; | 5 part of _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 class JSString extends Interceptor implements String, JSIndexable { | 13 class JSString extends Interceptor implements String, JSIndexable { |
14 const JSString(); | 14 const JSString(); |
15 | 15 |
16 @NoInline() | 16 @NoInline() |
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 return _codeUnitAt(index); | 20 return _codeUnitAt(index); |
21 } | 21 } |
22 | 22 |
23 int _codeUnitAt(int index) { | 23 int _codeUnitAt(int index) { |
24 if (index >= length) throw diagnoseIndexError(this, index); | 24 if (index >= length) throw diagnoseIndexError(this, index); |
25 return JS('JSUInt31', r'#.charCodeAt(#)', this, index); | 25 return JS('JSUInt31', r'#.charCodeAt(#)', this, index); |
26 } | 26 } |
27 | 27 |
28 Iterable<Match> allMatches(String string, [int start = 0]) { | 28 Iterable<Match> allMatches(String string, [int start = 0]) { |
(...skipping 13 matching lines...) Expand all Loading... |
42 // TODO(lrn): See if this can be optimized. | 42 // TODO(lrn): See if this can be optimized. |
43 for (int i = 0; i < this.length; i++) { | 43 for (int i = 0; i < this.length; i++) { |
44 if (string.codeUnitAt(start + i) != this.codeUnitAt(i)) { | 44 if (string.codeUnitAt(start + i) != this.codeUnitAt(i)) { |
45 return null; | 45 return null; |
46 } | 46 } |
47 } | 47 } |
48 return new StringMatch(start, string, this); | 48 return new StringMatch(start, string, this); |
49 } | 49 } |
50 | 50 |
51 String operator +(String other) { | 51 String operator +(String other) { |
52 if (other is !String) throw new ArgumentError.value(other); | 52 if (other is! String) throw new ArgumentError.value(other); |
53 return JS('String', r'# + #', this, other); | 53 return JS('String', r'# + #', this, other); |
54 } | 54 } |
55 | 55 |
56 bool endsWith(String other) { | 56 bool endsWith(String other) { |
57 checkString(other); | 57 checkString(other); |
58 int otherLength = other.length; | 58 int otherLength = other.length; |
59 if (otherLength > length) return false; | 59 if (otherLength > length) return false; |
60 return other == substring(length - otherLength); | 60 return other == substring(length - otherLength); |
61 } | 61 } |
62 | 62 |
63 String replaceAll(Pattern from, String to) { | 63 String replaceAll(Pattern from, String to) { |
64 checkString(to); | 64 checkString(to); |
65 return stringReplaceAllUnchecked(this, from, to); | 65 return stringReplaceAllUnchecked(this, from, to); |
66 } | 66 } |
67 | 67 |
68 String replaceAllMapped(Pattern from, String convert(Match match)) { | 68 String replaceAllMapped(Pattern from, String convert(Match match)) { |
69 return this.splitMapJoin(from, onMatch: convert); | 69 return this.splitMapJoin(from, onMatch: convert); |
70 } | 70 } |
71 | 71 |
72 String splitMapJoin(Pattern from, | 72 String splitMapJoin(Pattern from, |
73 {String onMatch(Match match), | 73 {String onMatch(Match match), String onNonMatch(String nonMatch)}) { |
74 String onNonMatch(String nonMatch)}) { | |
75 return stringReplaceAllFuncUnchecked(this, from, onMatch, onNonMatch); | 74 return stringReplaceAllFuncUnchecked(this, from, onMatch, onNonMatch); |
76 } | 75 } |
77 | 76 |
78 String replaceFirst(Pattern from, String to, [int startIndex = 0]) { | 77 String replaceFirst(Pattern from, String to, [int startIndex = 0]) { |
79 checkString(to); | 78 checkString(to); |
80 checkInt(startIndex); | 79 checkInt(startIndex); |
81 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); | 80 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); |
82 return stringReplaceFirstUnchecked(this, from, to, startIndex); | 81 return stringReplaceFirstUnchecked(this, from, to, startIndex); |
83 } | 82 } |
84 | 83 |
85 String replaceFirstMapped(Pattern from, String replace(Match match), | 84 String replaceFirstMapped(Pattern from, String replace(Match match), |
86 [int startIndex = 0]) { | 85 [int startIndex = 0]) { |
87 checkNull(replace); | 86 checkNull(replace); |
88 checkInt(startIndex); | 87 checkInt(startIndex); |
89 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); | 88 RangeError.checkValueInInterval(startIndex, 0, this.length, "startIndex"); |
90 return stringReplaceFirstMappedUnchecked(this, from, replace, startIndex); | 89 return stringReplaceFirstMappedUnchecked(this, from, replace, startIndex); |
91 } | 90 } |
92 | 91 |
93 List<String> split(Pattern pattern) { | 92 List<String> split(Pattern pattern) { |
94 checkNull(pattern); | 93 checkNull(pattern); |
95 if (pattern is String) { | 94 if (pattern is String) { |
96 return JS('JSExtendableArray', r'#.split(#)', this, pattern); | 95 return JS('JSExtendableArray', r'#.split(#)', this, pattern); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
150 if (endIndex > length) return false; | 149 if (endIndex > length) return false; |
151 return other == JS('String', r'#.substring(#, #)', this, index, endIndex); | 150 return other == JS('String', r'#.substring(#, #)', this, index, endIndex); |
152 } | 151 } |
153 return pattern.matchAsPrefix(this, index) != null; | 152 return pattern.matchAsPrefix(this, index) != null; |
154 } | 153 } |
155 | 154 |
156 String substring(int startIndex, [int endIndex]) { | 155 String substring(int startIndex, [int endIndex]) { |
157 checkInt(startIndex); | 156 checkInt(startIndex); |
158 if (endIndex == null) endIndex = length; | 157 if (endIndex == null) endIndex = length; |
159 checkInt(endIndex); | 158 checkInt(endIndex); |
160 if (startIndex < 0 ) throw new RangeError.value(startIndex); | 159 if (startIndex < 0) throw new RangeError.value(startIndex); |
161 if (startIndex > endIndex) throw new RangeError.value(startIndex); | 160 if (startIndex > endIndex) throw new RangeError.value(startIndex); |
162 if (endIndex > length) throw new RangeError.value(endIndex); | 161 if (endIndex > length) throw new RangeError.value(endIndex); |
163 return JS('String', r'#.substring(#, #)', this, startIndex, endIndex); | 162 return JS('String', r'#.substring(#, #)', this, startIndex, endIndex); |
164 } | 163 } |
165 | 164 |
166 String toLowerCase() { | 165 String toLowerCase() { |
167 return JS( | 166 return JS('returns:String;effects:none;depends:none;throws:null(1)', |
168 'returns:String;effects:none;depends:none;throws:null(1)', | |
169 r'#.toLowerCase()', this); | 167 r'#.toLowerCase()', this); |
170 } | 168 } |
171 | 169 |
172 String toUpperCase() { | 170 String toUpperCase() { |
173 return JS( | 171 return JS('returns:String;effects:none;depends:none;throws:null(1)', |
174 'returns:String;effects:none;depends:none;throws:null(1)', | |
175 r'#.toUpperCase()', this); | 172 r'#.toUpperCase()', this); |
176 } | 173 } |
177 | 174 |
178 // Characters with Whitespace property (Unicode 6.3). | 175 // Characters with Whitespace property (Unicode 6.3). |
179 // 0009..000D ; White_Space # Cc <control-0009>..<control-000D> | 176 // 0009..000D ; White_Space # Cc <control-0009>..<control-000D> |
180 // 0020 ; White_Space # Zs SPACE | 177 // 0020 ; White_Space # Zs SPACE |
181 // 0085 ; White_Space # Cc <control-0085> | 178 // 0085 ; White_Space # Cc <control-0085> |
182 // 00A0 ; White_Space # Zs NO-BREAK SPACE | 179 // 00A0 ; White_Space # Zs NO-BREAK SPACE |
183 // 1680 ; White_Space # Zs OGHAM SPACE MARK | 180 // 1680 ; White_Space # Zs OGHAM SPACE MARK |
184 // 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE | 181 // 2000..200A ; White_Space # Zs EN QUAD..HAIR SPACE |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 } else { | 340 } else { |
344 result = this; | 341 result = this; |
345 endIndex = _skipTrailingWhitespace(this, this.length); | 342 endIndex = _skipTrailingWhitespace(this, this.length); |
346 } | 343 } |
347 | 344 |
348 if (endIndex == result.length) return result; | 345 if (endIndex == result.length) return result; |
349 if (endIndex == 0) return ""; | 346 if (endIndex == 0) return ""; |
350 return JS('String', r'#.substring(#, #)', result, 0, endIndex); | 347 return JS('String', r'#.substring(#, #)', result, 0, endIndex); |
351 } | 348 } |
352 | 349 |
353 String operator*(int times) { | 350 String operator *(int times) { |
354 if (0 >= times) return ''; // Unnecessary but hoists argument type check. | 351 if (0 >= times) return ''; // Unnecessary but hoists argument type check. |
355 if (times == 1 || this.length == 0) return this; | 352 if (times == 1 || this.length == 0) return this; |
356 if (times != JS('JSUInt32', '# >>> 0', times)) { | 353 if (times != JS('JSUInt32', '# >>> 0', times)) { |
357 // times >= 2^32. We can't create a string that big. | 354 // times >= 2^32. We can't create a string that big. |
358 throw const OutOfMemoryError(); | 355 throw const OutOfMemoryError(); |
359 } | 356 } |
360 var result = ''; | 357 var result = ''; |
361 var s = this; | 358 var s = this; |
362 while (true) { | 359 while (true) { |
363 if (times & 1 == 1) result = s + result; | 360 if (times & 1 == 1) result = s + result; |
364 times = JS('JSUInt31', '# >>> 1', times); | 361 times = JS('JSUInt31', '# >>> 1', times); |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
432 throw new RangeError.range(startIndex, 0, this.length); | 429 throw new RangeError.range(startIndex, 0, this.length); |
433 } | 430 } |
434 return stringContainsUnchecked(this, other, startIndex); | 431 return stringContainsUnchecked(this, other, startIndex); |
435 } | 432 } |
436 | 433 |
437 bool get isEmpty => length == 0; | 434 bool get isEmpty => length == 0; |
438 | 435 |
439 bool get isNotEmpty => !isEmpty; | 436 bool get isNotEmpty => !isEmpty; |
440 | 437 |
441 int compareTo(String other) { | 438 int compareTo(String other) { |
442 if (other is !String) throw argumentErrorValue(other); | 439 if (other is! String) throw argumentErrorValue(other); |
443 return this == other ? 0 | 440 return this == other ? 0 : JS('bool', r'# < #', this, other) ? -1 : 1; |
444 : JS('bool', r'# < #', this, other) ? -1 : 1; | |
445 } | 441 } |
446 | 442 |
447 // Note: if you change this, also change the function [S]. | 443 // Note: if you change this, also change the function [S]. |
448 String toString() => this; | 444 String toString() => this; |
449 | 445 |
450 /** | 446 /** |
451 * This is the [Jenkins hash function][1] but using masking to keep | 447 * This is the [Jenkins hash function][1] but using masking to keep |
452 * values in SMI range. | 448 * values in SMI range. |
453 * | 449 * |
454 * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function | 450 * [1]: http://en.wikipedia.org/wiki/Jenkins_hash_function |
455 */ | 451 */ |
456 int get hashCode { | 452 int get hashCode { |
457 // TODO(ahe): This method shouldn't have to use JS. Update when our | 453 // TODO(ahe): This method shouldn't have to use JS. Update when our |
458 // optimizations are smarter. | 454 // optimizations are smarter. |
459 int hash = 0; | 455 int hash = 0; |
460 for (int i = 0; i < length; i++) { | 456 for (int i = 0; i < length; i++) { |
461 hash = 0x1fffffff & (hash + JS('int', r'#.charCodeAt(#)', this, i)); | 457 hash = 0x1fffffff & (hash + JS('int', r'#.charCodeAt(#)', this, i)); |
462 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); | 458 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); |
463 hash = JS('int', '# ^ (# >> 6)', hash, hash); | 459 hash = JS('int', '# ^ (# >> 6)', hash, hash); |
464 } | 460 } |
465 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | 461 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); |
466 hash = JS('int', '# ^ (# >> 11)', hash, hash); | 462 hash = JS('int', '# ^ (# >> 11)', hash, hash); |
467 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | 463 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); |
468 } | 464 } |
469 | 465 |
470 Type get runtimeType => String; | 466 Type get runtimeType => String; |
471 | 467 |
472 int get length => JS('int', r'#.length', this); | 468 int get length => JS('int', r'#.length', this); |
473 | 469 |
474 String operator [](int index) { | 470 String operator [](int index) { |
475 if (index is !int) throw diagnoseIndexError(this, index); | 471 if (index is! int) throw diagnoseIndexError(this, index); |
476 if (index >= length || index < 0) throw diagnoseIndexError(this, index); | 472 if (index >= length || index < 0) throw diagnoseIndexError(this, index); |
477 return JS('String', '#[#]', this, index); | 473 return JS('String', '#[#]', this, index); |
478 } | 474 } |
479 } | 475 } |
OLD | NEW |