OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
196 if (result !== null) lastMatchInfoOverride = null; | 196 if (result !== null) lastMatchInfoOverride = null; |
197 regexp.lastIndex = 0; | 197 regexp.lastIndex = 0; |
198 return result; | 198 return result; |
199 } | 199 } |
200 // Non-regexp argument. | 200 // Non-regexp argument. |
201 regexp = new $RegExp(regexp); | 201 regexp = new $RegExp(regexp); |
202 return RegExpExecNoTests(regexp, subject, 0); | 202 return RegExpExecNoTests(regexp, subject, 0); |
203 } | 203 } |
204 | 204 |
205 | 205 |
206 // SubString is an internal function that returns the sub string of 'string'. | |
207 // If resulting string is of length 1, we use the one character cache | |
208 // otherwise we call the runtime system. | |
209 function SubString(string, start, end) { | |
210 // Use the one character string cache. | |
211 if (start + 1 == end) return %_StringCharAt(string, start); | |
212 return %_SubString(string, start, end); | |
213 } | |
214 | |
215 | |
216 // This has the same size as the lastMatchInfo array, and can be used for | 206 // This has the same size as the lastMatchInfo array, and can be used for |
217 // functions that expect that structure to be returned. It is used when the | 207 // functions that expect that structure to be returned. It is used when the |
218 // needle is a string rather than a regexp. In this case we can't update | 208 // needle is a string rather than a regexp. In this case we can't update |
219 // lastMatchArray without erroneously affecting the properties on the global | 209 // lastMatchArray without erroneously affecting the properties on the global |
220 // RegExp object. | 210 // RegExp object. |
221 var reusableMatchInfo = [2, "", "", -1, -1]; | 211 var reusableMatchInfo = [2, "", "", -1, -1]; |
222 | 212 |
223 | 213 |
224 // ECMA-262, section 15.5.4.11 | 214 // ECMA-262, section 15.5.4.11 |
225 function StringReplace(search, replace) { | 215 function StringReplace(search, replace) { |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
289 %StringIndexOf(replace, '$', 0) < 0) { | 279 %StringIndexOf(replace, '$', 0) < 0) { |
290 // Searching by traversing a cons string tree and replace with cons of | 280 // Searching by traversing a cons string tree and replace with cons of |
291 // slices works only when the replaced string is a single character, being | 281 // slices works only when the replaced string is a single character, being |
292 // replaced by a simple string and only pays off for long strings. | 282 // replaced by a simple string and only pays off for long strings. |
293 return %StringReplaceOneCharWithString(subject, search, replace); | 283 return %StringReplaceOneCharWithString(subject, search, replace); |
294 } | 284 } |
295 var start = %StringIndexOf(subject, search, 0); | 285 var start = %StringIndexOf(subject, search, 0); |
296 if (start < 0) return subject; | 286 if (start < 0) return subject; |
297 var end = start + search.length; | 287 var end = start + search.length; |
298 | 288 |
299 var result = SubString(subject, 0, start); | 289 var result = %_SubString(subject, 0, start); |
300 | 290 |
301 // Compute the string to replace with. | 291 // Compute the string to replace with. |
302 if (IS_SPEC_FUNCTION(replace)) { | 292 if (IS_SPEC_FUNCTION(replace)) { |
303 var receiver = %GetDefaultReceiver(replace); | 293 var receiver = %GetDefaultReceiver(replace); |
304 result += %_CallFunction(receiver, search, start, subject, replace); | 294 result += %_CallFunction(receiver, search, start, subject, replace); |
305 } else { | 295 } else { |
306 reusableMatchInfo[CAPTURE0] = start; | 296 reusableMatchInfo[CAPTURE0] = start; |
307 reusableMatchInfo[CAPTURE1] = end; | 297 reusableMatchInfo[CAPTURE1] = end; |
308 replace = TO_STRING_INLINE(replace); | 298 replace = TO_STRING_INLINE(replace); |
309 result = ExpandReplacement(replace, subject, reusableMatchInfo, result); | 299 result = ExpandReplacement(replace, subject, reusableMatchInfo, result); |
310 } | 300 } |
311 | 301 |
312 return result + SubString(subject, end, subject.length); | 302 return result + %_SubString(subject, end, subject.length); |
313 } | 303 } |
314 | 304 |
315 | 305 |
316 // Expand the $-expressions in the string and return a new string with | 306 // Expand the $-expressions in the string and return a new string with |
317 // the result. | 307 // the result. |
318 function ExpandReplacement(string, subject, matchInfo, result) { | 308 function ExpandReplacement(string, subject, matchInfo, result) { |
319 var length = string.length; | 309 var length = string.length; |
320 var next = %StringIndexOf(string, '$', 0); | 310 var next = %StringIndexOf(string, '$', 0); |
321 if (next < 0) { | 311 if (next < 0) { |
322 if (length > 0) result += string; | 312 if (length > 0) result += string; |
323 return result; | 313 return result; |
324 } | 314 } |
325 | 315 |
326 if (next > 0) result += SubString(string, 0, next); | 316 if (next > 0) result += %_SubString(string, 0, next); |
327 | 317 |
328 while (true) { | 318 while (true) { |
329 var expansion = '$'; | 319 var expansion = '$'; |
330 var position = next + 1; | 320 var position = next + 1; |
331 if (position < length) { | 321 if (position < length) { |
332 var peek = %_StringCharCodeAt(string, position); | 322 var peek = %_StringCharCodeAt(string, position); |
333 if (peek == 36) { // $$ | 323 if (peek == 36) { // $$ |
334 ++position; | 324 ++position; |
335 result += '$'; | 325 result += '$'; |
336 } else if (peek == 38) { // $& - match | 326 } else if (peek == 38) { // $& - match |
337 ++position; | 327 ++position; |
338 result += SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]); | 328 result += |
| 329 %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]); |
339 } else if (peek == 96) { // $` - prefix | 330 } else if (peek == 96) { // $` - prefix |
340 ++position; | 331 ++position; |
341 result += SubString(subject, 0, matchInfo[CAPTURE0]); | 332 result += %_SubString(subject, 0, matchInfo[CAPTURE0]); |
342 } else if (peek == 39) { // $' - suffix | 333 } else if (peek == 39) { // $' - suffix |
343 ++position; | 334 ++position; |
344 result += SubString(subject, matchInfo[CAPTURE1], subject.length); | 335 result += %_SubString(subject, matchInfo[CAPTURE1], subject.length); |
345 } else { | 336 } else { |
346 result += '$'; | 337 result += '$'; |
347 } | 338 } |
348 } else { | 339 } else { |
349 result += '$'; | 340 result += '$'; |
350 } | 341 } |
351 | 342 |
352 // Go the the next $ in the string. | 343 // Go the the next $ in the string. |
353 next = %StringIndexOf(string, '$', position); | 344 next = %StringIndexOf(string, '$', position); |
354 | 345 |
355 // Return if there are no more $ characters in the string. If we | 346 // Return if there are no more $ characters in the string. If we |
356 // haven't reached the end, we need to append the suffix. | 347 // haven't reached the end, we need to append the suffix. |
357 if (next < 0) { | 348 if (next < 0) { |
358 if (position < length) { | 349 if (position < length) { |
359 result += SubString(string, position, length); | 350 result += %_SubString(string, position, length); |
360 } | 351 } |
361 return result; | 352 return result; |
362 } | 353 } |
363 | 354 |
364 // Append substring between the previous and the next $ character. | 355 // Append substring between the previous and the next $ character. |
365 if (next > position) { | 356 if (next > position) { |
366 result += SubString(string, position, next); | 357 result += %_SubString(string, position, next); |
367 } | 358 } |
368 } | 359 } |
369 return result; | 360 return result; |
370 } | 361 } |
371 | 362 |
372 | 363 |
373 // Compute the string of a given regular expression capture. | 364 // Compute the string of a given regular expression capture. |
374 function CaptureString(string, lastCaptureInfo, index) { | 365 function CaptureString(string, lastCaptureInfo, index) { |
375 // Scale the index. | 366 // Scale the index. |
376 var scaled = index << 1; | 367 var scaled = index << 1; |
377 // Compute start and end. | 368 // Compute start and end. |
378 var start = lastCaptureInfo[CAPTURE(scaled)]; | 369 var start = lastCaptureInfo[CAPTURE(scaled)]; |
379 // If start isn't valid, return undefined. | 370 // If start isn't valid, return undefined. |
380 if (start < 0) return; | 371 if (start < 0) return; |
381 var end = lastCaptureInfo[CAPTURE(scaled + 1)]; | 372 var end = lastCaptureInfo[CAPTURE(scaled + 1)]; |
382 return SubString(string, start, end); | 373 return %_SubString(string, start, end); |
383 } | 374 } |
384 | 375 |
385 | 376 |
386 // TODO(lrn): This array will survive indefinitely if replace is never | 377 // TODO(lrn): This array will survive indefinitely if replace is never |
387 // called again. However, it will be empty, since the contents are cleared | 378 // called again. However, it will be empty, since the contents are cleared |
388 // in the finally block. | 379 // in the finally block. |
389 var reusableReplaceArray = new InternalArray(16); | 380 var reusableReplaceArray = new InternalArray(16); |
390 | 381 |
391 // Helper function for replacing regular expressions with the result of a | 382 // Helper function for replacing regular expressions with the result of a |
392 // function application in String.prototype.replace. | 383 // function application in String.prototype.replace. |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
468 } | 459 } |
469 | 460 |
470 | 461 |
471 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { | 462 function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { |
472 var matchInfo = DoRegExpExec(regexp, subject, 0); | 463 var matchInfo = DoRegExpExec(regexp, subject, 0); |
473 if (IS_NULL(matchInfo)) { | 464 if (IS_NULL(matchInfo)) { |
474 regexp.lastIndex = 0; | 465 regexp.lastIndex = 0; |
475 return subject; | 466 return subject; |
476 } | 467 } |
477 var index = matchInfo[CAPTURE0]; | 468 var index = matchInfo[CAPTURE0]; |
478 var result = SubString(subject, 0, index); | 469 var result = %_SubString(subject, 0, index); |
479 var endOfMatch = matchInfo[CAPTURE1]; | 470 var endOfMatch = matchInfo[CAPTURE1]; |
480 // Compute the parameter list consisting of the match, captures, index, | 471 // Compute the parameter list consisting of the match, captures, index, |
481 // and subject for the replace function invocation. | 472 // and subject for the replace function invocation. |
482 // The number of captures plus one for the match. | 473 // The number of captures plus one for the match. |
483 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; | 474 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; |
484 var replacement; | 475 var replacement; |
485 var receiver = %GetDefaultReceiver(replace); | 476 var receiver = %GetDefaultReceiver(replace); |
486 if (m == 1) { | 477 if (m == 1) { |
487 // No captures, only the match, which is always valid. | 478 // No captures, only the match, which is always valid. |
488 var s = SubString(subject, index, endOfMatch); | 479 var s = %_SubString(subject, index, endOfMatch); |
489 // Don't call directly to avoid exposing the built-in global object. | 480 // Don't call directly to avoid exposing the built-in global object. |
490 replacement = %_CallFunction(receiver, s, index, subject, replace); | 481 replacement = %_CallFunction(receiver, s, index, subject, replace); |
491 } else { | 482 } else { |
492 var parameters = new InternalArray(m + 2); | 483 var parameters = new InternalArray(m + 2); |
493 for (var j = 0; j < m; j++) { | 484 for (var j = 0; j < m; j++) { |
494 parameters[j] = CaptureString(subject, matchInfo, j); | 485 parameters[j] = CaptureString(subject, matchInfo, j); |
495 } | 486 } |
496 parameters[j] = index; | 487 parameters[j] = index; |
497 parameters[j + 1] = subject; | 488 parameters[j + 1] = subject; |
498 | 489 |
499 replacement = %Apply(replace, receiver, parameters, 0, j + 2); | 490 replacement = %Apply(replace, receiver, parameters, 0, j + 2); |
500 } | 491 } |
501 | 492 |
502 result += replacement; // The add method converts to string if necessary. | 493 result += replacement; // The add method converts to string if necessary. |
503 // Can't use matchInfo any more from here, since the function could | 494 // Can't use matchInfo any more from here, since the function could |
504 // overwrite it. | 495 // overwrite it. |
505 return result + SubString(subject, endOfMatch, subject.length); | 496 return result + %_SubString(subject, endOfMatch, subject.length); |
506 } | 497 } |
507 | 498 |
508 | 499 |
509 // ECMA-262 section 15.5.4.12 | 500 // ECMA-262 section 15.5.4.12 |
510 function StringSearch(re) { | 501 function StringSearch(re) { |
511 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { | 502 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { |
512 throw MakeTypeError("called_on_null_or_undefined", | 503 throw MakeTypeError("called_on_null_or_undefined", |
513 ["String.prototype.search"]); | 504 ["String.prototype.search"]); |
514 } | 505 } |
515 var regexp; | 506 var regexp; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
561 } else { | 552 } else { |
562 if (end_i > s_len) { | 553 if (end_i > s_len) { |
563 end_i = s_len; | 554 end_i = s_len; |
564 } | 555 } |
565 } | 556 } |
566 | 557 |
567 if (end_i <= start_i) { | 558 if (end_i <= start_i) { |
568 return ''; | 559 return ''; |
569 } | 560 } |
570 | 561 |
571 return SubString(s, start_i, end_i); | 562 return %_SubString(s, start_i, end_i); |
572 } | 563 } |
573 | 564 |
574 | 565 |
575 // ECMA-262 section 15.5.4.14 | 566 // ECMA-262 section 15.5.4.14 |
576 function StringSplit(separator, limit) { | 567 function StringSplit(separator, limit) { |
577 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { | 568 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { |
578 throw MakeTypeError("called_on_null_or_undefined", | 569 throw MakeTypeError("called_on_null_or_undefined", |
579 ["String.prototype.split"]); | 570 ["String.prototype.split"]); |
580 } | 571 } |
581 var subject = TO_STRING_INLINE(this); | 572 var subject = TO_STRING_INLINE(this); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
622 | 613 |
623 var currentIndex = 0; | 614 var currentIndex = 0; |
624 var startIndex = 0; | 615 var startIndex = 0; |
625 var startMatch = 0; | 616 var startMatch = 0; |
626 var result = []; | 617 var result = []; |
627 | 618 |
628 outer_loop: | 619 outer_loop: |
629 while (true) { | 620 while (true) { |
630 | 621 |
631 if (startIndex === length) { | 622 if (startIndex === length) { |
632 result.push(SubString(subject, currentIndex, length)); | 623 result.push(%_SubString(subject, currentIndex, length)); |
633 break; | 624 break; |
634 } | 625 } |
635 | 626 |
636 var matchInfo = DoRegExpExec(separator, subject, startIndex); | 627 var matchInfo = DoRegExpExec(separator, subject, startIndex); |
637 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) { | 628 if (matchInfo == null || length === (startMatch = matchInfo[CAPTURE0])) { |
638 result.push(SubString(subject, currentIndex, length)); | 629 result.push(%_SubString(subject, currentIndex, length)); |
639 break; | 630 break; |
640 } | 631 } |
641 var endIndex = matchInfo[CAPTURE1]; | 632 var endIndex = matchInfo[CAPTURE1]; |
642 | 633 |
643 // We ignore a zero-length match at the currentIndex. | 634 // We ignore a zero-length match at the currentIndex. |
644 if (startIndex === endIndex && endIndex === currentIndex) { | 635 if (startIndex === endIndex && endIndex === currentIndex) { |
645 startIndex++; | 636 startIndex++; |
646 continue; | 637 continue; |
647 } | 638 } |
648 | 639 |
649 if (currentIndex + 1 == startMatch) { | 640 result.push(%_SubString(subject, currentIndex, startMatch)); |
650 result.push(%_StringCharAt(subject, currentIndex)); | |
651 } else { | |
652 result.push(%_SubString(subject, currentIndex, startMatch)); | |
653 } | |
654 | 641 |
655 if (result.length === limit) break; | 642 if (result.length === limit) break; |
656 | 643 |
657 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE; | 644 var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE; |
658 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) { | 645 for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) { |
659 var start = matchInfo[i++]; | 646 var start = matchInfo[i++]; |
660 var end = matchInfo[i++]; | 647 var end = matchInfo[i++]; |
661 if (end != -1) { | 648 if (end != -1) { |
662 if (start + 1 == end) { | 649 result.push(%_SubString(subject, start, end)); |
663 result.push(%_StringCharAt(subject, start)); | |
664 } else { | |
665 result.push(%_SubString(subject, start, end)); | |
666 } | |
667 } else { | 650 } else { |
668 result.push(void 0); | 651 result.push(void 0); |
669 } | 652 } |
670 if (result.length === limit) break outer_loop; | 653 if (result.length === limit) break outer_loop; |
671 } | 654 } |
672 | 655 |
673 startIndex = currentIndex = endIndex; | 656 startIndex = currentIndex = endIndex; |
674 } | 657 } |
675 return result; | 658 return result; |
676 } | 659 } |
(...skipping 23 matching lines...) Expand all Loading... |
700 } else { | 683 } else { |
701 if (end_i < 0) end_i = 0; | 684 if (end_i < 0) end_i = 0; |
702 if (start_i > end_i) { | 685 if (start_i > end_i) { |
703 var tmp = end_i; | 686 var tmp = end_i; |
704 end_i = start_i; | 687 end_i = start_i; |
705 start_i = tmp; | 688 start_i = tmp; |
706 } | 689 } |
707 } | 690 } |
708 } | 691 } |
709 | 692 |
710 return ((start_i + 1 == end_i) | 693 return %_SubString(s, start_i, end_i); |
711 ? %_StringCharAt(s, start_i) | |
712 : %_SubString(s, start_i, end_i)); | |
713 } | 694 } |
714 | 695 |
715 | 696 |
716 // This is not a part of ECMA-262. | 697 // This is not a part of ECMA-262. |
717 function StringSubstr(start, n) { | 698 function StringSubstr(start, n) { |
718 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { | 699 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { |
719 throw MakeTypeError("called_on_null_or_undefined", | 700 throw MakeTypeError("called_on_null_or_undefined", |
720 ["String.prototype.substr"]); | 701 ["String.prototype.substr"]); |
721 } | 702 } |
722 var s = TO_STRING_INLINE(this); | 703 var s = TO_STRING_INLINE(this); |
(...skipping 21 matching lines...) Expand all Loading... |
744 // use zero. | 725 // use zero. |
745 if (start < 0) { | 726 if (start < 0) { |
746 start += s.length; | 727 start += s.length; |
747 if (start < 0) start = 0; | 728 if (start < 0) start = 0; |
748 } | 729 } |
749 } | 730 } |
750 | 731 |
751 var end = start + len; | 732 var end = start + len; |
752 if (end > s.length) end = s.length; | 733 if (end > s.length) end = s.length; |
753 | 734 |
754 return ((start + 1 == end) | 735 return %_SubString(s, start, end); |
755 ? %_StringCharAt(s, start) | |
756 : %_SubString(s, start, end)); | |
757 } | 736 } |
758 | 737 |
759 | 738 |
760 // ECMA-262, 15.5.4.16 | 739 // ECMA-262, 15.5.4.16 |
761 function StringToLowerCase() { | 740 function StringToLowerCase() { |
762 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { | 741 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) { |
763 throw MakeTypeError("called_on_null_or_undefined", | 742 throw MakeTypeError("called_on_null_or_undefined", |
764 ["String.prototype.toLowerCase"]); | 743 ["String.prototype.toLowerCase"]); |
765 } | 744 } |
766 return %StringToLowerCase(TO_STRING_INLINE(this)); | 745 return %StringToLowerCase(TO_STRING_INLINE(this)); |
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1015 "fixed", StringFixed, | 994 "fixed", StringFixed, |
1016 "italics", StringItalics, | 995 "italics", StringItalics, |
1017 "small", StringSmall, | 996 "small", StringSmall, |
1018 "strike", StringStrike, | 997 "strike", StringStrike, |
1019 "sub", StringSub, | 998 "sub", StringSub, |
1020 "sup", StringSup | 999 "sup", StringSup |
1021 )); | 1000 )); |
1022 } | 1001 } |
1023 | 1002 |
1024 SetUpString(); | 1003 SetUpString(); |
OLD | NEW |