Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 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 #include "src/builtins/builtins-utils.h" | 5 #include "src/builtins/builtins-utils.h" |
| 6 #include "src/builtins/builtins.h" | 6 #include "src/builtins/builtins.h" |
| 7 | 7 |
| 8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
| 9 #include "src/regexp/jsregexp.h" | 9 #include "src/regexp/jsregexp.h" |
| 10 | 10 |
| (...skipping 419 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 430 | 430 |
| 431 a->Bind(&construct_result); | 431 a->Bind(&construct_result); |
| 432 { | 432 { |
| 433 Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, | 433 Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, |
| 434 match_elements, string); | 434 match_elements, string); |
| 435 a->Return(result); | 435 a->Return(result); |
| 436 } | 436 } |
| 437 } | 437 } |
| 438 } | 438 } |
| 439 | 439 |
| 440 namespace { | |
| 441 | |
| 442 compiler::Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, | |
| 443 compiler::Node* context, | |
| 444 compiler::Node* value, | |
| 445 char const* method_name) { | |
| 446 typedef compiler::Node Node; | |
| 447 typedef CodeStubAssembler::Label Label; | |
| 448 typedef CodeStubAssembler::Variable Variable; | |
| 449 | |
| 450 Label out(a), throw_exception(a, Label::kDeferred); | |
| 451 Variable var_value_map(a, MachineRepresentation::kTagged); | |
| 452 | |
| 453 a->GotoIf(a->WordIsSmi(value), &throw_exception); | |
| 454 | |
| 455 // Load the instance type of the {value}. | |
| 456 var_value_map.Bind(a->LoadMap(value)); | |
| 457 Node* const value_instance_type = | |
| 458 a->LoadMapInstanceType(var_value_map.value()); | |
| 459 | |
| 460 a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, | |
| 461 &throw_exception); | |
| 462 | |
| 463 // The {value} is not a compatible receiver for this method. | |
| 464 a->Bind(&throw_exception); | |
| 465 { | |
| 466 Node* const message_id = | |
| 467 a->SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonObject)); | |
| 468 Node* const method_name_str = a->HeapConstant( | |
| 469 isolate->factory()->NewStringFromAsciiChecked(method_name, TENURED)); | |
| 470 | |
| 471 Callable callable = CodeFactory::ToString(isolate); | |
| 472 Node* const value_str = a->CallStub(callable, context, value); | |
| 473 | |
| 474 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, | |
| 475 method_name_str, value_str); | |
| 476 var_value_map.Bind(a->UndefinedConstant()); | |
| 477 a->Goto(&out); // Never reached. | |
| 478 } | |
| 479 | |
| 480 a->Bind(&out); | |
| 481 return var_value_map.value(); | |
| 482 } | |
| 483 | |
| 484 compiler::Node* IsInitialRegExpMap(CodeStubAssembler* a, | |
| 485 compiler::Node* context, | |
| 486 compiler::Node* map) { | |
| 487 typedef compiler::Node Node; | |
| 488 | |
| 489 Node* const native_context = a->LoadNativeContext(context); | |
| 490 Node* const regexp_fun = | |
| 491 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | |
| 492 Node* const initial_map = | |
| 493 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | |
| 494 Node* const has_initialmap = a->WordEqual(map, initial_map); | |
| 495 | |
| 496 return has_initialmap; | |
| 497 } | |
| 498 | |
| 499 } // namespace | |
| 500 | |
| 501 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeStubAssembler* a) { | |
| 502 typedef CodeStubAssembler::Variable Variable; | |
| 503 typedef CodeStubAssembler::Label Label; | |
| 504 typedef compiler::Node Node; | |
| 505 | |
| 506 Node* const receiver = a->Parameter(0); | |
| 507 Node* const context = a->Parameter(3); | |
| 508 | |
| 509 Isolate* isolate = a->isolate(); | |
| 510 Node* const int_zero = a->IntPtrConstant(0); | |
| 511 Node* const int_one = a->IntPtrConstant(1); | |
| 512 | |
| 513 Node* const map = ThrowIfNotJSReceiver(a, isolate, context, receiver, | |
|
Benedikt Meurer
2016/10/05 16:39:23
This could be more efficient if we first check for
jgruber
2016/10/06 12:34:34
Ack. I'll add this to my backlog and fix all usage
| |
| 514 "RegExp.prototype.flags"); | |
| 515 | |
| 516 Variable var_length(a, MachineType::PointerRepresentation()); | |
| 517 Variable var_flags(a, MachineType::PointerRepresentation()); | |
| 518 | |
| 519 // First, count the number of characters we will need and check which flags | |
| 520 // are set. | |
| 521 | |
| 522 var_length.Bind(int_zero); | |
| 523 | |
| 524 Label if_isunmodifiedjsregexp(a), | |
| 525 if_isnotunmodifiedjsregexp(a, Label::kDeferred); | |
| 526 a->Branch(IsInitialRegExpMap(a, context, map), &if_isunmodifiedjsregexp, | |
| 527 &if_isnotunmodifiedjsregexp); | |
| 528 | |
| 529 Label construct_string(a); | |
| 530 a->Bind(&if_isunmodifiedjsregexp); | |
| 531 { | |
| 532 // Refer to JSRegExp's flag property on the fast-path. | |
| 533 Node* const flags_smi = | |
| 534 a->LoadObjectField(receiver, JSRegExp::kFlagsOffset); | |
| 535 Node* const flags_intptr = a->SmiUntag(flags_smi); | |
| 536 var_flags.Bind(flags_intptr); | |
| 537 | |
| 538 Label label_global(a), label_ignorecase(a), label_multiline(a), | |
| 539 label_unicode(a), label_sticky(a); | |
| 540 | |
| 541 #define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL) \ | |
| 542 do { \ | |
| 543 a->Bind(&LABEL); \ | |
| 544 Node* const mask = a->IntPtrConstant(FLAG); \ | |
| 545 a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ | |
| 546 &NEXT_LABEL); \ | |
| 547 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ | |
| 548 a->Goto(&NEXT_LABEL); \ | |
| 549 } while (false) | |
| 550 | |
| 551 a->Goto(&label_global); | |
| 552 CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase); | |
| 553 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline); | |
| 554 CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode); | |
| 555 CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky); | |
| 556 CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string); | |
| 557 #undef CASE_FOR_FLAG | |
| 558 } | |
| 559 | |
| 560 a->Bind(&if_isnotunmodifiedjsregexp); | |
| 561 { | |
| 562 // Fall back to GetProperty stub on the slow-path. | |
| 563 var_flags.Bind(int_zero); | |
| 564 | |
| 565 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | |
| 566 Label label_global(a), label_ignorecase(a), label_multiline(a), | |
| 567 label_unicode(a), label_sticky(a); | |
| 568 | |
| 569 #define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL) \ | |
| 570 do { \ | |
| 571 a->Bind(&LABEL); \ | |
| 572 Node* const name = \ | |
| 573 a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \ | |
| 574 Node* const flag = \ | |
| 575 a->CallStub(getproperty_callable, context, receiver, name); \ | |
| 576 Label if_isflagset(a); \ | |
| 577 a->BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL); \ | |
| 578 a->Bind(&if_isflagset); \ | |
| 579 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ | |
| 580 var_flags.Bind(a->WordOr(var_flags.value(), a->IntPtrConstant(FLAG))); \ | |
| 581 a->Goto(&NEXT_LABEL); \ | |
| 582 } while (false) | |
| 583 | |
| 584 a->Goto(&label_global); | |
| 585 CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase); | |
| 586 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase, | |
| 587 label_multiline); | |
| 588 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline, | |
| 589 label_unicode); | |
| 590 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky); | |
| 591 CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string); | |
| 592 #undef CASE_FOR_FLAG | |
| 593 } | |
| 594 | |
| 595 // Allocate a string of the required length and fill it with the corresponding | |
| 596 // char for each set flag. | |
| 597 | |
| 598 a->Bind(&construct_string); | |
| 599 { | |
| 600 Node* const result = | |
| 601 a->AllocateSeqOneByteString(context, var_length.value()); | |
| 602 Node* const flags_intptr = var_flags.value(); | |
| 603 | |
| 604 Variable var_offset(a, MachineType::PointerRepresentation()); | |
| 605 var_offset.Bind( | |
| 606 a->IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); | |
| 607 | |
| 608 Label label_global(a), label_ignorecase(a), label_multiline(a), | |
| 609 label_unicode(a), label_sticky(a), out(a); | |
| 610 | |
| 611 #define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL) \ | |
| 612 do { \ | |
| 613 a->Bind(&LABEL); \ | |
| 614 Node* const mask = a->IntPtrConstant(FLAG); \ | |
| 615 a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ | |
| 616 &NEXT_LABEL); \ | |
| 617 Node* const value = a->IntPtrConstant(CHAR); \ | |
| 618 a->StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ | |
| 619 var_offset.value(), value); \ | |
| 620 var_offset.Bind(a->IntPtrAdd(var_offset.value(), int_one)); \ | |
| 621 a->Goto(&NEXT_LABEL); \ | |
| 622 } while (false) | |
| 623 | |
| 624 a->Goto(&label_global); | |
| 625 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase); | |
| 626 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase, | |
| 627 label_multiline); | |
| 628 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode); | |
| 629 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky); | |
| 630 CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out); | |
| 631 #undef CASE_FOR_FLAG | |
| 632 | |
| 633 a->Bind(&out); | |
| 634 a->Return(result); | |
| 635 } | |
| 636 } | |
| 637 | |
| 638 // ES6 21.2.5.10. | |
| 639 BUILTIN(RegExpPrototypeSourceGetter) { | |
| 640 HandleScope scope(isolate); | |
| 641 | |
| 642 Handle<Object> recv = args.receiver(); | |
| 643 if (!recv->IsJSRegExp()) { | |
| 644 Handle<JSFunction> regexp_fun = isolate->regexp_function(); | |
| 645 if (*recv == regexp_fun->prototype()) { | |
| 646 isolate->CountUsage(v8::Isolate::kRegExpPrototypeSourceGetter); | |
| 647 return *isolate->factory()->NewStringFromAsciiChecked("(?:)"); | |
| 648 } | |
| 649 THROW_NEW_ERROR_RETURN_FAILURE( | |
| 650 isolate, NewTypeError(MessageTemplate::kRegExpNonRegExp, | |
| 651 isolate->factory()->NewStringFromAsciiChecked( | |
| 652 "RegExp.prototype.source"))); | |
| 653 } | |
| 654 | |
| 655 Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(recv); | |
| 656 return regexp->source(); | |
| 657 } | |
| 658 | |
| 659 // ES6 21.2.4.2. | |
| 660 BUILTIN(RegExpPrototypeSpeciesGetter) { | |
| 661 HandleScope scope(isolate); | |
| 662 return *args.receiver(); | |
| 663 } | |
| 664 | |
| 665 namespace { | |
| 666 | |
| 667 void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag, | |
| 668 v8::Isolate::UseCounterFeature counter, | |
| 669 const char* method_name) { | |
| 670 typedef CodeStubAssembler::Label Label; | |
| 671 typedef compiler::Node Node; | |
| 672 | |
| 673 Node* const receiver = a->Parameter(0); | |
| 674 Node* const context = a->Parameter(3); | |
| 675 | |
| 676 Isolate* isolate = a->isolate(); | |
| 677 Node* const int_zero = a->IntPtrConstant(0); | |
| 678 | |
| 679 // Check whether we have an unmodified regexp instance. | |
| 680 Label if_isunmodifiedjsregexp(a), | |
| 681 if_isnotunmodifiedjsregexp(a, Label::kDeferred); | |
| 682 | |
| 683 a->GotoIf(a->WordIsSmi(receiver), &if_isnotunmodifiedjsregexp); | |
| 684 | |
| 685 Node* const receiver_map = a->LoadMap(receiver); | |
| 686 Node* const instance_type = a->LoadMapInstanceType(receiver_map); | |
| 687 | |
| 688 a->Branch(a->Word32Equal(instance_type, a->Int32Constant(JS_REGEXP_TYPE)), | |
| 689 &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp); | |
| 690 | |
| 691 a->Bind(&if_isunmodifiedjsregexp); | |
| 692 { | |
| 693 // Refer to JSRegExp's flag property on the fast-path. | |
| 694 Node* const flags_smi = | |
| 695 a->LoadObjectField(receiver, JSRegExp::kFlagsOffset); | |
| 696 Node* const flags_intptr = a->SmiUntag(flags_smi); | |
| 697 Node* const mask = a->IntPtrConstant(flag); | |
| 698 Node* const is_global = | |
| 699 a->WordNotEqual(a->WordAnd(flags_intptr, mask), int_zero); | |
| 700 a->Return(a->Select(is_global, a->TrueConstant(), a->FalseConstant())); | |
| 701 } | |
| 702 | |
| 703 a->Bind(&if_isnotunmodifiedjsregexp); | |
| 704 { | |
| 705 Node* const native_context = a->LoadNativeContext(context); | |
| 706 Node* const regexp_fun = | |
| 707 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | |
| 708 Node* const initial_map = a->LoadObjectField( | |
| 709 regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | |
| 710 Node* const initial_prototype = a->LoadMapPrototype(initial_map); | |
| 711 | |
| 712 Label if_isprototype(a), if_isnotprototype(a); | |
| 713 a->Branch(a->WordEqual(receiver, initial_prototype), &if_isprototype, | |
| 714 &if_isnotprototype); | |
| 715 | |
| 716 a->Bind(&if_isprototype); | |
| 717 { | |
| 718 Node* const counter_smi = a->SmiConstant(Smi::FromInt(counter)); | |
| 719 a->CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); | |
| 720 a->Return(a->UndefinedConstant()); | |
| 721 } | |
| 722 | |
| 723 a->Bind(&if_isnotprototype); | |
| 724 { | |
| 725 Node* const message_id = | |
| 726 a->SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp)); | |
| 727 Node* const method_name_str = a->HeapConstant( | |
| 728 isolate->factory()->NewStringFromAsciiChecked(method_name)); | |
| 729 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, | |
| 730 method_name_str); | |
| 731 a->Return(a->UndefinedConstant()); // Never reached. | |
| 732 } | |
| 733 } | |
| 734 } | |
| 735 | |
| 736 } // namespace | |
| 737 | |
| 738 // ES6 21.2.5.4. | |
| 739 void Builtins::Generate_RegExpPrototypeGlobalGetter(CodeStubAssembler* a) { | |
| 740 Generate_FlagGetter(a, JSRegExp::kGlobal, | |
| 741 v8::Isolate::kRegExpPrototypeOldFlagGetter, | |
| 742 "RegExp.prototype.global"); | |
| 743 } | |
| 744 | |
| 745 // ES6 21.2.5.5. | |
| 746 void Builtins::Generate_RegExpPrototypeIgnoreCaseGetter(CodeStubAssembler* a) { | |
| 747 Generate_FlagGetter(a, JSRegExp::kIgnoreCase, | |
| 748 v8::Isolate::kRegExpPrototypeOldFlagGetter, | |
| 749 "RegExp.prototype.ignoreCase"); | |
| 750 } | |
| 751 | |
| 752 // ES6 21.2.5.7. | |
| 753 void Builtins::Generate_RegExpPrototypeMultilineGetter(CodeStubAssembler* a) { | |
| 754 Generate_FlagGetter(a, JSRegExp::kMultiline, | |
| 755 v8::Isolate::kRegExpPrototypeOldFlagGetter, | |
| 756 "RegExp.prototype.multiline"); | |
| 757 } | |
| 758 | |
| 759 // ES6 21.2.5.12. | |
| 760 void Builtins::Generate_RegExpPrototypeStickyGetter(CodeStubAssembler* a) { | |
| 761 Generate_FlagGetter(a, JSRegExp::kSticky, | |
| 762 v8::Isolate::kRegExpPrototypeStickyGetter, | |
| 763 "RegExp.prototype.sticky"); | |
| 764 } | |
| 765 | |
| 766 // ES6 21.2.5.15. | |
| 767 void Builtins::Generate_RegExpPrototypeUnicodeGetter(CodeStubAssembler* a) { | |
| 768 Generate_FlagGetter(a, JSRegExp::kUnicode, | |
| 769 v8::Isolate::kRegExpPrototypeUnicodeGetter, | |
| 770 "RegExp.prototype.unicode"); | |
| 771 } | |
| 772 | |
| 773 namespace { | |
| 774 | |
| 775 // Constants for accessing RegExpLastMatchInfo. | |
| 776 // TODO(jgruber): Currently, RegExpLastMatchInfo is still a JSObject maintained | |
| 777 // and accessed from JS. This is a crutch until all RegExp logic is ported, then | |
| 778 // we can take care of RegExpLastMatchInfo. | |
| 779 | |
| 780 Handle<Object> GetLastMatchField(Isolate* isolate, int index) { | |
| 781 Handle<JSObject> last_match_info = isolate->regexp_last_match_info(); | |
| 782 return JSReceiver::GetElement(isolate, last_match_info, index) | |
| 783 .ToHandleChecked(); | |
| 784 } | |
| 785 | |
| 786 void SetLastMatchField(Isolate* isolate, int index, Handle<Object> value) { | |
| 787 Handle<JSObject> last_match_info = isolate->regexp_last_match_info(); | |
| 788 JSReceiver::SetElement(isolate, last_match_info, index, value, SLOPPY) | |
| 789 .ToHandleChecked(); | |
| 790 } | |
| 791 | |
| 792 int GetLastMatchNumberOfCaptures(Isolate* isolate) { | |
| 793 Handle<Object> obj = | |
| 794 GetLastMatchField(isolate, RegExpImpl::kLastCaptureCount); | |
| 795 return Handle<Smi>::cast(obj)->value(); | |
| 796 } | |
| 797 | |
| 798 Handle<String> GetLastMatchSubject(Isolate* isolate) { | |
| 799 return Handle<String>::cast( | |
| 800 GetLastMatchField(isolate, RegExpImpl::kLastSubject)); | |
| 801 } | |
| 802 | |
| 803 Handle<Object> GetLastMatchInput(Isolate* isolate) { | |
| 804 return GetLastMatchField(isolate, RegExpImpl::kLastInput); | |
| 805 } | |
| 806 | |
| 807 int GetLastMatchCapture(Isolate* isolate, int i) { | |
| 808 Handle<Object> obj = | |
| 809 GetLastMatchField(isolate, RegExpImpl::kFirstCapture + i); | |
| 810 return Handle<Smi>::cast(obj)->value(); | |
| 811 } | |
| 812 | |
| 813 Object* GenericCaptureGetter(Isolate* isolate, int capture) { | |
| 814 HandleScope scope(isolate); | |
| 815 const int index = capture * 2; | |
| 816 if (index >= GetLastMatchNumberOfCaptures(isolate)) { | |
| 817 return isolate->heap()->empty_string(); | |
| 818 } | |
| 819 | |
| 820 const int match_start = GetLastMatchCapture(isolate, index); | |
| 821 const int match_end = GetLastMatchCapture(isolate, index + 1); | |
| 822 if (match_start == -1 || match_end == -1) { | |
| 823 return isolate->heap()->empty_string(); | |
| 824 } | |
| 825 | |
| 826 Handle<String> last_subject = GetLastMatchSubject(isolate); | |
| 827 return *isolate->factory()->NewSubString(last_subject, match_start, | |
| 828 match_end); | |
| 829 } | |
| 830 | |
| 831 } // namespace | |
| 832 | |
| 833 // The properties $1..$9 are the first nine capturing substrings of the last | |
| 834 // successful match, or ''. The function RegExpMakeCaptureGetter will be | |
| 835 // called with indices from 1 to 9. | |
| 836 #define DEFINE_CAPTURE_GETTER(i) \ | |
| 837 BUILTIN(RegExpCapture##i##Getter) { \ | |
| 838 HandleScope scope(isolate); \ | |
| 839 return GenericCaptureGetter(isolate, i); \ | |
| 840 } | |
| 841 DEFINE_CAPTURE_GETTER(1) | |
| 842 DEFINE_CAPTURE_GETTER(2) | |
| 843 DEFINE_CAPTURE_GETTER(3) | |
| 844 DEFINE_CAPTURE_GETTER(4) | |
| 845 DEFINE_CAPTURE_GETTER(5) | |
| 846 DEFINE_CAPTURE_GETTER(6) | |
| 847 DEFINE_CAPTURE_GETTER(7) | |
| 848 DEFINE_CAPTURE_GETTER(8) | |
| 849 DEFINE_CAPTURE_GETTER(9) | |
| 850 #undef DEFINE_CAPTURE_GETTER | |
| 851 | |
| 852 // The properties `input` and `$_` are aliases for each other. When this | |
| 853 // value is set, the value it is set to is coerced to a string. | |
| 854 // Getter and setter for the input. | |
| 855 | |
| 856 BUILTIN(RegExpInputGetter) { | |
| 857 HandleScope scope(isolate); | |
| 858 Handle<Object> obj = GetLastMatchInput(isolate); | |
| 859 return obj->IsUndefined(isolate) ? isolate->heap()->empty_string() | |
| 860 : String::cast(*obj); | |
| 861 } | |
| 862 | |
| 863 BUILTIN(RegExpInputSetter) { | |
| 864 HandleScope scope(isolate); | |
| 865 Handle<Object> value = args.atOrUndefined(isolate, 1); | |
| 866 Handle<String> str; | |
| 867 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str, | |
| 868 Object::ToString(isolate, value)); | |
| 869 SetLastMatchField(isolate, RegExpImpl::kLastInput, str); | |
| 870 return isolate->heap()->undefined_value(); | |
| 871 } | |
| 872 | |
| 873 // Getters for the static properties lastMatch, lastParen, leftContext, and | |
| 874 // rightContext of the RegExp constructor. The properties are computed based | |
| 875 // on the captures array of the last successful match and the subject string | |
| 876 // of the last successful match. | |
| 877 BUILTIN(RegExpLastMatchGetter) { | |
| 878 HandleScope scope(isolate); | |
| 879 return GenericCaptureGetter(isolate, 0); | |
| 880 } | |
| 881 | |
| 882 BUILTIN(RegExpLastParenGetter) { | |
| 883 HandleScope scope(isolate); | |
| 884 const int length = GetLastMatchNumberOfCaptures(isolate); | |
| 885 if (length <= 2) return isolate->heap()->empty_string(); // No captures. | |
| 886 | |
| 887 DCHECK_EQ(0, length % 2); | |
| 888 const int last_capture = (length / 2) - 1; | |
| 889 | |
| 890 // We match the SpiderMonkey behavior: return the substring defined by the | |
| 891 // last pair (after the first pair) of elements of the capture array even if | |
| 892 // it is empty. | |
| 893 return GenericCaptureGetter(isolate, last_capture); | |
| 894 } | |
| 895 | |
| 896 BUILTIN(RegExpLeftContextGetter) { | |
| 897 HandleScope scope(isolate); | |
| 898 const int start_index = GetLastMatchCapture(isolate, 0); | |
| 899 Handle<String> last_subject = GetLastMatchSubject(isolate); | |
| 900 return *isolate->factory()->NewSubString(last_subject, 0, start_index); | |
| 901 } | |
| 902 | |
| 903 BUILTIN(RegExpRightContextGetter) { | |
| 904 HandleScope scope(isolate); | |
| 905 const int start_index = GetLastMatchCapture(isolate, 1); | |
| 906 Handle<String> last_subject = GetLastMatchSubject(isolate); | |
| 907 const int len = last_subject->length(); | |
| 908 return *isolate->factory()->NewSubString(last_subject, start_index, len); | |
| 909 } | |
| 910 | |
| 440 } // namespace internal | 911 } // namespace internal |
| 441 } // namespace v8 | 912 } // namespace v8 |
| OLD | NEW |