OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 (function(global, utils) { | 5 (function(global, utils) { |
6 | 6 |
7 %CheckIsBootstrapping(); | 7 %CheckIsBootstrapping(); |
8 | 8 |
9 // ------------------------------------------------------------------- | 9 // ------------------------------------------------------------------- |
10 // Imports | 10 // Imports |
11 | 11 |
12 var GlobalRegExp = global.RegExp; | 12 var GlobalRegExp = global.RegExp; |
13 var GlobalString = global.String; | 13 var GlobalString = global.String; |
14 var InternalArray = utils.InternalArray; | 14 var InternalArray = utils.InternalArray; |
15 var InternalPackedArray = utils.InternalPackedArray; | 15 var InternalPackedArray = utils.InternalPackedArray; |
16 | 16 |
17 var MathMax; | 17 var MathMax; |
18 var MathMin; | 18 var MathMin; |
| 19 var RegExpExec; |
| 20 var RegExpExecNoTests; |
| 21 var RegExpLastMatchInfo; |
19 | 22 |
20 utils.Import(function(from) { | 23 utils.Import(function(from) { |
21 MathMax = from.MathMax; | 24 MathMax = from.MathMax; |
22 MathMin = from.MathMin; | 25 MathMin = from.MathMin; |
| 26 RegExpExec = from.RegExpExec; |
| 27 RegExpExecNoTests = from.RegExpExecNoTests; |
| 28 RegExpLastMatchInfo = from.RegExpLastMatchInfo; |
23 }); | 29 }); |
24 | 30 |
25 //------------------------------------------------------------------- | 31 //------------------------------------------------------------------- |
26 | 32 |
27 function StringConstructor(x) { | 33 function StringConstructor(x) { |
28 if (%_ArgumentsLength() == 0) x = ''; | 34 if (%_ArgumentsLength() == 0) x = ''; |
29 if (%_IsConstructCall()) { | 35 if (%_IsConstructCall()) { |
30 %_SetValueOf(this, TO_STRING_INLINE(x)); | 36 %_SetValueOf(this, TO_STRING_INLINE(x)); |
31 } else { | 37 } else { |
32 return IS_SYMBOL(x) ? | 38 return IS_SYMBOL(x) ? |
(...skipping 122 matching lines...) Loading... |
155 // ECMA-262 section 15.5.4.10 | 161 // ECMA-262 section 15.5.4.10 |
156 function StringMatchJS(regexp) { | 162 function StringMatchJS(regexp) { |
157 CHECK_OBJECT_COERCIBLE(this, "String.prototype.match"); | 163 CHECK_OBJECT_COERCIBLE(this, "String.prototype.match"); |
158 | 164 |
159 var subject = TO_STRING_INLINE(this); | 165 var subject = TO_STRING_INLINE(this); |
160 if (IS_REGEXP(regexp)) { | 166 if (IS_REGEXP(regexp)) { |
161 // Emulate RegExp.prototype.exec's side effect in step 5, even though | 167 // Emulate RegExp.prototype.exec's side effect in step 5, even though |
162 // value is discarded. | 168 // value is discarded. |
163 var lastIndex = regexp.lastIndex; | 169 var lastIndex = regexp.lastIndex; |
164 TO_INTEGER_FOR_SIDE_EFFECT(lastIndex); | 170 TO_INTEGER_FOR_SIDE_EFFECT(lastIndex); |
165 if (!regexp.global) return $regexpExecNoTests(regexp, subject, 0); | 171 if (!regexp.global) return RegExpExecNoTests(regexp, subject, 0); |
166 var result = %StringMatch(subject, regexp, $regexpLastMatchInfo); | 172 var result = %StringMatch(subject, regexp, RegExpLastMatchInfo); |
167 if (result !== null) $regexpLastMatchInfoOverride = null; | 173 if (result !== null) $regexpLastMatchInfoOverride = null; |
168 regexp.lastIndex = 0; | 174 regexp.lastIndex = 0; |
169 return result; | 175 return result; |
170 } | 176 } |
171 // Non-regexp argument. | 177 // Non-regexp argument. |
172 regexp = new GlobalRegExp(regexp); | 178 regexp = new GlobalRegExp(regexp); |
173 return $regexpExecNoTests(regexp, subject, 0); | 179 return RegExpExecNoTests(regexp, subject, 0); |
174 } | 180 } |
175 | 181 |
176 | 182 |
177 // ECMA-262 v6, section 21.1.3.12 | 183 // ECMA-262 v6, section 21.1.3.12 |
178 // | 184 // |
179 // For now we do nothing, as proper normalization requires big tables. | 185 // For now we do nothing, as proper normalization requires big tables. |
180 // If Intl is enabled, then i18n.js will override it and provide the the | 186 // If Intl is enabled, then i18n.js will override it and provide the the |
181 // proper functionality. | 187 // proper functionality. |
182 function StringNormalizeJS(form) { | 188 function StringNormalizeJS(form) { |
183 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize"); | 189 CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize"); |
184 | 190 |
185 var form = form ? TO_STRING_INLINE(form) : 'NFC'; | 191 var form = form ? TO_STRING_INLINE(form) : 'NFC'; |
186 | 192 |
187 var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD']; | 193 var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD']; |
188 | 194 |
189 var normalizationForm = NORMALIZATION_FORMS.indexOf(form); | 195 var normalizationForm = NORMALIZATION_FORMS.indexOf(form); |
190 if (normalizationForm === -1) { | 196 if (normalizationForm === -1) { |
191 throw MakeRangeError(kNormalizationForm, NORMALIZATION_FORMS.join(', ')); | 197 throw MakeRangeError(kNormalizationForm, NORMALIZATION_FORMS.join(', ')); |
192 } | 198 } |
193 | 199 |
194 return %_ValueOf(this); | 200 return %_ValueOf(this); |
195 } | 201 } |
196 | 202 |
197 | 203 |
198 // This has the same size as the $regexpLastMatchInfo array, and can be used | 204 // This has the same size as the RegExpLastMatchInfo array, and can be used |
199 // for functions that expect that structure to be returned. It is used when | 205 // for functions that expect that structure to be returned. It is used when |
200 // the needle is a string rather than a regexp. In this case we can't update | 206 // the needle is a string rather than a regexp. In this case we can't update |
201 // lastMatchArray without erroneously affecting the properties on the global | 207 // lastMatchArray without erroneously affecting the properties on the global |
202 // RegExp object. | 208 // RegExp object. |
203 var reusableMatchInfo = [2, "", "", -1, -1]; | 209 var reusableMatchInfo = [2, "", "", -1, -1]; |
204 | 210 |
205 | 211 |
206 // ECMA-262, section 15.5.4.11 | 212 // ECMA-262, section 15.5.4.11 |
207 function StringReplace(search, replace) { | 213 function StringReplace(search, replace) { |
208 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace"); | 214 CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace"); |
(...skipping 21 matching lines...) Loading... |
230 // Emulate RegExp.prototype.exec's side effect in step 5, even if | 236 // Emulate RegExp.prototype.exec's side effect in step 5, even if |
231 // value is discarded. | 237 // value is discarded. |
232 var lastIndex = search.lastIndex; | 238 var lastIndex = search.lastIndex; |
233 TO_INTEGER_FOR_SIDE_EFFECT(lastIndex); | 239 TO_INTEGER_FOR_SIDE_EFFECT(lastIndex); |
234 | 240 |
235 if (!IS_SPEC_FUNCTION(replace)) { | 241 if (!IS_SPEC_FUNCTION(replace)) { |
236 replace = TO_STRING_INLINE(replace); | 242 replace = TO_STRING_INLINE(replace); |
237 | 243 |
238 if (!search.global) { | 244 if (!search.global) { |
239 // Non-global regexp search, string replace. | 245 // Non-global regexp search, string replace. |
240 var match = $regexpExec(search, subject, 0); | 246 var match = RegExpExec(search, subject, 0); |
241 if (match == null) { | 247 if (match == null) { |
242 search.lastIndex = 0 | 248 search.lastIndex = 0 |
243 return subject; | 249 return subject; |
244 } | 250 } |
245 if (replace.length == 0) { | 251 if (replace.length == 0) { |
246 return %_SubString(subject, 0, match[CAPTURE0]) + | 252 return %_SubString(subject, 0, match[CAPTURE0]) + |
247 %_SubString(subject, match[CAPTURE1], subject.length) | 253 %_SubString(subject, match[CAPTURE1], subject.length) |
248 } | 254 } |
249 return ExpandReplacement(replace, subject, $regexpLastMatchInfo, | 255 return ExpandReplacement(replace, subject, RegExpLastMatchInfo, |
250 %_SubString(subject, 0, match[CAPTURE0])) + | 256 %_SubString(subject, 0, match[CAPTURE0])) + |
251 %_SubString(subject, match[CAPTURE1], subject.length); | 257 %_SubString(subject, match[CAPTURE1], subject.length); |
252 } | 258 } |
253 | 259 |
254 // Global regexp search, string replace. | 260 // Global regexp search, string replace. |
255 search.lastIndex = 0; | 261 search.lastIndex = 0; |
256 if ($regexpLastMatchInfoOverride == null) { | 262 if ($regexpLastMatchInfoOverride == null) { |
257 return %StringReplaceGlobalRegExpWithString( | 263 return %StringReplaceGlobalRegExpWithString( |
258 subject, search, replace, $regexpLastMatchInfo); | 264 subject, search, replace, RegExpLastMatchInfo); |
259 } else { | 265 } else { |
260 // We use this hack to detect whether StringReplaceRegExpWithString | 266 // We use this hack to detect whether StringReplaceRegExpWithString |
261 // found at least one hit. In that case we need to remove any | 267 // found at least one hit. In that case we need to remove any |
262 // override. | 268 // override. |
263 var saved_subject = $regexpLastMatchInfo[LAST_SUBJECT_INDEX]; | 269 var saved_subject = RegExpLastMatchInfo[LAST_SUBJECT_INDEX]; |
264 $regexpLastMatchInfo[LAST_SUBJECT_INDEX] = 0; | 270 RegExpLastMatchInfo[LAST_SUBJECT_INDEX] = 0; |
265 var answer = %StringReplaceGlobalRegExpWithString( | 271 var answer = %StringReplaceGlobalRegExpWithString( |
266 subject, search, replace, $regexpLastMatchInfo); | 272 subject, search, replace, RegExpLastMatchInfo); |
267 if (%_IsSmi($regexpLastMatchInfo[LAST_SUBJECT_INDEX])) { | 273 if (%_IsSmi(RegExpLastMatchInfo[LAST_SUBJECT_INDEX])) { |
268 $regexpLastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject; | 274 RegExpLastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject; |
269 } else { | 275 } else { |
270 $regexpLastMatchInfoOverride = null; | 276 $regexpLastMatchInfoOverride = null; |
271 } | 277 } |
272 return answer; | 278 return answer; |
273 } | 279 } |
274 } | 280 } |
275 | 281 |
276 if (search.global) { | 282 if (search.global) { |
277 // Global regexp search, function replace. | 283 // Global regexp search, function replace. |
278 return StringReplaceGlobalRegExpWithFunction(subject, search, replace); | 284 return StringReplaceGlobalRegExpWithFunction(subject, search, replace); |
(...skipping 145 matching lines...) Loading... |
424 reusableReplaceArray = null; | 430 reusableReplaceArray = null; |
425 } else { | 431 } else { |
426 // Inside a nested replace (replace called from the replacement function | 432 // Inside a nested replace (replace called from the replacement function |
427 // of another replace) or we have failed to set the reusable array | 433 // of another replace) or we have failed to set the reusable array |
428 // back due to an exception in a replacement function. Create a new | 434 // back due to an exception in a replacement function. Create a new |
429 // array to use in the future, or until the original is written back. | 435 // array to use in the future, or until the original is written back. |
430 resultArray = new InternalArray(16); | 436 resultArray = new InternalArray(16); |
431 } | 437 } |
432 var res = %RegExpExecMultiple(regexp, | 438 var res = %RegExpExecMultiple(regexp, |
433 subject, | 439 subject, |
434 $regexpLastMatchInfo, | 440 RegExpLastMatchInfo, |
435 resultArray); | 441 resultArray); |
436 regexp.lastIndex = 0; | 442 regexp.lastIndex = 0; |
437 if (IS_NULL(res)) { | 443 if (IS_NULL(res)) { |
438 // No matches at all. | 444 // No matches at all. |
439 reusableReplaceArray = resultArray; | 445 reusableReplaceArray = resultArray; |
440 return subject; | 446 return subject; |
441 } | 447 } |
442 var len = res.length; | 448 var len = res.length; |
443 if (NUMBER_OF_CAPTURES($regexpLastMatchInfo) == 2) { | 449 if (NUMBER_OF_CAPTURES(RegExpLastMatchInfo) == 2) { |
444 // If the number of captures is two then there are no explicit captures in | 450 // If the number of captures is two then there are no explicit captures in |
445 // the regexp, just the implicit capture that captures the whole match. In | 451 // the regexp, just the implicit capture that captures the whole match. In |
446 // this case we can simplify quite a bit and end up with something faster. | 452 // this case we can simplify quite a bit and end up with something faster. |
447 // The builder will consist of some integers that indicate slices of the | 453 // The builder will consist of some integers that indicate slices of the |
448 // input string and some replacements that were returned from the replace | 454 // input string and some replacements that were returned from the replace |
449 // function. | 455 // function. |
450 var match_start = 0; | 456 var match_start = 0; |
451 var override = new InternalPackedArray(null, 0, subject); | 457 var override = new InternalPackedArray(null, 0, subject); |
452 for (var i = 0; i < len; i++) { | 458 for (var i = 0; i < len; i++) { |
453 var elem = res[i]; | 459 var elem = res[i]; |
(...skipping 33 matching lines...) Loading... |
487 } | 493 } |
488 } | 494 } |
489 var result = %StringBuilderConcat(res, res.length, subject); | 495 var result = %StringBuilderConcat(res, res.length, subject); |
490 resultArray.length = 0; | 496 resultArray.length = 0; |
491 reusableReplaceArray = resultArray; | 497 reusableReplaceArray = resultArray; |
492 return result; | 498 return result; |
493 } | 499 } |
494 | 500 |
495 | 501 |
496 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { | 502 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { |
497 var matchInfo = $regexpExec(regexp, subject, 0); | 503 var matchInfo = RegExpExec(regexp, subject, 0); |
498 if (IS_NULL(matchInfo)) { | 504 if (IS_NULL(matchInfo)) { |
499 regexp.lastIndex = 0; | 505 regexp.lastIndex = 0; |
500 return subject; | 506 return subject; |
501 } | 507 } |
502 var index = matchInfo[CAPTURE0]; | 508 var index = matchInfo[CAPTURE0]; |
503 var result = %_SubString(subject, 0, index); | 509 var result = %_SubString(subject, 0, index); |
504 var endOfMatch = matchInfo[CAPTURE1]; | 510 var endOfMatch = matchInfo[CAPTURE1]; |
505 // Compute the parameter list consisting of the match, captures, index, | 511 // Compute the parameter list consisting of the match, captures, index, |
506 // and subject for the replace function invocation. | 512 // and subject for the replace function invocation. |
507 // The number of captures plus one for the match. | 513 // The number of captures plus one for the match. |
(...skipping 27 matching lines...) Loading... |
535 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search"); | 541 CHECK_OBJECT_COERCIBLE(this, "String.prototype.search"); |
536 | 542 |
537 var regexp; | 543 var regexp; |
538 if (IS_STRING(re)) { | 544 if (IS_STRING(re)) { |
539 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re); | 545 regexp = %_GetFromCache(STRING_TO_REGEXP_CACHE_ID, re); |
540 } else if (IS_REGEXP(re)) { | 546 } else if (IS_REGEXP(re)) { |
541 regexp = re; | 547 regexp = re; |
542 } else { | 548 } else { |
543 regexp = new GlobalRegExp(re); | 549 regexp = new GlobalRegExp(re); |
544 } | 550 } |
545 var match = $regexpExec(regexp, TO_STRING_INLINE(this), 0); | 551 var match = RegExpExec(regexp, TO_STRING_INLINE(this), 0); |
546 if (match) { | 552 if (match) { |
547 return match[CAPTURE0]; | 553 return match[CAPTURE0]; |
548 } | 554 } |
549 return -1; | 555 return -1; |
550 } | 556 } |
551 | 557 |
552 | 558 |
553 // ECMA-262 section 15.5.4.13 | 559 // ECMA-262 section 15.5.4.13 |
554 function StringSlice(start, end) { | 560 function StringSlice(start, end) { |
555 CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice"); | 561 CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice"); |
(...skipping 65 matching lines...) Loading... |
621 | 627 |
622 if (limit === 0) return []; | 628 if (limit === 0) return []; |
623 | 629 |
624 // Separator is a regular expression. | 630 // Separator is a regular expression. |
625 return StringSplitOnRegExp(subject, separator, limit, length); | 631 return StringSplitOnRegExp(subject, separator, limit, length); |
626 } | 632 } |
627 | 633 |
628 | 634 |
629 function StringSplitOnRegExp(subject, separator, limit, length) { | 635 function StringSplitOnRegExp(subject, separator, limit, length) { |
630 if (length === 0) { | 636 if (length === 0) { |
631 if ($regexpExec(separator, subject, 0, 0) != null) { | 637 if (RegExpExec(separator, subject, 0, 0) != null) { |
632 return []; | 638 return []; |
633 } | 639 } |
634 return [subject]; | 640 return [subject]; |
635 } | 641 } |
636 | 642 |
637 var currentIndex = 0; | 643 var currentIndex = 0; |
638 var startIndex = 0; | 644 var startIndex = 0; |
639 var startMatch = 0; | 645 var startMatch = 0; |
640 var result = new InternalArray(); | 646 var result = new InternalArray(); |
641 | 647 |
642 outer_loop: | 648 outer_loop: |
643 while (true) { | 649 while (true) { |
644 | 650 |
645 if (startIndex === length) { | 651 if (startIndex === length) { |
646 result[result.length] = %_SubString(subject, currentIndex, length); | 652 result[result.length] = %_SubString(subject, currentIndex, length); |
647 break; | 653 break; |
648 } | 654 } |
649 | 655 |
650 var matchInfo = $regexpExec(separator, subject, startIndex); | 656 var matchInfo = RegExpExec(separator, subject, startIndex); |
651 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) { | 657 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) { |
652 result[result.length] = %_SubString(subject, currentIndex, length); | 658 result[result.length] = %_SubString(subject, currentIndex, length); |
653 break; | 659 break; |
654 } | 660 } |
655 var endIndex = matchInfo[CAPTURE1]; | 661 var endIndex = matchInfo[CAPTURE1]; |
656 | 662 |
657 // We ignore a zero-length match at the currentIndex. | 663 // We ignore a zero-length match at the currentIndex. |
658 if (startIndex === endIndex && endIndex === currentIndex) { | 664 if (startIndex === endIndex && endIndex === currentIndex) { |
659 startIndex++; | 665 startIndex++; |
660 continue; | 666 continue; |
(...skipping 456 matching lines...) Loading... |
1117 | 1123 |
1118 // Set the String function and constructor. | 1124 // Set the String function and constructor. |
1119 %SetCode(GlobalString, StringConstructor); | 1125 %SetCode(GlobalString, StringConstructor); |
1120 %FunctionSetPrototype(GlobalString, new GlobalString()); | 1126 %FunctionSetPrototype(GlobalString, new GlobalString()); |
1121 | 1127 |
1122 // Set up the constructor property on the String prototype object. | 1128 // Set up the constructor property on the String prototype object. |
1123 %AddNamedProperty( | 1129 %AddNamedProperty( |
1124 GlobalString.prototype, "constructor", GlobalString, DONT_ENUM); | 1130 GlobalString.prototype, "constructor", GlobalString, DONT_ENUM); |
1125 | 1131 |
1126 // Set up the non-enumerable functions on the String object. | 1132 // Set up the non-enumerable functions on the String object. |
1127 $installFunctions(GlobalString, DONT_ENUM, [ | 1133 utils.InstallFunctions(GlobalString, DONT_ENUM, [ |
1128 "fromCharCode", StringFromCharCode, | 1134 "fromCharCode", StringFromCharCode, |
1129 "fromCodePoint", StringFromCodePoint, | 1135 "fromCodePoint", StringFromCodePoint, |
1130 "raw", StringRaw | 1136 "raw", StringRaw |
1131 ]); | 1137 ]); |
1132 | 1138 |
1133 // Set up the non-enumerable functions on the String prototype object. | 1139 // Set up the non-enumerable functions on the String prototype object. |
1134 $installFunctions(GlobalString.prototype, DONT_ENUM, [ | 1140 utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [ |
1135 "valueOf", StringValueOf, | 1141 "valueOf", StringValueOf, |
1136 "toString", StringToString, | 1142 "toString", StringToString, |
1137 "charAt", StringCharAtJS, | 1143 "charAt", StringCharAtJS, |
1138 "charCodeAt", StringCharCodeAtJS, | 1144 "charCodeAt", StringCharCodeAtJS, |
1139 "codePointAt", StringCodePointAt, | 1145 "codePointAt", StringCodePointAt, |
1140 "concat", StringConcat, | 1146 "concat", StringConcat, |
1141 "endsWith", StringEndsWith, | 1147 "endsWith", StringEndsWith, |
1142 "includes", StringIncludes, | 1148 "includes", StringIncludes, |
1143 "indexOf", StringIndexOfJS, | 1149 "indexOf", StringIndexOfJS, |
1144 "lastIndexOf", StringLastIndexOfJS, | 1150 "lastIndexOf", StringLastIndexOfJS, |
(...skipping 34 matching lines...) Loading... |
1179 // ------------------------------------------------------------------- | 1185 // ------------------------------------------------------------------- |
1180 // Exports | 1186 // Exports |
1181 | 1187 |
1182 utils.Export(function(to) { | 1188 utils.Export(function(to) { |
1183 to.StringCharAt = StringCharAtJS; | 1189 to.StringCharAt = StringCharAtJS; |
1184 to.StringIndexOf = StringIndexOfJS; | 1190 to.StringIndexOf = StringIndexOfJS; |
1185 to.StringSubstring = StringSubstring; | 1191 to.StringSubstring = StringSubstring; |
1186 }); | 1192 }); |
1187 | 1193 |
1188 }) | 1194 }) |
OLD | NEW |