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 |