Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: src/builtins/builtins-regexp.cc

Issue 2389233002: [regexp] Port RegExp getters and setters (Closed)
Patch Set: Handle Smi receivers Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
OLDNEW
« src/bootstrapper.cc ('K') | « src/builtins/builtins.h ('k') | src/code-stub-assembler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698