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