OLD | NEW |
1 // Copyright 2017 the V8 project authors. All rights reserved. | 1 // Copyright 2017 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-regexp-gen.h" | 5 #include "src/builtins/builtins-regexp-gen.h" |
6 #include "src/builtins/builtins-utils-gen.h" | 6 #include "src/builtins/builtins-utils-gen.h" |
7 #include "src/builtins/builtins.h" | 7 #include "src/builtins/builtins.h" |
8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
9 #include "src/code-stub-assembler.h" | 9 #include "src/code-stub-assembler.h" |
10 #include "src/objects.h" | 10 #include "src/objects.h" |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
52 Int32Constant(kShortExternalStringTag))); | 52 Int32Constant(kShortExternalStringTag))); |
53 var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset, | 53 var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset, |
54 MachineType::Pointer())); | 54 MachineType::Pointer())); |
55 Goto(&if_join); | 55 Goto(&if_join); |
56 } | 56 } |
57 | 57 |
58 BIND(&if_join); | 58 BIND(&if_join); |
59 return var_data.value(); | 59 return var_data.value(); |
60 } | 60 } |
61 | 61 |
62 Node* LoadOneByteChar(Node* string, Node* index) { | 62 void DispatchOnStringEncodings(Node* const lhs_instance_type, |
63 return Load(MachineType::Uint8(), string, OneByteCharOffset(index)); | 63 Node* const rhs_instance_type, |
| 64 Label* if_one_one, Label* if_one_two, |
| 65 Label* if_two_one, Label* if_two_two) { |
| 66 STATIC_ASSERT(kStringEncodingMask == 0x8); |
| 67 STATIC_ASSERT(kTwoByteStringTag == 0x0); |
| 68 STATIC_ASSERT(kOneByteStringTag == 0x8); |
| 69 |
| 70 // First combine the encodings. |
| 71 |
| 72 Node* const encoding_mask = Int32Constant(kStringEncodingMask); |
| 73 Node* const lhs_encoding = Word32And(lhs_instance_type, encoding_mask); |
| 74 Node* const rhs_encoding = Word32And(rhs_instance_type, encoding_mask); |
| 75 |
| 76 Node* const combined_encodings = |
| 77 Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1)); |
| 78 |
| 79 // Then dispatch on the combined encoding. |
| 80 |
| 81 Label unreachable(this, Label::kDeferred); |
| 82 |
| 83 int32_t values[] = { |
| 84 kOneByteStringTag | (kOneByteStringTag >> 1), |
| 85 kOneByteStringTag | (kTwoByteStringTag >> 1), |
| 86 kTwoByteStringTag | (kOneByteStringTag >> 1), |
| 87 kTwoByteStringTag | (kTwoByteStringTag >> 1), |
| 88 }; |
| 89 Label* labels[] = { |
| 90 if_one_one, if_one_two, if_two_one, if_two_two, |
| 91 }; |
| 92 |
| 93 STATIC_ASSERT(arraysize(values) == arraysize(labels)); |
| 94 Switch(combined_encodings, &unreachable, values, labels, arraysize(values)); |
| 95 |
| 96 BIND(&unreachable); |
| 97 Unreachable(); |
64 } | 98 } |
65 | 99 |
66 Node* OneByteCharAddress(Node* string, Node* index) { | 100 template <typename SubjectChar, typename PatternChar> |
67 Node* offset = OneByteCharOffset(index); | 101 Node* CallSearchStringRaw(Node* const subject_ptr, Node* const subject_length, |
68 return IntPtrAdd(string, offset); | 102 Node* const search_ptr, Node* const search_length, |
| 103 Node* const start_position) { |
| 104 Node* const function_addr = ExternalConstant( |
| 105 ExternalReference::search_string_raw<SubjectChar, PatternChar>( |
| 106 isolate())); |
| 107 Node* const isolate_ptr = |
| 108 ExternalConstant(ExternalReference::isolate_address(isolate())); |
| 109 |
| 110 MachineType type_ptr = MachineType::Pointer(); |
| 111 MachineType type_intptr = MachineType::IntPtr(); |
| 112 |
| 113 Node* const result = CallCFunction6( |
| 114 type_intptr, type_ptr, type_ptr, type_intptr, type_ptr, type_intptr, |
| 115 type_intptr, function_addr, isolate_ptr, subject_ptr, subject_length, |
| 116 search_ptr, search_length, start_position); |
| 117 |
| 118 return result; |
69 } | 119 } |
70 | 120 |
71 Node* OneByteCharOffset(Node* index) { | 121 Node* PointerToStringDataAtIndex(Node* const string_data, Node* const index, |
72 return CharOffset(String::ONE_BYTE_ENCODING, index); | 122 String::Encoding encoding) { |
73 } | 123 const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING) |
| 124 ? UINT8_ELEMENTS |
| 125 : UINT16_ELEMENTS; |
74 | 126 |
75 Node* CharOffset(String::Encoding encoding, Node* index) { | 127 Node* const offset_in_bytes = |
76 const int header = SeqOneByteString::kHeaderSize - kHeapObjectTag; | 128 ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS); |
77 Node* offset = index; | 129 return IntPtrAdd(string_data, offset_in_bytes); |
78 if (encoding == String::TWO_BYTE_ENCODING) { | |
79 offset = IntPtrAdd(offset, offset); | |
80 } | |
81 offset = IntPtrAdd(offset, IntPtrConstant(header)); | |
82 return offset; | |
83 } | |
84 | |
85 void DispatchOnStringInstanceType(Node* const instance_type, | |
86 Label* if_onebyte_sequential, | |
87 Label* if_onebyte_external, | |
88 Label* if_otherwise) { | |
89 const int kMask = kStringRepresentationMask | kStringEncodingMask; | |
90 Node* const encoding_and_representation = | |
91 Word32And(instance_type, Int32Constant(kMask)); | |
92 | |
93 int32_t values[] = { | |
94 kOneByteStringTag | kSeqStringTag, | |
95 kOneByteStringTag | kExternalStringTag, | |
96 }; | |
97 Label* labels[] = { | |
98 if_onebyte_sequential, if_onebyte_external, | |
99 }; | |
100 STATIC_ASSERT(arraysize(values) == arraysize(labels)); | |
101 | |
102 Switch(encoding_and_representation, if_otherwise, values, labels, | |
103 arraysize(values)); | |
104 } | 130 } |
105 | 131 |
106 void GenerateStringEqual(Node* context, Node* left, Node* right); | 132 void GenerateStringEqual(Node* context, Node* left, Node* right); |
107 void GenerateStringRelationalComparison(Node* context, Node* left, | 133 void GenerateStringRelationalComparison(Node* context, Node* left, |
108 Node* right, | 134 Node* right, |
109 RelationalComparisonMode mode); | 135 RelationalComparisonMode mode); |
110 | 136 |
111 Node* ToSmiBetweenZeroAnd(Node* context, Node* value, Node* limit); | 137 Node* ToSmiBetweenZeroAnd(Node* context, Node* value, Node* limit); |
112 | 138 |
113 Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index, | 139 Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index, |
114 UnicodeEncoding encoding); | 140 UnicodeEncoding encoding); |
115 | 141 |
116 void StringIndexOf(Node* receiver, Node* instance_type, Node* search_string, | 142 void StringIndexOf(Node* const subject_string, |
117 Node* search_string_instance_type, Node* position, | 143 Node* const subject_instance_type, |
| 144 Node* const search_string, |
| 145 Node* const search_instance_type, Node* const position, |
118 std::function<void(Node*)> f_return); | 146 std::function<void(Node*)> f_return); |
119 | 147 |
120 Node* IndexOfDollarChar(Node* const context, Node* const string); | 148 Node* IndexOfDollarChar(Node* const context, Node* const string); |
121 | 149 |
122 Node* IsNullOrUndefined(Node* const value); | 150 Node* IsNullOrUndefined(Node* const value); |
123 void RequireObjectCoercible(Node* const context, Node* const value, | 151 void RequireObjectCoercible(Node* const context, Node* const value, |
124 const char* method_name); | 152 const char* method_name); |
125 | 153 |
126 Node* SmiIsNegative(Node* const value) { | 154 Node* SmiIsNegative(Node* const value) { |
127 return SmiLessThan(value, SmiConstant(0)); | 155 return SmiLessThan(value, SmiConstant(0)); |
(...skipping 577 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
705 CodeStubAssembler::VariableList({&var_result}, zone()), | 733 CodeStubAssembler::VariableList({&var_result}, zone()), |
706 [this, context, &var_result](Node* arg) { | 734 [this, context, &var_result](Node* arg) { |
707 arg = CallStub(CodeFactory::ToString(isolate()), context, arg); | 735 arg = CallStub(CodeFactory::ToString(isolate()), context, arg); |
708 var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context, | 736 var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context, |
709 var_result.value(), arg)); | 737 var_result.value(), arg)); |
710 }); | 738 }); |
711 arguments.PopAndReturn(var_result.value()); | 739 arguments.PopAndReturn(var_result.value()); |
712 } | 740 } |
713 | 741 |
714 void StringBuiltinsAssembler::StringIndexOf( | 742 void StringBuiltinsAssembler::StringIndexOf( |
715 Node* receiver, Node* instance_type, Node* search_string, | 743 Node* const subject_string, Node* const subject_instance_type, |
716 Node* search_string_instance_type, Node* position, | 744 Node* const search_string, Node* const search_instance_type, |
717 std::function<void(Node*)> f_return) { | 745 Node* const position, std::function<void(Node*)> f_return) { |
718 CSA_ASSERT(this, IsString(receiver)); | 746 CSA_ASSERT(this, IsString(subject_string)); |
719 CSA_ASSERT(this, IsString(search_string)); | 747 CSA_ASSERT(this, IsString(search_string)); |
720 CSA_ASSERT(this, TaggedIsSmi(position)); | 748 CSA_ASSERT(this, TaggedIsSmi(position)); |
721 | 749 |
722 Label zero_length_needle(this), | 750 Node* const int_zero = IntPtrConstant(0); |
723 call_runtime_unchecked(this, Label::kDeferred), return_minus_1(this), | |
724 check_search_string(this), continue_fast_path(this); | |
725 | 751 |
726 Node* const int_zero = IntPtrConstant(0); | |
727 VARIABLE(var_needle_byte, MachineType::PointerRepresentation(), int_zero); | 752 VARIABLE(var_needle_byte, MachineType::PointerRepresentation(), int_zero); |
728 VARIABLE(var_string_addr, MachineType::PointerRepresentation(), int_zero); | 753 VARIABLE(var_string_addr, MachineType::PointerRepresentation(), int_zero); |
729 | 754 |
730 Node* needle_length = SmiUntag(LoadStringLength(search_string)); | 755 Node* const search_length = SmiUntag(LoadStringLength(search_string)); |
731 // Use faster/complex runtime fallback for long search strings. | 756 Node* const subject_length = SmiUntag(LoadStringLength(subject_string)); |
732 GotoIf(IntPtrLessThan(IntPtrConstant(1), needle_length), | 757 Node* const start_position = IntPtrMax(SmiUntag(position), int_zero); |
733 &call_runtime_unchecked); | |
734 Node* string_length = SmiUntag(LoadStringLength(receiver)); | |
735 Node* start_position = IntPtrMax(SmiUntag(position), int_zero); | |
736 | 758 |
737 GotoIf(IntPtrEqual(int_zero, needle_length), &zero_length_needle); | 759 Label zero_length_needle(this), return_minus_1(this); |
738 // Check that the needle fits in the start position. | 760 { |
739 GotoIfNot(IntPtrLessThanOrEqual(needle_length, | 761 GotoIf(IntPtrEqual(int_zero, search_length), &zero_length_needle); |
740 IntPtrSub(string_length, start_position)), | |
741 &return_minus_1); | |
742 | 762 |
743 // Load the string address. | 763 // Check that the needle fits in the start position. |
| 764 GotoIfNot(IntPtrLessThanOrEqual(search_length, |
| 765 IntPtrSub(subject_length, start_position)), |
| 766 &return_minus_1); |
| 767 } |
| 768 |
| 769 // Try to unpack subject and search strings. Bail to runtime if either needs |
| 770 // to be flattened. |
| 771 ToDirectStringAssembler subject_to_direct(state(), subject_string); |
| 772 ToDirectStringAssembler search_to_direct(state(), search_string); |
| 773 |
| 774 Label call_runtime_unchecked(this, Label::kDeferred); |
| 775 |
| 776 subject_to_direct.TryToDirect(&call_runtime_unchecked); |
| 777 search_to_direct.TryToDirect(&call_runtime_unchecked); |
| 778 |
| 779 // Load pointers to string data. |
| 780 Node* const subject_ptr = |
| 781 subject_to_direct.PointerToData(&call_runtime_unchecked); |
| 782 Node* const search_ptr = |
| 783 search_to_direct.PointerToData(&call_runtime_unchecked); |
| 784 |
| 785 Node* const subject_offset = subject_to_direct.offset(); |
| 786 Node* const search_offset = search_to_direct.offset(); |
| 787 |
| 788 // Like String::IndexOf, the actual matching is done by the optimized |
| 789 // SearchString method in string-search.h. Dispatch based on string instance |
| 790 // types, then call straight into C++ for matching. |
| 791 |
| 792 CSA_ASSERT(this, IntPtrGreaterThan(search_length, int_zero)); |
| 793 CSA_ASSERT(this, IntPtrGreaterThanOrEqual(start_position, int_zero)); |
| 794 CSA_ASSERT(this, IntPtrGreaterThanOrEqual(subject_length, start_position)); |
| 795 CSA_ASSERT(this, |
| 796 IntPtrLessThanOrEqual(search_length, |
| 797 IntPtrSub(subject_length, start_position))); |
| 798 |
| 799 Label one_one(this), one_two(this), two_one(this), two_two(this); |
| 800 DispatchOnStringEncodings(subject_to_direct.instance_type(), |
| 801 search_to_direct.instance_type(), &one_one, |
| 802 &one_two, &two_one, &two_two); |
| 803 |
| 804 typedef const uint8_t onebyte_t; |
| 805 typedef const uc16 twobyte_t; |
| 806 |
| 807 BIND(&one_one); |
744 { | 808 { |
745 Label if_onebyte_sequential(this); | 809 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex( |
746 Label if_onebyte_external(this, Label::kDeferred); | 810 subject_ptr, subject_offset, String::ONE_BYTE_ENCODING); |
| 811 Node* const adjusted_search_ptr = PointerToStringDataAtIndex( |
| 812 search_ptr, search_offset, String::ONE_BYTE_ENCODING); |
747 | 813 |
748 // Only support one-byte strings on the fast path. | 814 Label direct_memchr_call(this), generic_fast_path(this); |
749 DispatchOnStringInstanceType(instance_type, &if_onebyte_sequential, | 815 Branch(IntPtrEqual(search_length, IntPtrConstant(1)), &direct_memchr_call, |
750 &if_onebyte_external, &call_runtime_unchecked); | 816 &generic_fast_path); |
751 | 817 |
752 BIND(&if_onebyte_sequential); | 818 // An additional fast path that calls directly into memchr for 1-length |
| 819 // search strings. |
| 820 BIND(&direct_memchr_call); |
753 { | 821 { |
754 var_string_addr.Bind( | 822 Node* const string_addr = IntPtrAdd(adjusted_subject_ptr, start_position); |
755 OneByteCharAddress(BitcastTaggedToWord(receiver), start_position)); | 823 Node* const search_length = IntPtrSub(subject_length, start_position); |
756 Goto(&check_search_string); | 824 Node* const search_byte = |
| 825 ChangeInt32ToIntPtr(Load(MachineType::Uint8(), adjusted_search_ptr)); |
| 826 |
| 827 Node* const memchr = |
| 828 ExternalConstant(ExternalReference::libc_memchr_function(isolate())); |
| 829 Node* const result_address = |
| 830 CallCFunction3(MachineType::Pointer(), MachineType::Pointer(), |
| 831 MachineType::IntPtr(), MachineType::UintPtr(), memchr, |
| 832 string_addr, search_byte, search_length); |
| 833 GotoIf(WordEqual(result_address, int_zero), &return_minus_1); |
| 834 Node* const result_index = |
| 835 IntPtrAdd(IntPtrSub(result_address, string_addr), start_position); |
| 836 f_return(SmiTag(result_index)); |
757 } | 837 } |
758 | 838 |
759 BIND(&if_onebyte_external); | 839 BIND(&generic_fast_path); |
760 { | 840 { |
761 Node* const unpacked = TryDerefExternalString(receiver, instance_type, | 841 Node* const result = CallSearchStringRaw<onebyte_t, onebyte_t>( |
762 &call_runtime_unchecked); | 842 adjusted_subject_ptr, subject_length, adjusted_search_ptr, |
763 var_string_addr.Bind(OneByteCharAddress(unpacked, start_position)); | 843 search_length, start_position); |
764 Goto(&check_search_string); | 844 f_return(SmiTag(result)); |
765 } | 845 } |
766 } | 846 } |
767 | 847 |
768 // Load the needle character. | 848 BIND(&one_two); |
769 BIND(&check_search_string); | |
770 { | 849 { |
771 Label if_onebyte_sequential(this); | 850 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex( |
772 Label if_onebyte_external(this, Label::kDeferred); | 851 subject_ptr, subject_offset, String::ONE_BYTE_ENCODING); |
| 852 Node* const adjusted_search_ptr = PointerToStringDataAtIndex( |
| 853 search_ptr, search_offset, String::TWO_BYTE_ENCODING); |
773 | 854 |
774 DispatchOnStringInstanceType(search_string_instance_type, | 855 Node* const result = CallSearchStringRaw<onebyte_t, twobyte_t>( |
775 &if_onebyte_sequential, &if_onebyte_external, | 856 adjusted_subject_ptr, subject_length, adjusted_search_ptr, |
776 &call_runtime_unchecked); | 857 search_length, start_position); |
777 | 858 f_return(SmiTag(result)); |
778 BIND(&if_onebyte_sequential); | |
779 { | |
780 var_needle_byte.Bind( | |
781 ChangeInt32ToIntPtr(LoadOneByteChar(search_string, int_zero))); | |
782 Goto(&continue_fast_path); | |
783 } | |
784 | |
785 BIND(&if_onebyte_external); | |
786 { | |
787 Node* const unpacked = TryDerefExternalString( | |
788 search_string, search_string_instance_type, &call_runtime_unchecked); | |
789 var_needle_byte.Bind( | |
790 ChangeInt32ToIntPtr(LoadOneByteChar(unpacked, int_zero))); | |
791 Goto(&continue_fast_path); | |
792 } | |
793 } | 859 } |
794 | 860 |
795 BIND(&continue_fast_path); | 861 BIND(&two_one); |
796 { | 862 { |
797 Node* needle_byte = var_needle_byte.value(); | 863 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex( |
798 Node* string_addr = var_string_addr.value(); | 864 subject_ptr, subject_offset, String::TWO_BYTE_ENCODING); |
799 Node* search_length = IntPtrSub(string_length, start_position); | 865 Node* const adjusted_search_ptr = PointerToStringDataAtIndex( |
800 // Call out to the highly optimized memchr to perform the actual byte | 866 search_ptr, search_offset, String::ONE_BYTE_ENCODING); |
801 // search. | 867 |
802 Node* memchr = | 868 Node* const result = CallSearchStringRaw<twobyte_t, onebyte_t>( |
803 ExternalConstant(ExternalReference::libc_memchr_function(isolate())); | 869 adjusted_subject_ptr, subject_length, adjusted_search_ptr, |
804 Node* result_address = | 870 search_length, start_position); |
805 CallCFunction3(MachineType::Pointer(), MachineType::Pointer(), | 871 f_return(SmiTag(result)); |
806 MachineType::IntPtr(), MachineType::UintPtr(), memchr, | 872 } |
807 string_addr, needle_byte, search_length); | 873 |
808 GotoIf(WordEqual(result_address, int_zero), &return_minus_1); | 874 BIND(&two_two); |
809 Node* result_index = | 875 { |
810 IntPtrAdd(IntPtrSub(result_address, string_addr), start_position); | 876 Node* const adjusted_subject_ptr = PointerToStringDataAtIndex( |
811 f_return(SmiTag(result_index)); | 877 subject_ptr, subject_offset, String::TWO_BYTE_ENCODING); |
| 878 Node* const adjusted_search_ptr = PointerToStringDataAtIndex( |
| 879 search_ptr, search_offset, String::TWO_BYTE_ENCODING); |
| 880 |
| 881 Node* const result = CallSearchStringRaw<twobyte_t, twobyte_t>( |
| 882 adjusted_subject_ptr, subject_length, adjusted_search_ptr, |
| 883 search_length, start_position); |
| 884 f_return(SmiTag(result)); |
812 } | 885 } |
813 | 886 |
814 BIND(&return_minus_1); | 887 BIND(&return_minus_1); |
815 f_return(SmiConstant(-1)); | 888 f_return(SmiConstant(-1)); |
816 | 889 |
817 BIND(&zero_length_needle); | 890 BIND(&zero_length_needle); |
818 { | 891 { |
819 Comment("0-length search_string"); | 892 Comment("0-length search_string"); |
820 f_return(SmiTag(IntPtrMin(string_length, start_position))); | 893 f_return(SmiTag(IntPtrMin(subject_length, start_position))); |
821 } | 894 } |
822 | 895 |
823 BIND(&call_runtime_unchecked); | 896 BIND(&call_runtime_unchecked); |
824 { | 897 { |
825 // Simplified version of the runtime call where the types of the arguments | 898 // Simplified version of the runtime call where the types of the arguments |
826 // are already known due to type checks in this stub. | 899 // are already known due to type checks in this stub. |
827 Comment("Call Runtime Unchecked"); | 900 Comment("Call Runtime Unchecked"); |
828 Node* result = CallRuntime(Runtime::kStringIndexOfUnchecked, SmiConstant(0), | 901 Node* result = CallRuntime(Runtime::kStringIndexOfUnchecked, SmiConstant(0), |
829 receiver, search_string, position); | 902 subject_string, search_string, position); |
830 f_return(result); | 903 f_return(result); |
831 } | 904 } |
832 } | 905 } |
833 | 906 |
834 // ES6 String.prototype.indexOf(searchString [, position]) | 907 // ES6 String.prototype.indexOf(searchString [, position]) |
835 // #sec-string.prototype.indexof | 908 // #sec-string.prototype.indexof |
836 // Unchecked helper for builtins lowering. | 909 // Unchecked helper for builtins lowering. |
837 TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) { | 910 TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) { |
838 Node* receiver = Parameter(Descriptor::kReceiver); | 911 Node* receiver = Parameter(Descriptor::kReceiver); |
839 Node* search_string = Parameter(Descriptor::kSearchString); | 912 Node* search_string = Parameter(Descriptor::kSearchString); |
(...skipping 866 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1706 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, | 1779 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, |
1707 HeapConstant(factory()->NewStringFromAsciiChecked( | 1780 HeapConstant(factory()->NewStringFromAsciiChecked( |
1708 "String Iterator.prototype.next", TENURED)), | 1781 "String Iterator.prototype.next", TENURED)), |
1709 iterator); | 1782 iterator); |
1710 Unreachable(); | 1783 Unreachable(); |
1711 } | 1784 } |
1712 } | 1785 } |
1713 | 1786 |
1714 } // namespace internal | 1787 } // namespace internal |
1715 } // namespace v8 | 1788 } // namespace v8 |
OLD | NEW |