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 |