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 #include "src/regexp/regexp-utils.h" | 10 #include "src/regexp/regexp-utils.h" |
11 #include "src/string-builder.h" | 11 #include "src/string-builder.h" |
12 | 12 |
13 namespace v8 { | 13 namespace v8 { |
14 namespace internal { | 14 namespace internal { |
15 | 15 |
16 typedef compiler::Node Node; | 16 typedef compiler::Node Node; |
17 typedef CodeStubAssembler::Label CLabel; | |
18 typedef CodeStubAssembler::Variable CVariable; | |
19 typedef CodeStubAssembler::ParameterMode ParameterMode; | 17 typedef CodeStubAssembler::ParameterMode ParameterMode; |
20 typedef compiler::CodeAssemblerState CodeAssemblerState; | 18 typedef compiler::CodeAssemblerState CodeAssemblerState; |
21 | 19 |
22 class RegExpBuiltinsAssembler : public CodeStubAssembler { | 20 class RegExpBuiltinsAssembler : public CodeStubAssembler { |
23 public: | 21 public: |
24 explicit RegExpBuiltinsAssembler(compiler::CodeAssemblerState* state) | 22 explicit RegExpBuiltinsAssembler(CodeAssemblerState* state) |
25 : CodeStubAssembler(state) {} | 23 : CodeStubAssembler(state) {} |
26 | 24 |
27 protected: | 25 protected: |
26 Node* FastLoadLastIndex(Node* regexp); | |
27 Node* SlowLoadLastIndex(Node* context, Node* regexp); | |
28 Node* LoadLastIndex(Node* context, Node* regexp, bool is_fastpath); | |
29 | |
30 void FastStoreLastIndex(Node* regexp, Node* value); | |
31 void SlowStoreLastIndex(Node* context, Node* regexp, Node* value); | |
32 void StoreLastIndex(Node* context, Node* regexp, Node* value, | |
33 bool is_fastpath); | |
34 | |
35 Node* ConstructNewResultFromMatchInfo(Node* context, Node* match_info, | |
36 Node* string); | |
37 | |
38 Node* RegExpPrototypeExecBodyWithoutResult(Node* const context, | |
39 Node* const regexp, | |
40 Node* const string, | |
41 Label* if_didnotmatch, | |
42 const bool is_fastpath); | |
43 Node* RegExpPrototypeExecBody(Node* const context, Node* const regexp, | |
44 Node* const string, const bool is_fastpath); | |
45 | |
46 Node* ThrowIfNotJSReceiver(Node* context, Node* maybe_receiver, | |
47 MessageTemplate::Template msg_template, | |
48 char const* method_name); | |
49 | |
50 Node* IsInitialRegExpMap(Node* context, Node* map); | |
51 void BranchIfFastRegExp(Node* context, Node* map, Label* if_isunmodified, | |
52 Label* if_ismodified); | |
53 void BranchIfFastRegExpResult(Node* context, Node* map, | |
54 Label* if_isunmodified, Label* if_ismodified); | |
55 | |
56 Node* FlagsGetter(Node* const context, Node* const regexp, bool is_fastpath); | |
57 | |
58 Node* FastFlagGetter(Node* const regexp, JSRegExp::Flag flag); | |
59 Node* SlowFlagGetter(Node* const context, Node* const regexp, | |
60 JSRegExp::Flag flag); | |
61 Node* FlagGetter(Node* const context, Node* const regexp, JSRegExp::Flag flag, | |
62 bool is_fastpath); | |
63 void FlagGetter(JSRegExp::Flag flag, v8::Isolate::UseCounterFeature counter, | |
64 const char* method_name); | |
65 | |
66 Node* IsRegExp(Node* const context, Node* const maybe_receiver); | |
67 Node* RegExpInitialize(Node* const context, Node* const regexp, | |
68 Node* const maybe_pattern, Node* const maybe_flags); | |
69 | |
70 Node* RegExpExec(Node* context, Node* regexp, Node* string); | |
71 | |
72 Node* AdvanceStringIndex(Node* const string, Node* const index, | |
73 Node* const is_unicode); | |
74 | |
75 void RegExpPrototypeMatchBody(Node* const context, Node* const regexp, | |
76 Node* const string, const bool is_fastpath); | |
77 | |
78 void RegExpPrototypeSearchBodyFast(Node* const context, Node* const regexp, | |
79 Node* const string); | |
80 void RegExpPrototypeSearchBodySlow(Node* const context, Node* const regexp, | |
81 Node* const string); | |
82 | |
83 void RegExpPrototypeSplitBody(Node* const context, Node* const regexp, | |
84 Node* const string, Node* const limit); | |
85 | |
86 Node* ReplaceGlobalCallableFastPath(Node* context, Node* regexp, Node* string, | |
87 Node* replace_callable); | |
88 Node* ReplaceSimpleStringFastPath(Node* context, Node* regexp, Node* string, | |
89 Node* replace_string); | |
28 }; | 90 }; |
29 | 91 |
30 // ----------------------------------------------------------------------------- | 92 // ----------------------------------------------------------------------------- |
31 // ES6 section 21.2 RegExp Objects | 93 // ES6 section 21.2 RegExp Objects |
32 | 94 |
33 namespace { | 95 Node* RegExpBuiltinsAssembler::FastLoadLastIndex(Node* regexp) { |
Camillo Bruni
2016/12/05 09:52:10
I'd directly define them inline to avoid declaring
| |
34 | |
35 Node* FastLoadLastIndex(CodeStubAssembler* a, Node* regexp) { | |
36 // Load the in-object field. | 96 // Load the in-object field. |
37 static const int field_offset = | 97 static const int field_offset = |
38 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; | 98 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; |
39 return a->LoadObjectField(regexp, field_offset); | 99 return LoadObjectField(regexp, field_offset); |
40 } | 100 } |
41 | 101 |
42 Node* SlowLoadLastIndex(CodeStubAssembler* a, Node* context, Node* regexp) { | 102 Node* RegExpBuiltinsAssembler::SlowLoadLastIndex(Node* context, Node* regexp) { |
43 // Load through the GetProperty stub. | 103 // Load through the GetProperty stub. |
44 Node* const name = | 104 Node* const name = HeapConstant(isolate()->factory()->lastIndex_string()); |
45 a->HeapConstant(a->isolate()->factory()->lastIndex_string()); | 105 Callable getproperty_callable = CodeFactory::GetProperty(isolate()); |
46 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 106 return CallStub(getproperty_callable, context, regexp, name); |
47 return a->CallStub(getproperty_callable, context, regexp, name); | |
48 } | 107 } |
49 | 108 |
50 Node* LoadLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, | 109 Node* RegExpBuiltinsAssembler::LoadLastIndex(Node* context, Node* regexp, |
51 bool is_fastpath) { | 110 bool is_fastpath) { |
52 return is_fastpath ? FastLoadLastIndex(a, regexp) | 111 return is_fastpath ? FastLoadLastIndex(regexp) |
53 : SlowLoadLastIndex(a, context, regexp); | 112 : SlowLoadLastIndex(context, regexp); |
54 } | 113 } |
55 | 114 |
56 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified | 115 // The fast-path of StoreLastIndex when regexp is guaranteed to be an unmodified |
57 // JSRegExp instance. | 116 // JSRegExp instance. |
58 void FastStoreLastIndex(CodeStubAssembler* a, Node* regexp, Node* value) { | 117 void RegExpBuiltinsAssembler::FastStoreLastIndex(Node* regexp, Node* value) { |
59 // Store the in-object field. | 118 // Store the in-object field. |
60 static const int field_offset = | 119 static const int field_offset = |
61 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; | 120 JSRegExp::kSize + JSRegExp::kLastIndexFieldIndex * kPointerSize; |
62 a->StoreObjectField(regexp, field_offset, value); | 121 StoreObjectField(regexp, field_offset, value); |
63 } | 122 } |
64 | 123 |
65 void SlowStoreLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, | 124 void RegExpBuiltinsAssembler::SlowStoreLastIndex(Node* context, Node* regexp, |
66 Node* value) { | 125 Node* value) { |
67 // Store through runtime. | 126 // Store through runtime. |
68 // TODO(ishell): Use SetPropertyStub here once available. | 127 // TODO(ishell): Use SetPropertyStub here once available. |
69 Node* const name = | 128 Node* const name = HeapConstant(isolate()->factory()->lastIndex_string()); |
70 a->HeapConstant(a->isolate()->factory()->lastIndex_string()); | 129 Node* const language_mode = SmiConstant(Smi::FromInt(STRICT)); |
71 Node* const language_mode = a->SmiConstant(Smi::FromInt(STRICT)); | 130 CallRuntime(Runtime::kSetProperty, context, regexp, name, value, |
72 a->CallRuntime(Runtime::kSetProperty, context, regexp, name, value, | 131 language_mode); |
73 language_mode); | |
74 } | 132 } |
75 | 133 |
76 void StoreLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, | 134 void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp, |
77 Node* value, bool is_fastpath) { | 135 Node* value, bool is_fastpath) { |
78 if (is_fastpath) { | 136 if (is_fastpath) { |
79 FastStoreLastIndex(a, regexp, value); | 137 FastStoreLastIndex(regexp, value); |
80 } else { | 138 } else { |
81 SlowStoreLastIndex(a, context, regexp, value); | 139 SlowStoreLastIndex(context, regexp, value); |
82 } | 140 } |
83 } | 141 } |
84 | 142 |
85 Node* ConstructNewResultFromMatchInfo(Isolate* isolate, CodeStubAssembler* a, | 143 Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(Node* context, |
86 Node* context, Node* match_info, | 144 Node* match_info, |
87 Node* string) { | 145 Node* string) { |
88 CLabel out(a); | 146 Label out(this); |
89 | 147 |
90 Node* const num_indices = a->SmiUntag(a->LoadFixedArrayElement( | 148 Node* const num_indices = SmiUntag(LoadFixedArrayElement( |
91 match_info, RegExpMatchInfo::kNumberOfCapturesIndex)); | 149 match_info, RegExpMatchInfo::kNumberOfCapturesIndex)); |
92 Node* const num_results = a->SmiTag(a->WordShr(num_indices, 1)); | 150 Node* const num_results = SmiTag(WordShr(num_indices, 1)); |
93 Node* const start = | 151 Node* const start = |
94 a->LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex); | 152 LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex); |
95 Node* const end = a->LoadFixedArrayElement( | 153 Node* const end = LoadFixedArrayElement( |
96 match_info, RegExpMatchInfo::kFirstCaptureIndex + 1); | 154 match_info, RegExpMatchInfo::kFirstCaptureIndex + 1); |
97 | 155 |
98 // Calculate the substring of the first match before creating the result array | 156 // Calculate the substring of the first match before creating the result array |
99 // to avoid an unnecessary write barrier storing the first result. | 157 // to avoid an unnecessary write barrier storing the first result. |
100 Node* const first = a->SubString(context, string, start, end); | 158 Node* const first = SubString(context, string, start, end); |
101 | 159 |
102 Node* const result = | 160 Node* const result = |
103 a->AllocateRegExpResult(context, num_results, start, string); | 161 AllocateRegExpResult(context, num_results, start, string); |
104 Node* const result_elements = a->LoadElements(result); | 162 Node* const result_elements = LoadElements(result); |
105 | 163 |
106 a->StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER); | 164 StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER); |
107 | 165 |
108 a->GotoIf(a->SmiEqual(num_results, a->SmiConstant(Smi::FromInt(1))), &out); | 166 GotoIf(SmiEqual(num_results, SmiConstant(Smi::FromInt(1))), &out); |
109 | 167 |
110 // Store all remaining captures. | 168 // Store all remaining captures. |
111 Node* const limit = a->IntPtrAdd( | 169 Node* const limit = IntPtrAdd( |
112 a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices); | 170 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices); |
113 | 171 |
114 CVariable var_from_cursor(a, MachineType::PointerRepresentation()); | 172 Variable var_from_cursor(this, MachineType::PointerRepresentation()); |
115 CVariable var_to_cursor(a, MachineType::PointerRepresentation()); | 173 Variable var_to_cursor(this, MachineType::PointerRepresentation()); |
116 | 174 |
117 var_from_cursor.Bind( | 175 var_from_cursor.Bind(IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2)); |
118 a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2)); | 176 var_to_cursor.Bind(IntPtrConstant(1)); |
119 var_to_cursor.Bind(a->IntPtrConstant(1)); | |
120 | 177 |
121 CVariable* vars[] = {&var_from_cursor, &var_to_cursor}; | 178 Variable* vars[] = {&var_from_cursor, &var_to_cursor}; |
122 CLabel loop(a, 2, vars); | 179 Label loop(this, 2, vars); |
123 | 180 |
124 a->Goto(&loop); | 181 Goto(&loop); |
125 a->Bind(&loop); | 182 Bind(&loop); |
126 { | 183 { |
127 Node* const from_cursor = var_from_cursor.value(); | 184 Node* const from_cursor = var_from_cursor.value(); |
128 Node* const to_cursor = var_to_cursor.value(); | 185 Node* const to_cursor = var_to_cursor.value(); |
129 Node* const start = a->LoadFixedArrayElement(match_info, from_cursor); | 186 Node* const start = LoadFixedArrayElement(match_info, from_cursor); |
130 | 187 |
131 CLabel next_iter(a); | 188 Label next_iter(this); |
132 a->GotoIf(a->SmiEqual(start, a->SmiConstant(Smi::FromInt(-1))), &next_iter); | 189 GotoIf(SmiEqual(start, SmiConstant(Smi::FromInt(-1))), &next_iter); |
133 | 190 |
134 Node* const from_cursor_plus1 = | 191 Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1)); |
135 a->IntPtrAdd(from_cursor, a->IntPtrConstant(1)); | 192 Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1); |
136 Node* const end = a->LoadFixedArrayElement(match_info, from_cursor_plus1); | |
137 | 193 |
138 Node* const capture = a->SubString(context, string, start, end); | 194 Node* const capture = SubString(context, string, start, end); |
139 a->StoreFixedArrayElement(result_elements, to_cursor, capture); | 195 StoreFixedArrayElement(result_elements, to_cursor, capture); |
140 a->Goto(&next_iter); | 196 Goto(&next_iter); |
141 | 197 |
142 a->Bind(&next_iter); | 198 Bind(&next_iter); |
143 var_from_cursor.Bind(a->IntPtrAdd(from_cursor, a->IntPtrConstant(2))); | 199 var_from_cursor.Bind(IntPtrAdd(from_cursor, IntPtrConstant(2))); |
144 var_to_cursor.Bind(a->IntPtrAdd(to_cursor, a->IntPtrConstant(1))); | 200 var_to_cursor.Bind(IntPtrAdd(to_cursor, IntPtrConstant(1))); |
145 a->Branch(a->UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out); | 201 Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out); |
146 } | 202 } |
147 | 203 |
148 a->Bind(&out); | 204 Bind(&out); |
149 return result; | 205 return result; |
150 } | 206 } |
151 | 207 |
152 // ES#sec-regexp.prototype.exec | 208 // ES#sec-regexp.prototype.exec |
153 // RegExp.prototype.exec ( string ) | 209 // RegExp.prototype.exec ( string ) |
154 // Implements the core of RegExp.prototype.exec but without actually | 210 // Implements the core of RegExp.prototype.exec but without actually |
155 // constructing the JSRegExpResult. Returns either null (if the RegExp did not | 211 // constructing the JSRegExpResult. Returns either null (if the RegExp did not |
156 // match) or a fixed array containing match indices as returned by | 212 // match) or a fixed array containing match indices as returned by |
157 // RegExpExecStub. | 213 // RegExpExecStub. |
158 Node* RegExpPrototypeExecBodyWithoutResult( | 214 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult( |
159 CodeStubAssembler* a, Node* const context, Node* const regexp, | 215 Node* const context, Node* const regexp, Node* const string, |
160 Node* const string, CLabel* if_didnotmatch, const bool is_fastpath) { | 216 Label* if_didnotmatch, const bool is_fastpath) { |
161 Isolate* const isolate = a->isolate(); | 217 Isolate* const isolate = this->isolate(); |
162 | 218 |
163 Node* const null = a->NullConstant(); | 219 Node* const null = NullConstant(); |
164 Node* const int_zero = a->IntPtrConstant(0); | 220 Node* const int_zero = IntPtrConstant(0); |
165 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 221 Node* const smi_zero = SmiConstant(Smi::kZero); |
166 | 222 |
167 if (!is_fastpath) { | 223 if (!is_fastpath) { |
168 a->ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, | 224 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, |
169 "RegExp.prototype.exec"); | 225 "RegExp.prototype.exec"); |
170 } | 226 } |
171 | 227 |
172 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(string))); | 228 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(string))); |
173 CSA_ASSERT(a, a->HasInstanceType(regexp, JS_REGEXP_TYPE)); | 229 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE)); |
174 | 230 |
175 CVariable var_result(a, MachineRepresentation::kTagged); | 231 Variable var_result(this, MachineRepresentation::kTagged); |
176 CLabel out(a); | 232 Label out(this); |
177 | 233 |
178 Node* const native_context = a->LoadNativeContext(context); | 234 Node* const native_context = LoadNativeContext(context); |
179 Node* const string_length = a->LoadStringLength(string); | 235 Node* const string_length = LoadStringLength(string); |
180 | 236 |
181 // Check whether the regexp is global or sticky, which determines whether we | 237 // Check whether the regexp is global or sticky, which determines whether we |
182 // update last index later on. | 238 // update last index later on. |
183 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); | 239 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
184 Node* const is_global_or_sticky = | 240 Node* const is_global_or_sticky = WordAnd( |
185 a->WordAnd(a->SmiUntag(flags), | 241 SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky)); |
186 a->IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky)); | |
187 Node* const should_update_last_index = | 242 Node* const should_update_last_index = |
188 a->WordNotEqual(is_global_or_sticky, int_zero); | 243 WordNotEqual(is_global_or_sticky, int_zero); |
189 | 244 |
190 // Grab and possibly update last index. | 245 // Grab and possibly update last index. |
191 CLabel run_exec(a); | 246 Label run_exec(this); |
192 CVariable var_lastindex(a, MachineRepresentation::kTagged); | 247 Variable var_lastindex(this, MachineRepresentation::kTagged); |
193 { | 248 { |
194 CLabel if_doupdate(a), if_dontupdate(a); | 249 Label if_doupdate(this), if_dontupdate(this); |
195 a->Branch(should_update_last_index, &if_doupdate, &if_dontupdate); | 250 Branch(should_update_last_index, &if_doupdate, &if_dontupdate); |
196 | 251 |
197 a->Bind(&if_doupdate); | 252 Bind(&if_doupdate); |
198 { | 253 { |
199 Node* const regexp_lastindex = | 254 Node* const regexp_lastindex = |
200 LoadLastIndex(a, context, regexp, is_fastpath); | 255 LoadLastIndex(context, regexp, is_fastpath); |
201 var_lastindex.Bind(regexp_lastindex); | 256 var_lastindex.Bind(regexp_lastindex); |
202 | 257 |
203 // Omit ToLength if lastindex is a non-negative smi. | 258 // Omit ToLength if lastindex is a non-negative smi. |
204 { | 259 { |
205 CLabel call_tolength(a, CLabel::kDeferred), next(a); | 260 Label call_tolength(this, Label::kDeferred), next(this); |
206 a->Branch(a->WordIsPositiveSmi(regexp_lastindex), &next, | 261 Branch(WordIsPositiveSmi(regexp_lastindex), &next, &call_tolength); |
207 &call_tolength); | 262 |
208 | 263 Bind(&call_tolength); |
209 a->Bind(&call_tolength); | |
210 { | 264 { |
211 Callable tolength_callable = CodeFactory::ToLength(isolate); | 265 Callable tolength_callable = CodeFactory::ToLength(isolate); |
212 var_lastindex.Bind( | 266 var_lastindex.Bind( |
213 a->CallStub(tolength_callable, context, regexp_lastindex)); | 267 CallStub(tolength_callable, context, regexp_lastindex)); |
214 a->Goto(&next); | 268 Goto(&next); |
215 } | 269 } |
216 | 270 |
217 a->Bind(&next); | 271 Bind(&next); |
218 } | 272 } |
219 | 273 |
220 Node* const lastindex = var_lastindex.value(); | 274 Node* const lastindex = var_lastindex.value(); |
221 | 275 |
222 CLabel if_isoob(a, CLabel::kDeferred); | 276 Label if_isoob(this, Label::kDeferred); |
223 a->GotoUnless(a->TaggedIsSmi(lastindex), &if_isoob); | 277 GotoUnless(TaggedIsSmi(lastindex), &if_isoob); |
224 a->GotoUnless(a->SmiLessThanOrEqual(lastindex, string_length), &if_isoob); | 278 GotoUnless(SmiLessThanOrEqual(lastindex, string_length), &if_isoob); |
225 a->Goto(&run_exec); | 279 Goto(&run_exec); |
226 | 280 |
227 a->Bind(&if_isoob); | 281 Bind(&if_isoob); |
228 { | 282 { |
229 StoreLastIndex(a, context, regexp, smi_zero, is_fastpath); | 283 StoreLastIndex(context, regexp, smi_zero, is_fastpath); |
230 var_result.Bind(null); | 284 var_result.Bind(null); |
231 a->Goto(if_didnotmatch); | 285 Goto(if_didnotmatch); |
232 } | 286 } |
233 } | 287 } |
234 | 288 |
235 a->Bind(&if_dontupdate); | 289 Bind(&if_dontupdate); |
236 { | 290 { |
237 var_lastindex.Bind(smi_zero); | 291 var_lastindex.Bind(smi_zero); |
238 a->Goto(&run_exec); | 292 Goto(&run_exec); |
239 } | 293 } |
240 } | 294 } |
241 | 295 |
242 Node* match_indices; | 296 Node* match_indices; |
243 CLabel successful_match(a); | 297 Label successful_match(this); |
244 a->Bind(&run_exec); | 298 Bind(&run_exec); |
245 { | 299 { |
246 // Get last match info from the context. | 300 // Get last match info from the context. |
247 Node* const last_match_info = a->LoadContextElement( | 301 Node* const last_match_info = LoadContextElement( |
248 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 302 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
249 | 303 |
250 // Call the exec stub. | 304 // Call the exec stub. |
251 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 305 Callable exec_callable = CodeFactory::RegExpExec(isolate); |
252 match_indices = a->CallStub(exec_callable, context, regexp, string, | 306 match_indices = CallStub(exec_callable, context, regexp, string, |
253 var_lastindex.value(), last_match_info); | 307 var_lastindex.value(), last_match_info); |
254 var_result.Bind(match_indices); | 308 var_result.Bind(match_indices); |
255 | 309 |
256 // {match_indices} is either null or the RegExpMatchInfo array. | 310 // {match_indices} is either null or the RegExpMatchInfo array. |
257 // Return early if exec failed, possibly updating last index. | 311 // Return early if exec failed, possibly updating last index. |
258 a->GotoUnless(a->WordEqual(match_indices, null), &successful_match); | 312 GotoUnless(WordEqual(match_indices, null), &successful_match); |
259 | 313 |
260 a->GotoUnless(should_update_last_index, if_didnotmatch); | 314 GotoUnless(should_update_last_index, if_didnotmatch); |
261 | 315 |
262 StoreLastIndex(a, context, regexp, smi_zero, is_fastpath); | 316 StoreLastIndex(context, regexp, smi_zero, is_fastpath); |
263 a->Goto(if_didnotmatch); | 317 Goto(if_didnotmatch); |
264 } | 318 } |
265 | 319 |
266 a->Bind(&successful_match); | 320 Bind(&successful_match); |
267 { | 321 { |
268 a->GotoUnless(should_update_last_index, &out); | 322 GotoUnless(should_update_last_index, &out); |
269 | 323 |
270 // Update the new last index from {match_indices}. | 324 // Update the new last index from {match_indices}. |
271 Node* const new_lastindex = a->LoadFixedArrayElement( | 325 Node* const new_lastindex = LoadFixedArrayElement( |
272 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); | 326 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
273 | 327 |
274 StoreLastIndex(a, context, regexp, new_lastindex, is_fastpath); | 328 StoreLastIndex(context, regexp, new_lastindex, is_fastpath); |
275 a->Goto(&out); | 329 Goto(&out); |
276 } | 330 } |
277 | 331 |
278 a->Bind(&out); | 332 Bind(&out); |
279 return var_result.value(); | 333 return var_result.value(); |
280 } | 334 } |
281 | 335 |
282 // ES#sec-regexp.prototype.exec | 336 // ES#sec-regexp.prototype.exec |
283 // RegExp.prototype.exec ( string ) | 337 // RegExp.prototype.exec ( string ) |
284 Node* RegExpPrototypeExecBody(CodeStubAssembler* a, Node* const context, | 338 Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBody(Node* const context, |
285 Node* const regexp, Node* const string, | 339 Node* const regexp, |
286 const bool is_fastpath) { | 340 Node* const string, |
287 Isolate* const isolate = a->isolate(); | 341 const bool is_fastpath) { |
288 Node* const null = a->NullConstant(); | 342 Node* const null = NullConstant(); |
289 | 343 |
290 CVariable var_result(a, MachineRepresentation::kTagged); | 344 Variable var_result(this, MachineRepresentation::kTagged); |
291 | 345 |
292 CLabel if_didnotmatch(a), out(a); | 346 Label if_didnotmatch(this), out(this); |
293 Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult( | 347 Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult( |
294 a, context, regexp, string, &if_didnotmatch, is_fastpath); | 348 context, regexp, string, &if_didnotmatch, is_fastpath); |
295 | 349 |
296 // Successful match. | 350 // Successful match. |
297 { | 351 { |
298 Node* const match_indices = indices_or_null; | 352 Node* const match_indices = indices_or_null; |
299 Node* const result = ConstructNewResultFromMatchInfo(isolate, a, context, | 353 Node* const result = |
300 match_indices, string); | 354 ConstructNewResultFromMatchInfo(context, match_indices, string); |
301 var_result.Bind(result); | 355 var_result.Bind(result); |
302 a->Goto(&out); | 356 Goto(&out); |
303 } | 357 } |
304 | 358 |
305 a->Bind(&if_didnotmatch); | 359 Bind(&if_didnotmatch); |
306 { | 360 { |
307 var_result.Bind(null); | 361 var_result.Bind(null); |
308 a->Goto(&out); | 362 Goto(&out); |
309 } | 363 } |
310 | 364 |
311 a->Bind(&out); | 365 Bind(&out); |
312 return var_result.value(); | 366 return var_result.value(); |
313 } | 367 } |
314 | 368 |
315 Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, | 369 Node* RegExpBuiltinsAssembler::ThrowIfNotJSReceiver( |
316 Node* context, Node* value, | 370 Node* context, Node* maybe_receiver, MessageTemplate::Template msg_template, |
317 MessageTemplate::Template msg_template, | 371 char const* method_name) { |
318 char const* method_name) { | 372 Label out(this), throw_exception(this, Label::kDeferred); |
319 CLabel out(a), throw_exception(a, CLabel::kDeferred); | 373 Variable var_value_map(this, MachineRepresentation::kTagged); |
320 CVariable var_value_map(a, MachineRepresentation::kTagged); | 374 |
321 | 375 GotoIf(TaggedIsSmi(maybe_receiver), &throw_exception); |
322 a->GotoIf(a->TaggedIsSmi(value), &throw_exception); | |
323 | 376 |
324 // Load the instance type of the {value}. | 377 // Load the instance type of the {value}. |
325 var_value_map.Bind(a->LoadMap(value)); | 378 var_value_map.Bind(LoadMap(maybe_receiver)); |
326 Node* const value_instance_type = | 379 Node* const value_instance_type = LoadMapInstanceType(var_value_map.value()); |
327 a->LoadMapInstanceType(var_value_map.value()); | 380 |
328 | 381 Branch(IsJSReceiverInstanceType(value_instance_type), &out, &throw_exception); |
329 a->Branch(a->IsJSReceiverInstanceType(value_instance_type), &out, | |
330 &throw_exception); | |
331 | 382 |
332 // The {value} is not a compatible receiver for this method. | 383 // The {value} is not a compatible receiver for this method. |
333 a->Bind(&throw_exception); | 384 Bind(&throw_exception); |
334 { | 385 { |
335 Node* const message_id = a->SmiConstant(Smi::FromInt(msg_template)); | 386 Node* const message_id = SmiConstant(Smi::FromInt(msg_template)); |
336 Node* const method_name_str = a->HeapConstant( | 387 Node* const method_name_str = HeapConstant( |
337 isolate->factory()->NewStringFromAsciiChecked(method_name, TENURED)); | 388 isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED)); |
338 | 389 |
339 Callable callable = CodeFactory::ToString(isolate); | 390 Callable callable = CodeFactory::ToString(isolate()); |
340 Node* const value_str = a->CallStub(callable, context, value); | 391 Node* const value_str = CallStub(callable, context, maybe_receiver); |
341 | 392 |
342 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, | 393 CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str, |
343 method_name_str, value_str); | 394 value_str); |
344 var_value_map.Bind(a->UndefinedConstant()); | 395 var_value_map.Bind(UndefinedConstant()); |
345 a->Goto(&out); // Never reached. | 396 Goto(&out); // Never reached. |
346 } | 397 } |
347 | 398 |
348 a->Bind(&out); | 399 Bind(&out); |
349 return var_value_map.value(); | 400 return var_value_map.value(); |
350 } | 401 } |
351 | 402 |
352 Node* IsInitialRegExpMap(CodeStubAssembler* a, Node* context, Node* map) { | 403 Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* map) { |
353 Node* const native_context = a->LoadNativeContext(context); | 404 Node* const native_context = LoadNativeContext(context); |
354 Node* const regexp_fun = | 405 Node* const regexp_fun = |
355 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 406 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
356 Node* const initial_map = | 407 Node* const initial_map = |
357 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 408 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
358 Node* const has_initialmap = a->WordEqual(map, initial_map); | 409 Node* const has_initialmap = WordEqual(map, initial_map); |
359 | 410 |
360 return has_initialmap; | 411 return has_initialmap; |
361 } | 412 } |
362 | 413 |
363 // RegExp fast path implementations rely on unmodified JSRegExp instances. | 414 // RegExp fast path implementations rely on unmodified JSRegExp instances. |
364 // We use a fairly coarse granularity for this and simply check whether both | 415 // We use a fairly coarse granularity for this and simply check whether both |
365 // the regexp itself is unmodified (i.e. its map has not changed) and its | 416 // the regexp itself is unmodified (i.e. its map has not changed) and its |
366 // prototype is unmodified. | 417 // prototype is unmodified. |
367 void BranchIfFastPath(CodeStubAssembler* a, Node* context, Node* map, | 418 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map, |
368 CLabel* if_isunmodified, CLabel* if_ismodified) { | 419 Label* if_isunmodified, |
369 Node* const native_context = a->LoadNativeContext(context); | 420 Label* if_ismodified) { |
421 Node* const native_context = LoadNativeContext(context); | |
370 Node* const regexp_fun = | 422 Node* const regexp_fun = |
371 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 423 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
372 Node* const initial_map = | 424 Node* const initial_map = |
373 a->LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 425 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
374 Node* const has_initialmap = a->WordEqual(map, initial_map); | 426 Node* const has_initialmap = WordEqual(map, initial_map); |
375 | 427 |
376 a->GotoUnless(has_initialmap, if_ismodified); | 428 GotoUnless(has_initialmap, if_ismodified); |
377 | 429 |
378 Node* const initial_proto_initial_map = a->LoadContextElement( | 430 Node* const initial_proto_initial_map = |
379 native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); | 431 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); |
380 Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map)); | 432 Node* const proto_map = LoadMap(LoadMapPrototype(map)); |
381 Node* const proto_has_initialmap = | 433 Node* const proto_has_initialmap = |
382 a->WordEqual(proto_map, initial_proto_initial_map); | 434 WordEqual(proto_map, initial_proto_initial_map); |
383 | 435 |
384 // TODO(ishell): Update this check once map changes for constant field | 436 // TODO(ishell): Update this check once map changes for constant field |
385 // tracking are landing. | 437 // tracking are landing. |
386 | 438 |
387 a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); | 439 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); |
388 } | 440 } |
389 | 441 |
390 void BranchIfFastRegExpResult(CodeStubAssembler* a, Node* context, Node* map, | 442 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map, |
391 CLabel* if_isunmodified, CLabel* if_ismodified) { | 443 Label* if_isunmodified, |
392 Node* const native_context = a->LoadNativeContext(context); | 444 Label* if_ismodified) { |
445 Node* const native_context = LoadNativeContext(context); | |
393 Node* const initial_regexp_result_map = | 446 Node* const initial_regexp_result_map = |
394 a->LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); | 447 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); |
395 | 448 |
396 a->Branch(a->WordEqual(map, initial_regexp_result_map), if_isunmodified, | 449 Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified, |
397 if_ismodified); | 450 if_ismodified); |
398 } | 451 } |
399 | 452 |
400 } // namespace | |
401 | |
402 // ES#sec-regexp.prototype.exec | 453 // ES#sec-regexp.prototype.exec |
403 // RegExp.prototype.exec ( string ) | 454 // RegExp.prototype.exec ( string ) |
404 TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) { | 455 TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) { |
405 Node* const maybe_receiver = Parameter(0); | 456 Node* const maybe_receiver = Parameter(0); |
406 Node* const maybe_string = Parameter(1); | 457 Node* const maybe_string = Parameter(1); |
407 Node* const context = Parameter(4); | 458 Node* const context = Parameter(4); |
408 | 459 |
409 // Ensure {maybe_receiver} is a JSRegExp. | 460 // Ensure {maybe_receiver} is a JSRegExp. |
410 Node* const regexp_map = ThrowIfNotInstanceType( | 461 Node* const regexp_map = ThrowIfNotInstanceType( |
411 context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); | 462 context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); |
412 Node* const receiver = maybe_receiver; | 463 Node* const receiver = maybe_receiver; |
413 | 464 |
414 // Convert {maybe_string} to a String. | 465 // Convert {maybe_string} to a String. |
415 Node* const string = ToString(context, maybe_string); | 466 Node* const string = ToString(context, maybe_string); |
416 | 467 |
417 CLabel if_isfastpath(this), if_isslowpath(this); | 468 Label if_isfastpath(this), if_isslowpath(this); |
418 Branch(IsInitialRegExpMap(this, context, regexp_map), &if_isfastpath, | 469 Branch(IsInitialRegExpMap(context, regexp_map), &if_isfastpath, |
419 &if_isslowpath); | 470 &if_isslowpath); |
420 | 471 |
421 Bind(&if_isfastpath); | 472 Bind(&if_isfastpath); |
422 { | 473 { |
423 Node* const result = | 474 Node* const result = |
424 RegExpPrototypeExecBody(this, context, receiver, string, true); | 475 RegExpPrototypeExecBody(context, receiver, string, true); |
425 Return(result); | 476 Return(result); |
426 } | 477 } |
427 | 478 |
428 Bind(&if_isslowpath); | 479 Bind(&if_isslowpath); |
429 { | 480 { |
430 Node* const result = | 481 Node* const result = |
431 RegExpPrototypeExecBody(this, context, receiver, string, false); | 482 RegExpPrototypeExecBody(context, receiver, string, false); |
432 Return(result); | 483 Return(result); |
433 } | 484 } |
434 } | 485 } |
435 | 486 |
436 namespace { | 487 Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context, |
488 Node* const regexp, | |
489 bool is_fastpath) { | |
490 Isolate* isolate = this->isolate(); | |
437 | 491 |
438 Node* FlagsGetter(CodeStubAssembler* a, Node* const receiver, | 492 Node* const int_zero = IntPtrConstant(0); |
439 Node* const context, bool is_fastpath) { | 493 Node* const int_one = IntPtrConstant(1); |
440 Isolate* isolate = a->isolate(); | 494 Variable var_length(this, MachineType::PointerRepresentation()); |
441 | 495 Variable var_flags(this, MachineType::PointerRepresentation()); |
442 Node* const int_zero = a->IntPtrConstant(0); | |
443 Node* const int_one = a->IntPtrConstant(1); | |
444 CVariable var_length(a, MachineType::PointerRepresentation()); | |
445 CVariable var_flags(a, MachineType::PointerRepresentation()); | |
446 | 496 |
447 // First, count the number of characters we will need and check which flags | 497 // First, count the number of characters we will need and check which flags |
448 // are set. | 498 // are set. |
449 | 499 |
450 var_length.Bind(int_zero); | 500 var_length.Bind(int_zero); |
451 | 501 |
452 CLabel construct_string(a); | 502 Label construct_string(this); |
453 if (is_fastpath) { | 503 if (is_fastpath) { |
454 // Refer to JSRegExp's flag property on the fast-path. | 504 // Refer to JSRegExp's flag property on the fast-path. |
455 Node* const flags_smi = | 505 Node* const flags_smi = LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
456 a->LoadObjectField(receiver, JSRegExp::kFlagsOffset); | 506 Node* const flags_intptr = SmiUntag(flags_smi); |
457 Node* const flags_intptr = a->SmiUntag(flags_smi); | |
458 var_flags.Bind(flags_intptr); | 507 var_flags.Bind(flags_intptr); |
459 | 508 |
460 CLabel label_global(a), label_ignorecase(a), label_multiline(a), | 509 Label label_global(this), label_ignorecase(this), label_multiline(this), |
461 label_unicode(a), label_sticky(a); | 510 label_unicode(this), label_sticky(this); |
462 | 511 |
463 #define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL) \ | 512 #define CASE_FOR_FLAG(FLAG, LABEL, NEXT_LABEL) \ |
464 do { \ | 513 do { \ |
465 a->Bind(&LABEL); \ | 514 Bind(&LABEL); \ |
466 Node* const mask = a->IntPtrConstant(FLAG); \ | 515 Node* const mask = IntPtrConstant(FLAG); \ |
467 a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ | 516 GotoIf(WordEqual(WordAnd(flags_intptr, mask), int_zero), &NEXT_LABEL); \ |
468 &NEXT_LABEL); \ | 517 var_length.Bind(IntPtrAdd(var_length.value(), int_one)); \ |
469 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ | 518 Goto(&NEXT_LABEL); \ |
470 a->Goto(&NEXT_LABEL); \ | |
471 } while (false) | 519 } while (false) |
472 | 520 |
473 a->Goto(&label_global); | 521 Goto(&label_global); |
474 CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase); | 522 CASE_FOR_FLAG(JSRegExp::kGlobal, label_global, label_ignorecase); |
475 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline); | 523 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, label_ignorecase, label_multiline); |
476 CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode); | 524 CASE_FOR_FLAG(JSRegExp::kMultiline, label_multiline, label_unicode); |
477 CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky); | 525 CASE_FOR_FLAG(JSRegExp::kUnicode, label_unicode, label_sticky); |
478 CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string); | 526 CASE_FOR_FLAG(JSRegExp::kSticky, label_sticky, construct_string); |
479 #undef CASE_FOR_FLAG | 527 #undef CASE_FOR_FLAG |
480 } else { | 528 } else { |
481 DCHECK(!is_fastpath); | 529 DCHECK(!is_fastpath); |
482 | 530 |
483 // Fall back to GetProperty stub on the slow-path. | 531 // Fall back to GetProperty stub on the slow-path. |
484 var_flags.Bind(int_zero); | 532 var_flags.Bind(int_zero); |
485 | 533 |
486 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 534 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
487 CLabel label_global(a), label_ignorecase(a), label_multiline(a), | 535 Label label_global(this), label_ignorecase(this), label_multiline(this), |
488 label_unicode(a), label_sticky(a); | 536 label_unicode(this), label_sticky(this); |
489 | 537 |
490 #define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL) \ | 538 #define CASE_FOR_FLAG(NAME, FLAG, LABEL, NEXT_LABEL) \ |
491 do { \ | 539 do { \ |
492 a->Bind(&LABEL); \ | 540 Bind(&LABEL); \ |
493 Node* const name = \ | 541 Node* const name = \ |
494 a->HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \ | 542 HeapConstant(isolate->factory()->NewStringFromAsciiChecked(NAME)); \ |
495 Node* const flag = \ | 543 Node* const flag = CallStub(getproperty_callable, context, regexp, name); \ |
496 a->CallStub(getproperty_callable, context, receiver, name); \ | 544 Label if_isflagset(this); \ |
497 CLabel if_isflagset(a); \ | 545 BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL); \ |
498 a->BranchIfToBooleanIsTrue(flag, &if_isflagset, &NEXT_LABEL); \ | 546 Bind(&if_isflagset); \ |
499 a->Bind(&if_isflagset); \ | 547 var_length.Bind(IntPtrAdd(var_length.value(), int_one)); \ |
500 var_length.Bind(a->IntPtrAdd(var_length.value(), int_one)); \ | 548 var_flags.Bind(WordOr(var_flags.value(), IntPtrConstant(FLAG))); \ |
501 var_flags.Bind(a->WordOr(var_flags.value(), a->IntPtrConstant(FLAG))); \ | 549 Goto(&NEXT_LABEL); \ |
502 a->Goto(&NEXT_LABEL); \ | |
503 } while (false) | 550 } while (false) |
504 | 551 |
505 a->Goto(&label_global); | 552 Goto(&label_global); |
506 CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase); | 553 CASE_FOR_FLAG("global", JSRegExp::kGlobal, label_global, label_ignorecase); |
507 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase, | 554 CASE_FOR_FLAG("ignoreCase", JSRegExp::kIgnoreCase, label_ignorecase, |
508 label_multiline); | 555 label_multiline); |
509 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline, | 556 CASE_FOR_FLAG("multiline", JSRegExp::kMultiline, label_multiline, |
510 label_unicode); | 557 label_unicode); |
511 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky); | 558 CASE_FOR_FLAG("unicode", JSRegExp::kUnicode, label_unicode, label_sticky); |
512 CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string); | 559 CASE_FOR_FLAG("sticky", JSRegExp::kSticky, label_sticky, construct_string); |
513 #undef CASE_FOR_FLAG | 560 #undef CASE_FOR_FLAG |
514 } | 561 } |
515 | 562 |
516 // Allocate a string of the required length and fill it with the corresponding | 563 // Allocate a string of the required length and fill it with the corresponding |
517 // char for each set flag. | 564 // char for each set flag. |
518 | 565 |
519 a->Bind(&construct_string); | 566 Bind(&construct_string); |
520 { | 567 { |
521 Node* const result = | 568 Node* const result = AllocateSeqOneByteString(context, var_length.value()); |
522 a->AllocateSeqOneByteString(context, var_length.value()); | |
523 Node* const flags_intptr = var_flags.value(); | 569 Node* const flags_intptr = var_flags.value(); |
524 | 570 |
525 CVariable var_offset(a, MachineType::PointerRepresentation()); | 571 Variable var_offset(this, MachineType::PointerRepresentation()); |
526 var_offset.Bind( | 572 var_offset.Bind( |
527 a->IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); | 573 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); |
528 | 574 |
529 CLabel label_global(a), label_ignorecase(a), label_multiline(a), | 575 Label label_global(this), label_ignorecase(this), label_multiline(this), |
530 label_unicode(a), label_sticky(a), out(a); | 576 label_unicode(this), label_sticky(this), out(this); |
531 | 577 |
532 #define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL) \ | 578 #define CASE_FOR_FLAG(FLAG, CHAR, LABEL, NEXT_LABEL) \ |
533 do { \ | 579 do { \ |
534 a->Bind(&LABEL); \ | 580 Bind(&LABEL); \ |
535 Node* const mask = a->IntPtrConstant(FLAG); \ | 581 Node* const mask = IntPtrConstant(FLAG); \ |
536 a->GotoIf(a->WordEqual(a->WordAnd(flags_intptr, mask), int_zero), \ | 582 GotoIf(WordEqual(WordAnd(flags_intptr, mask), int_zero), &NEXT_LABEL); \ |
537 &NEXT_LABEL); \ | 583 Node* const value = IntPtrConstant(CHAR); \ |
538 Node* const value = a->IntPtrConstant(CHAR); \ | 584 StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ |
539 a->StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \ | 585 var_offset.value(), value); \ |
540 var_offset.value(), value); \ | 586 var_offset.Bind(IntPtrAdd(var_offset.value(), int_one)); \ |
541 var_offset.Bind(a->IntPtrAdd(var_offset.value(), int_one)); \ | 587 Goto(&NEXT_LABEL); \ |
542 a->Goto(&NEXT_LABEL); \ | |
543 } while (false) | 588 } while (false) |
544 | 589 |
545 a->Goto(&label_global); | 590 Goto(&label_global); |
546 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase); | 591 CASE_FOR_FLAG(JSRegExp::kGlobal, 'g', label_global, label_ignorecase); |
547 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase, | 592 CASE_FOR_FLAG(JSRegExp::kIgnoreCase, 'i', label_ignorecase, |
548 label_multiline); | 593 label_multiline); |
549 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode); | 594 CASE_FOR_FLAG(JSRegExp::kMultiline, 'm', label_multiline, label_unicode); |
550 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky); | 595 CASE_FOR_FLAG(JSRegExp::kUnicode, 'u', label_unicode, label_sticky); |
551 CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out); | 596 CASE_FOR_FLAG(JSRegExp::kSticky, 'y', label_sticky, out); |
552 #undef CASE_FOR_FLAG | 597 #undef CASE_FOR_FLAG |
553 | 598 |
554 a->Bind(&out); | 599 Bind(&out); |
555 return result; | 600 return result; |
556 } | 601 } |
557 } | 602 } |
558 | 603 |
559 // ES#sec-isregexp IsRegExp ( argument ) | 604 // ES#sec-isregexp IsRegExp ( argument ) |
560 Node* IsRegExp(CodeStubAssembler* a, Node* const context, | 605 Node* RegExpBuiltinsAssembler::IsRegExp(Node* const context, |
561 Node* const maybe_receiver) { | 606 Node* const maybe_receiver) { |
562 CLabel out(a), if_isregexp(a); | 607 Label out(this), if_isregexp(this); |
563 | 608 |
564 CVariable var_result(a, MachineType::PointerRepresentation()); | 609 Variable var_result(this, MachineType::PointerRepresentation()); |
565 var_result.Bind(a->IntPtrConstant(0)); | 610 var_result.Bind(IntPtrConstant(0)); |
566 | 611 |
567 a->GotoIf(a->TaggedIsSmi(maybe_receiver), &out); | 612 GotoIf(TaggedIsSmi(maybe_receiver), &out); |
568 a->GotoUnless(a->IsJSReceiver(maybe_receiver), &out); | 613 GotoUnless(IsJSReceiver(maybe_receiver), &out); |
569 | 614 |
570 Node* const receiver = maybe_receiver; | 615 Node* const receiver = maybe_receiver; |
571 | 616 |
572 // Check @@match. | 617 // Check @@match. |
573 { | 618 { |
574 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 619 Callable getproperty_callable = CodeFactory::GetProperty(isolate()); |
575 Node* const name = a->HeapConstant(a->isolate()->factory()->match_symbol()); | 620 Node* const name = HeapConstant(isolate()->factory()->match_symbol()); |
576 Node* const value = | 621 Node* const value = CallStub(getproperty_callable, context, receiver, name); |
577 a->CallStub(getproperty_callable, context, receiver, name); | |
578 | 622 |
579 CLabel match_isundefined(a), match_isnotundefined(a); | 623 Label match_isundefined(this), match_isnotundefined(this); |
580 a->Branch(a->IsUndefined(value), &match_isundefined, &match_isnotundefined); | 624 Branch(IsUndefined(value), &match_isundefined, &match_isnotundefined); |
581 | 625 |
582 a->Bind(&match_isundefined); | 626 Bind(&match_isundefined); |
583 a->Branch(a->HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isregexp, &out); | 627 Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isregexp, &out); |
584 | 628 |
585 a->Bind(&match_isnotundefined); | 629 Bind(&match_isnotundefined); |
586 a->BranchIfToBooleanIsTrue(value, &if_isregexp, &out); | 630 BranchIfToBooleanIsTrue(value, &if_isregexp, &out); |
587 } | 631 } |
588 | 632 |
589 a->Bind(&if_isregexp); | 633 Bind(&if_isregexp); |
590 var_result.Bind(a->IntPtrConstant(1)); | 634 var_result.Bind(IntPtrConstant(1)); |
591 a->Goto(&out); | 635 Goto(&out); |
592 | 636 |
593 a->Bind(&out); | 637 Bind(&out); |
594 return var_result.value(); | 638 return var_result.value(); |
595 } | 639 } |
596 | 640 |
597 // ES#sec-regexpinitialize | 641 // ES#sec-regexpinitialize |
598 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) | 642 // Runtime Semantics: RegExpInitialize ( obj, pattern, flags ) |
599 Node* RegExpInitialize(CodeStubAssembler* a, Node* const context, | 643 Node* RegExpBuiltinsAssembler::RegExpInitialize(Node* const context, |
600 Node* const regexp, Node* const maybe_pattern, | 644 Node* const regexp, |
601 Node* const maybe_flags) { | 645 Node* const maybe_pattern, |
602 CVariable var_flags(a, MachineRepresentation::kTagged); | 646 Node* const maybe_flags) { |
603 CVariable var_pattern(a, MachineRepresentation::kTagged); | 647 Variable var_flags(this, MachineRepresentation::kTagged); |
648 Variable var_pattern(this, MachineRepresentation::kTagged); | |
604 | 649 |
605 // Normalize pattern. | 650 // Normalize pattern. |
606 { | 651 { |
607 CLabel next(a), if_isundefined(a), if_notundefined(a); | 652 Label next(this), if_isundefined(this), if_notundefined(this); |
608 a->Branch(a->IsUndefined(maybe_pattern), &if_isundefined, &if_notundefined); | 653 Branch(IsUndefined(maybe_pattern), &if_isundefined, &if_notundefined); |
609 | 654 |
610 a->Bind(&if_isundefined); | 655 Bind(&if_isundefined); |
611 { | 656 { |
612 var_pattern.Bind(a->EmptyStringConstant()); | 657 var_pattern.Bind(EmptyStringConstant()); |
613 a->Goto(&next); | 658 Goto(&next); |
614 } | 659 } |
615 | 660 |
616 a->Bind(&if_notundefined); | 661 Bind(&if_notundefined); |
617 { | 662 { |
618 Node* const pattern = a->ToString(context, maybe_pattern); | 663 Node* const pattern = ToString(context, maybe_pattern); |
619 var_pattern.Bind(pattern); | 664 var_pattern.Bind(pattern); |
620 a->Goto(&next); | 665 Goto(&next); |
621 } | 666 } |
622 | 667 |
623 a->Bind(&next); | 668 Bind(&next); |
624 } | 669 } |
625 | 670 |
626 // Normalize flags. | 671 // Normalize flags. |
627 { | 672 { |
628 CLabel next(a), if_isundefined(a), if_notundefined(a); | 673 Label next(this), if_isundefined(this), if_notundefined(this); |
629 a->Branch(a->IsUndefined(maybe_flags), &if_isundefined, &if_notundefined); | 674 Branch(IsUndefined(maybe_flags), &if_isundefined, &if_notundefined); |
630 | 675 |
631 a->Bind(&if_isundefined); | 676 Bind(&if_isundefined); |
632 { | 677 { |
633 var_flags.Bind(a->EmptyStringConstant()); | 678 var_flags.Bind(EmptyStringConstant()); |
634 a->Goto(&next); | 679 Goto(&next); |
635 } | 680 } |
636 | 681 |
637 a->Bind(&if_notundefined); | 682 Bind(&if_notundefined); |
638 { | 683 { |
639 Node* const flags = a->ToString(context, maybe_flags); | 684 Node* const flags = ToString(context, maybe_flags); |
640 var_flags.Bind(flags); | 685 var_flags.Bind(flags); |
641 a->Goto(&next); | 686 Goto(&next); |
642 } | 687 } |
643 | 688 |
644 a->Bind(&next); | 689 Bind(&next); |
645 } | 690 } |
646 | 691 |
647 // Initialize. | 692 // Initialize. |
648 | 693 |
649 { | 694 { |
650 Node* const result = | 695 Node* const result = |
651 a->CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp, | 696 CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp, |
652 var_pattern.value(), var_flags.value()); | 697 var_pattern.value(), var_flags.value()); |
653 return result; | 698 return result; |
654 } | 699 } |
655 } | 700 } |
656 | 701 |
657 } // namespace | |
658 | |
659 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) { | 702 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) { |
660 Isolate* isolate = this->isolate(); | |
661 | |
662 Node* const maybe_receiver = Parameter(0); | 703 Node* const maybe_receiver = Parameter(0); |
663 Node* const context = Parameter(3); | 704 Node* const context = Parameter(3); |
664 | 705 |
665 Node* const map = ThrowIfNotJSReceiver(this, isolate, context, maybe_receiver, | 706 Node* const map = ThrowIfNotJSReceiver(context, maybe_receiver, |
666 MessageTemplate::kRegExpNonObject, | 707 MessageTemplate::kRegExpNonObject, |
667 "RegExp.prototype.flags"); | 708 "RegExp.prototype.flags"); |
668 Node* const receiver = maybe_receiver; | 709 Node* const receiver = maybe_receiver; |
669 | 710 |
670 CLabel if_isfastpath(this), if_isslowpath(this, CLabel::kDeferred); | 711 Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred); |
671 Branch(IsInitialRegExpMap(this, context, map), &if_isfastpath, | 712 Branch(IsInitialRegExpMap(context, map), &if_isfastpath, &if_isslowpath); |
672 &if_isslowpath); | |
673 | 713 |
674 Bind(&if_isfastpath); | 714 Bind(&if_isfastpath); |
675 Return(FlagsGetter(this, receiver, context, true)); | 715 Return(FlagsGetter(context, receiver, true)); |
676 | 716 |
677 Bind(&if_isslowpath); | 717 Bind(&if_isslowpath); |
678 Return(FlagsGetter(this, receiver, context, false)); | 718 Return(FlagsGetter(context, receiver, false)); |
679 } | 719 } |
680 | 720 |
681 // ES#sec-regexp-pattern-flags | 721 // ES#sec-regexp-pattern-flags |
682 // RegExp ( pattern, flags ) | 722 // RegExp ( pattern, flags ) |
683 TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { | 723 TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) { |
684 Node* const pattern = Parameter(1); | 724 Node* const pattern = Parameter(1); |
685 Node* const flags = Parameter(2); | 725 Node* const flags = Parameter(2); |
686 Node* const new_target = Parameter(3); | 726 Node* const new_target = Parameter(3); |
687 Node* const context = Parameter(5); | 727 Node* const context = Parameter(5); |
688 | 728 |
689 Isolate* isolate = this->isolate(); | 729 Isolate* isolate = this->isolate(); |
690 | 730 |
691 CVariable var_flags(this, MachineRepresentation::kTagged); | 731 Variable var_flags(this, MachineRepresentation::kTagged); |
692 CVariable var_pattern(this, MachineRepresentation::kTagged); | 732 Variable var_pattern(this, MachineRepresentation::kTagged); |
693 CVariable var_new_target(this, MachineRepresentation::kTagged); | 733 Variable var_new_target(this, MachineRepresentation::kTagged); |
694 | 734 |
695 var_flags.Bind(flags); | 735 var_flags.Bind(flags); |
696 var_pattern.Bind(pattern); | 736 var_pattern.Bind(pattern); |
697 var_new_target.Bind(new_target); | 737 var_new_target.Bind(new_target); |
698 | 738 |
699 Node* const native_context = LoadNativeContext(context); | 739 Node* const native_context = LoadNativeContext(context); |
700 Node* const regexp_function = | 740 Node* const regexp_function = |
701 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 741 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
702 | 742 |
703 Node* const pattern_is_regexp = IsRegExp(this, context, pattern); | 743 Node* const pattern_is_regexp = IsRegExp(context, pattern); |
704 | 744 |
705 { | 745 { |
706 CLabel next(this); | 746 Label next(this); |
707 | 747 |
708 GotoUnless(IsUndefined(new_target), &next); | 748 GotoUnless(IsUndefined(new_target), &next); |
709 var_new_target.Bind(regexp_function); | 749 var_new_target.Bind(regexp_function); |
710 | 750 |
711 GotoUnless(pattern_is_regexp, &next); | 751 GotoUnless(pattern_is_regexp, &next); |
712 GotoUnless(IsUndefined(flags), &next); | 752 GotoUnless(IsUndefined(flags), &next); |
713 | 753 |
714 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 754 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
715 Node* const name = HeapConstant(isolate->factory()->constructor_string()); | 755 Node* const name = HeapConstant(isolate->factory()->constructor_string()); |
716 Node* const value = CallStub(getproperty_callable, context, pattern, name); | 756 Node* const value = CallStub(getproperty_callable, context, pattern, name); |
717 | 757 |
718 GotoUnless(WordEqual(value, regexp_function), &next); | 758 GotoUnless(WordEqual(value, regexp_function), &next); |
719 Return(pattern); | 759 Return(pattern); |
720 | 760 |
721 Bind(&next); | 761 Bind(&next); |
722 } | 762 } |
723 | 763 |
724 { | 764 { |
725 CLabel next(this), if_patternisfastregexp(this), | 765 Label next(this), if_patternisfastregexp(this), |
726 if_patternisslowregexp(this); | 766 if_patternisslowregexp(this); |
727 GotoIf(TaggedIsSmi(pattern), &next); | 767 GotoIf(TaggedIsSmi(pattern), &next); |
728 | 768 |
729 GotoIf(HasInstanceType(pattern, JS_REGEXP_TYPE), &if_patternisfastregexp); | 769 GotoIf(HasInstanceType(pattern, JS_REGEXP_TYPE), &if_patternisfastregexp); |
730 | 770 |
731 Branch(pattern_is_regexp, &if_patternisslowregexp, &next); | 771 Branch(pattern_is_regexp, &if_patternisslowregexp, &next); |
732 | 772 |
733 Bind(&if_patternisfastregexp); | 773 Bind(&if_patternisfastregexp); |
734 { | 774 { |
735 Node* const source = LoadObjectField(pattern, JSRegExp::kSourceOffset); | 775 Node* const source = LoadObjectField(pattern, JSRegExp::kSourceOffset); |
736 var_pattern.Bind(source); | 776 var_pattern.Bind(source); |
737 | 777 |
738 { | 778 { |
739 CLabel inner_next(this); | 779 Label inner_next(this); |
740 GotoUnless(IsUndefined(flags), &inner_next); | 780 GotoUnless(IsUndefined(flags), &inner_next); |
741 | 781 |
742 Node* const value = FlagsGetter(this, pattern, context, true); | 782 Node* const value = FlagsGetter(context, pattern, true); |
743 var_flags.Bind(value); | 783 var_flags.Bind(value); |
744 Goto(&inner_next); | 784 Goto(&inner_next); |
745 | 785 |
746 Bind(&inner_next); | 786 Bind(&inner_next); |
747 } | 787 } |
748 | 788 |
749 Goto(&next); | 789 Goto(&next); |
750 } | 790 } |
751 | 791 |
752 Bind(&if_patternisslowregexp); | 792 Bind(&if_patternisslowregexp); |
753 { | 793 { |
754 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 794 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
755 | 795 |
756 { | 796 { |
757 Node* const name = HeapConstant(isolate->factory()->source_string()); | 797 Node* const name = HeapConstant(isolate->factory()->source_string()); |
758 Node* const value = | 798 Node* const value = |
759 CallStub(getproperty_callable, context, pattern, name); | 799 CallStub(getproperty_callable, context, pattern, name); |
760 var_pattern.Bind(value); | 800 var_pattern.Bind(value); |
761 } | 801 } |
762 | 802 |
763 { | 803 { |
764 CLabel inner_next(this); | 804 Label inner_next(this); |
765 GotoUnless(IsUndefined(flags), &inner_next); | 805 GotoUnless(IsUndefined(flags), &inner_next); |
766 | 806 |
767 Node* const name = HeapConstant(isolate->factory()->flags_string()); | 807 Node* const name = HeapConstant(isolate->factory()->flags_string()); |
768 Node* const value = | 808 Node* const value = |
769 CallStub(getproperty_callable, context, pattern, name); | 809 CallStub(getproperty_callable, context, pattern, name); |
770 var_flags.Bind(value); | 810 var_flags.Bind(value); |
771 Goto(&inner_next); | 811 Goto(&inner_next); |
772 | 812 |
773 Bind(&inner_next); | 813 Bind(&inner_next); |
774 } | 814 } |
775 | 815 |
776 Goto(&next); | 816 Goto(&next); |
777 } | 817 } |
778 | 818 |
779 Bind(&next); | 819 Bind(&next); |
780 } | 820 } |
781 | 821 |
782 // Allocate. | 822 // Allocate. |
783 | 823 |
784 CVariable var_regexp(this, MachineRepresentation::kTagged); | 824 Variable var_regexp(this, MachineRepresentation::kTagged); |
785 { | 825 { |
786 CLabel allocate_jsregexp(this), allocate_generic(this, CLabel::kDeferred), | 826 Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred), |
787 next(this); | 827 next(this); |
788 Branch(WordEqual(var_new_target.value(), regexp_function), | 828 Branch(WordEqual(var_new_target.value(), regexp_function), |
789 &allocate_jsregexp, &allocate_generic); | 829 &allocate_jsregexp, &allocate_generic); |
790 | 830 |
791 Bind(&allocate_jsregexp); | 831 Bind(&allocate_jsregexp); |
792 { | 832 { |
793 Node* const initial_map = LoadObjectField( | 833 Node* const initial_map = LoadObjectField( |
794 regexp_function, JSFunction::kPrototypeOrInitialMapOffset); | 834 regexp_function, JSFunction::kPrototypeOrInitialMapOffset); |
795 Node* const regexp = AllocateJSObjectFromMap(initial_map); | 835 Node* const regexp = AllocateJSObjectFromMap(initial_map); |
796 var_regexp.Bind(regexp); | 836 var_regexp.Bind(regexp); |
797 Goto(&next); | 837 Goto(&next); |
798 } | 838 } |
799 | 839 |
800 Bind(&allocate_generic); | 840 Bind(&allocate_generic); |
801 { | 841 { |
802 Callable fastnewobject_callable = CodeFactory::FastNewObject(isolate); | 842 Callable fastnewobject_callable = CodeFactory::FastNewObject(isolate); |
803 Node* const regexp = CallStub(fastnewobject_callable, context, | 843 Node* const regexp = CallStub(fastnewobject_callable, context, |
804 regexp_function, var_new_target.value()); | 844 regexp_function, var_new_target.value()); |
805 var_regexp.Bind(regexp); | 845 var_regexp.Bind(regexp); |
806 Goto(&next); | 846 Goto(&next); |
807 } | 847 } |
808 | 848 |
809 Bind(&next); | 849 Bind(&next); |
810 } | 850 } |
811 | 851 |
812 Node* const result = RegExpInitialize(this, context, var_regexp.value(), | 852 Node* const result = RegExpInitialize(context, var_regexp.value(), |
813 var_pattern.value(), var_flags.value()); | 853 var_pattern.value(), var_flags.value()); |
814 Return(result); | 854 Return(result); |
815 } | 855 } |
816 | 856 |
817 // ES#sec-regexp.prototype.compile | 857 // ES#sec-regexp.prototype.compile |
818 // RegExp.prototype.compile ( pattern, flags ) | 858 // RegExp.prototype.compile ( pattern, flags ) |
819 TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) { | 859 TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) { |
820 Node* const maybe_receiver = Parameter(0); | 860 Node* const maybe_receiver = Parameter(0); |
821 Node* const maybe_pattern = Parameter(1); | 861 Node* const maybe_pattern = Parameter(1); |
822 Node* const maybe_flags = Parameter(2); | 862 Node* const maybe_flags = Parameter(2); |
823 Node* const context = Parameter(5); | 863 Node* const context = Parameter(5); |
824 | 864 |
825 ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE, | 865 ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE, |
826 "RegExp.prototype.compile"); | 866 "RegExp.prototype.compile"); |
827 Node* const receiver = maybe_receiver; | 867 Node* const receiver = maybe_receiver; |
828 | 868 |
829 CVariable var_flags(this, MachineRepresentation::kTagged); | 869 Variable var_flags(this, MachineRepresentation::kTagged); |
830 CVariable var_pattern(this, MachineRepresentation::kTagged); | 870 Variable var_pattern(this, MachineRepresentation::kTagged); |
831 | 871 |
832 var_flags.Bind(maybe_flags); | 872 var_flags.Bind(maybe_flags); |
833 var_pattern.Bind(maybe_pattern); | 873 var_pattern.Bind(maybe_pattern); |
834 | 874 |
835 // Handle a JSRegExp pattern. | 875 // Handle a JSRegExp pattern. |
836 { | 876 { |
837 CLabel next(this); | 877 Label next(this); |
838 | 878 |
839 GotoIf(TaggedIsSmi(maybe_pattern), &next); | 879 GotoIf(TaggedIsSmi(maybe_pattern), &next); |
840 GotoUnless(HasInstanceType(maybe_pattern, JS_REGEXP_TYPE), &next); | 880 GotoUnless(HasInstanceType(maybe_pattern, JS_REGEXP_TYPE), &next); |
841 | 881 |
842 Node* const pattern = maybe_pattern; | 882 Node* const pattern = maybe_pattern; |
843 | 883 |
844 // {maybe_flags} must be undefined in this case, otherwise throw. | 884 // {maybe_flags} must be undefined in this case, otherwise throw. |
845 { | 885 { |
846 CLabel next(this); | 886 Label next(this); |
847 GotoIf(IsUndefined(maybe_flags), &next); | 887 GotoIf(IsUndefined(maybe_flags), &next); |
848 | 888 |
849 Node* const message_id = SmiConstant(MessageTemplate::kRegExpFlags); | 889 Node* const message_id = SmiConstant(MessageTemplate::kRegExpFlags); |
850 TailCallRuntime(Runtime::kThrowTypeError, context, message_id); | 890 TailCallRuntime(Runtime::kThrowTypeError, context, message_id); |
851 | 891 |
852 Bind(&next); | 892 Bind(&next); |
853 } | 893 } |
854 | 894 |
855 Node* const new_flags = FlagsGetter(this, pattern, context, true); | 895 Node* const new_flags = FlagsGetter(context, pattern, true); |
856 Node* const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset); | 896 Node* const new_pattern = LoadObjectField(pattern, JSRegExp::kSourceOffset); |
857 | 897 |
858 var_flags.Bind(new_flags); | 898 var_flags.Bind(new_flags); |
859 var_pattern.Bind(new_pattern); | 899 var_pattern.Bind(new_pattern); |
860 | 900 |
861 Goto(&next); | 901 Goto(&next); |
862 Bind(&next); | 902 Bind(&next); |
863 } | 903 } |
864 | 904 |
865 RegExpInitialize(this, context, receiver, var_pattern.value(), | 905 RegExpInitialize(context, receiver, var_pattern.value(), var_flags.value()); |
866 var_flags.value()); | |
867 | 906 |
868 // Return undefined for compatibility with JSC. | 907 // Return undefined for compatibility with JSC. |
869 // See http://crbug.com/585775 for web compat details. | 908 // See http://crbug.com/585775 for web compat details. |
870 | 909 |
871 Return(UndefinedConstant()); | 910 Return(UndefinedConstant()); |
872 } | 911 } |
873 | 912 |
874 // ES6 21.2.5.10. | 913 // ES6 21.2.5.10. |
875 TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) { | 914 TF_BUILTIN(RegExpPrototypeSourceGetter, RegExpBuiltinsAssembler) { |
876 Node* const receiver = Parameter(0); | 915 Node* const receiver = Parameter(0); |
877 Node* const context = Parameter(3); | 916 Node* const context = Parameter(3); |
878 | 917 |
879 // Check whether we have an unmodified regexp instance. | 918 // Check whether we have an unmodified regexp instance. |
880 CLabel if_isjsregexp(this), if_isnotjsregexp(this, CLabel::kDeferred); | 919 Label if_isjsregexp(this), if_isnotjsregexp(this, Label::kDeferred); |
881 | 920 |
882 GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp); | 921 GotoIf(TaggedIsSmi(receiver), &if_isnotjsregexp); |
883 Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isjsregexp, | 922 Branch(HasInstanceType(receiver, JS_REGEXP_TYPE), &if_isjsregexp, |
884 &if_isnotjsregexp); | 923 &if_isnotjsregexp); |
885 | 924 |
886 Bind(&if_isjsregexp); | 925 Bind(&if_isjsregexp); |
887 { | 926 { |
888 Node* const source = LoadObjectField(receiver, JSRegExp::kSourceOffset); | 927 Node* const source = LoadObjectField(receiver, JSRegExp::kSourceOffset); |
889 Return(source); | 928 Return(source); |
890 } | 929 } |
891 | 930 |
892 Bind(&if_isnotjsregexp); | 931 Bind(&if_isnotjsregexp); |
893 { | 932 { |
894 Isolate* isolate = this->isolate(); | 933 Isolate* isolate = this->isolate(); |
895 Node* const native_context = LoadNativeContext(context); | 934 Node* const native_context = LoadNativeContext(context); |
896 Node* const regexp_fun = | 935 Node* const regexp_fun = |
897 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 936 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
898 Node* const initial_map = | 937 Node* const initial_map = |
899 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 938 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
900 Node* const initial_prototype = LoadMapPrototype(initial_map); | 939 Node* const initial_prototype = LoadMapPrototype(initial_map); |
901 | 940 |
902 CLabel if_isprototype(this), if_isnotprototype(this); | 941 Label if_isprototype(this), if_isnotprototype(this); |
903 Branch(WordEqual(receiver, initial_prototype), &if_isprototype, | 942 Branch(WordEqual(receiver, initial_prototype), &if_isprototype, |
904 &if_isnotprototype); | 943 &if_isnotprototype); |
905 | 944 |
906 Bind(&if_isprototype); | 945 Bind(&if_isprototype); |
907 { | 946 { |
908 const int counter = v8::Isolate::kRegExpPrototypeSourceGetter; | 947 const int counter = v8::Isolate::kRegExpPrototypeSourceGetter; |
909 Node* const counter_smi = SmiConstant(counter); | 948 Node* const counter_smi = SmiConstant(counter); |
910 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); | 949 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); |
911 | 950 |
912 Node* const result = | 951 Node* const result = |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
963 | 1002 |
964 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish()); | 1003 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish()); |
965 } | 1004 } |
966 | 1005 |
967 // ES6 21.2.4.2. | 1006 // ES6 21.2.4.2. |
968 TF_BUILTIN(RegExpPrototypeSpeciesGetter, RegExpBuiltinsAssembler) { | 1007 TF_BUILTIN(RegExpPrototypeSpeciesGetter, RegExpBuiltinsAssembler) { |
969 Node* const receiver = Parameter(0); | 1008 Node* const receiver = Parameter(0); |
970 Return(receiver); | 1009 Return(receiver); |
971 } | 1010 } |
972 | 1011 |
973 namespace { | |
974 | |
975 // Fast-path implementation for flag checks on an unmodified JSRegExp instance. | 1012 // Fast-path implementation for flag checks on an unmodified JSRegExp instance. |
976 Node* FastFlagGetter(CodeStubAssembler* a, Node* const regexp, | 1013 Node* RegExpBuiltinsAssembler::FastFlagGetter(Node* const regexp, |
977 JSRegExp::Flag flag) { | 1014 JSRegExp::Flag flag) { |
978 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 1015 Node* const smi_zero = SmiConstant(Smi::kZero); |
979 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); | 1016 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
980 Node* const mask = a->SmiConstant(Smi::FromInt(flag)); | 1017 Node* const mask = SmiConstant(Smi::FromInt(flag)); |
981 Node* const is_flag_set = a->WordNotEqual(a->WordAnd(flags, mask), smi_zero); | 1018 Node* const is_flag_set = WordNotEqual(WordAnd(flags, mask), smi_zero); |
982 | 1019 |
983 return is_flag_set; | 1020 return is_flag_set; |
984 } | 1021 } |
985 | 1022 |
986 // Load through the GetProperty stub. | 1023 // Load through the GetProperty stub. |
987 compiler::Node* SlowFlagGetter(CodeStubAssembler* a, | 1024 Node* RegExpBuiltinsAssembler::SlowFlagGetter(Node* const context, |
988 compiler::Node* const context, | 1025 Node* const regexp, |
989 compiler::Node* const regexp, | 1026 JSRegExp::Flag flag) { |
990 JSRegExp::Flag flag) { | 1027 Factory* factory = isolate()->factory(); |
991 Factory* factory = a->isolate()->factory(); | |
992 | 1028 |
993 CLabel out(a); | 1029 Label out(this); |
994 CVariable var_result(a, MachineType::PointerRepresentation()); | 1030 Variable var_result(this, MachineType::PointerRepresentation()); |
995 | 1031 |
996 Node* name; | 1032 Node* name; |
997 | 1033 |
998 switch (flag) { | 1034 switch (flag) { |
999 case JSRegExp::kGlobal: | 1035 case JSRegExp::kGlobal: |
1000 name = a->HeapConstant(factory->global_string()); | 1036 name = HeapConstant(factory->global_string()); |
1001 break; | 1037 break; |
1002 case JSRegExp::kIgnoreCase: | 1038 case JSRegExp::kIgnoreCase: |
1003 name = a->HeapConstant(factory->ignoreCase_string()); | 1039 name = HeapConstant(factory->ignoreCase_string()); |
1004 break; | 1040 break; |
1005 case JSRegExp::kMultiline: | 1041 case JSRegExp::kMultiline: |
1006 name = a->HeapConstant(factory->multiline_string()); | 1042 name = HeapConstant(factory->multiline_string()); |
1007 break; | 1043 break; |
1008 case JSRegExp::kSticky: | 1044 case JSRegExp::kSticky: |
1009 name = a->HeapConstant(factory->sticky_string()); | 1045 name = HeapConstant(factory->sticky_string()); |
1010 break; | 1046 break; |
1011 case JSRegExp::kUnicode: | 1047 case JSRegExp::kUnicode: |
1012 name = a->HeapConstant(factory->unicode_string()); | 1048 name = HeapConstant(factory->unicode_string()); |
1013 break; | 1049 break; |
1014 default: | 1050 default: |
1015 UNREACHABLE(); | 1051 UNREACHABLE(); |
1016 } | 1052 } |
1017 | 1053 |
1018 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 1054 Callable getproperty_callable = CodeFactory::GetProperty(isolate()); |
1019 Node* const value = a->CallStub(getproperty_callable, context, regexp, name); | 1055 Node* const value = CallStub(getproperty_callable, context, regexp, name); |
1020 | 1056 |
1021 CLabel if_true(a), if_false(a); | 1057 Label if_true(this), if_false(this); |
1022 a->BranchIfToBooleanIsTrue(value, &if_true, &if_false); | 1058 BranchIfToBooleanIsTrue(value, &if_true, &if_false); |
1023 | 1059 |
1024 a->Bind(&if_true); | 1060 Bind(&if_true); |
1025 { | 1061 { |
1026 var_result.Bind(a->IntPtrConstant(1)); | 1062 var_result.Bind(IntPtrConstant(1)); |
1027 a->Goto(&out); | 1063 Goto(&out); |
1028 } | 1064 } |
1029 | 1065 |
1030 a->Bind(&if_false); | 1066 Bind(&if_false); |
1031 { | 1067 { |
1032 var_result.Bind(a->IntPtrConstant(0)); | 1068 var_result.Bind(IntPtrConstant(0)); |
1033 a->Goto(&out); | 1069 Goto(&out); |
1034 } | 1070 } |
1035 | 1071 |
1036 a->Bind(&out); | 1072 Bind(&out); |
1037 return var_result.value(); | 1073 return var_result.value(); |
1038 } | 1074 } |
1039 | 1075 |
1040 compiler::Node* FlagGetter(CodeStubAssembler* a, compiler::Node* const context, | 1076 Node* RegExpBuiltinsAssembler::FlagGetter(Node* const context, |
1041 compiler::Node* const regexp, JSRegExp::Flag flag, | 1077 Node* const regexp, |
1042 bool is_fastpath) { | 1078 JSRegExp::Flag flag, |
1043 return is_fastpath ? FastFlagGetter(a, regexp, flag) | 1079 bool is_fastpath) { |
1044 : SlowFlagGetter(a, context, regexp, flag); | 1080 return is_fastpath ? FastFlagGetter(regexp, flag) |
1081 : SlowFlagGetter(context, regexp, flag); | |
1045 } | 1082 } |
1046 | 1083 |
1047 void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag, | 1084 void RegExpBuiltinsAssembler::FlagGetter(JSRegExp::Flag flag, |
1048 v8::Isolate::UseCounterFeature counter, | 1085 v8::Isolate::UseCounterFeature counter, |
1049 const char* method_name) { | 1086 const char* method_name) { |
1050 Node* const receiver = a->Parameter(0); | 1087 Node* const receiver = Parameter(0); |
1051 Node* const context = a->Parameter(3); | 1088 Node* const context = Parameter(3); |
1052 | 1089 |
1053 Isolate* isolate = a->isolate(); | 1090 Isolate* isolate = this->isolate(); |
1054 | 1091 |
1055 // Check whether we have an unmodified regexp instance. | 1092 // Check whether we have an unmodified regexp instance. |
1056 CLabel if_isunmodifiedjsregexp(a), | 1093 Label if_isunmodifiedjsregexp(this), |
1057 if_isnotunmodifiedjsregexp(a, CLabel::kDeferred); | 1094 if_isnotunmodifiedjsregexp(this, Label::kDeferred); |
1058 | 1095 |
1059 a->GotoIf(a->TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp); | 1096 GotoIf(TaggedIsSmi(receiver), &if_isnotunmodifiedjsregexp); |
1060 | 1097 |
1061 Node* const receiver_map = a->LoadMap(receiver); | 1098 Node* const receiver_map = LoadMap(receiver); |
1062 Node* const instance_type = a->LoadMapInstanceType(receiver_map); | 1099 Node* const instance_type = LoadMapInstanceType(receiver_map); |
1063 | 1100 |
1064 a->Branch(a->Word32Equal(instance_type, a->Int32Constant(JS_REGEXP_TYPE)), | 1101 Branch(Word32Equal(instance_type, Int32Constant(JS_REGEXP_TYPE)), |
1065 &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp); | 1102 &if_isunmodifiedjsregexp, &if_isnotunmodifiedjsregexp); |
1066 | 1103 |
1067 a->Bind(&if_isunmodifiedjsregexp); | 1104 Bind(&if_isunmodifiedjsregexp); |
1068 { | 1105 { |
1069 // Refer to JSRegExp's flag property on the fast-path. | 1106 // Refer to JSRegExp's flag property on the fast-path. |
1070 Node* const is_flag_set = FastFlagGetter(a, receiver, flag); | 1107 Node* const is_flag_set = FastFlagGetter(receiver, flag); |
1071 a->Return(a->Select(is_flag_set, a->TrueConstant(), a->FalseConstant())); | 1108 Return(Select(is_flag_set, TrueConstant(), FalseConstant())); |
1072 } | 1109 } |
1073 | 1110 |
1074 a->Bind(&if_isnotunmodifiedjsregexp); | 1111 Bind(&if_isnotunmodifiedjsregexp); |
1075 { | 1112 { |
1076 Node* const native_context = a->LoadNativeContext(context); | 1113 Node* const native_context = LoadNativeContext(context); |
1077 Node* const regexp_fun = | 1114 Node* const regexp_fun = |
1078 a->LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); | 1115 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); |
1079 Node* const initial_map = a->LoadObjectField( | 1116 Node* const initial_map = |
1080 regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); | 1117 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); |
1081 Node* const initial_prototype = a->LoadMapPrototype(initial_map); | 1118 Node* const initial_prototype = LoadMapPrototype(initial_map); |
1082 | 1119 |
1083 CLabel if_isprototype(a), if_isnotprototype(a); | 1120 Label if_isprototype(this), if_isnotprototype(this); |
1084 a->Branch(a->WordEqual(receiver, initial_prototype), &if_isprototype, | 1121 Branch(WordEqual(receiver, initial_prototype), &if_isprototype, |
1085 &if_isnotprototype); | 1122 &if_isnotprototype); |
1086 | 1123 |
1087 a->Bind(&if_isprototype); | 1124 Bind(&if_isprototype); |
1088 { | 1125 { |
1089 Node* const counter_smi = a->SmiConstant(Smi::FromInt(counter)); | 1126 Node* const counter_smi = SmiConstant(Smi::FromInt(counter)); |
1090 a->CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); | 1127 CallRuntime(Runtime::kIncrementUseCounter, context, counter_smi); |
1091 a->Return(a->UndefinedConstant()); | 1128 Return(UndefinedConstant()); |
1092 } | 1129 } |
1093 | 1130 |
1094 a->Bind(&if_isnotprototype); | 1131 Bind(&if_isnotprototype); |
1095 { | 1132 { |
1096 Node* const message_id = | 1133 Node* const message_id = |
1097 a->SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp)); | 1134 SmiConstant(Smi::FromInt(MessageTemplate::kRegExpNonRegExp)); |
1098 Node* const method_name_str = a->HeapConstant( | 1135 Node* const method_name_str = HeapConstant( |
1099 isolate->factory()->NewStringFromAsciiChecked(method_name)); | 1136 isolate->factory()->NewStringFromAsciiChecked(method_name)); |
1100 a->CallRuntime(Runtime::kThrowTypeError, context, message_id, | 1137 CallRuntime(Runtime::kThrowTypeError, context, message_id, |
1101 method_name_str); | 1138 method_name_str); |
1102 a->Return(a->UndefinedConstant()); // Never reached. | 1139 Return(UndefinedConstant()); // Never reached. |
1103 } | 1140 } |
1104 } | 1141 } |
1105 } | 1142 } |
1106 | 1143 |
1107 } // namespace | |
1108 | |
1109 // ES6 21.2.5.4. | 1144 // ES6 21.2.5.4. |
1110 TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) { | 1145 TF_BUILTIN(RegExpPrototypeGlobalGetter, RegExpBuiltinsAssembler) { |
1111 Generate_FlagGetter(this, JSRegExp::kGlobal, | 1146 FlagGetter(JSRegExp::kGlobal, v8::Isolate::kRegExpPrototypeOldFlagGetter, |
1112 v8::Isolate::kRegExpPrototypeOldFlagGetter, | 1147 "RegExp.prototype.global"); |
1113 "RegExp.prototype.global"); | |
1114 } | 1148 } |
1115 | 1149 |
1116 // ES6 21.2.5.5. | 1150 // ES6 21.2.5.5. |
1117 TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) { | 1151 TF_BUILTIN(RegExpPrototypeIgnoreCaseGetter, RegExpBuiltinsAssembler) { |
1118 Generate_FlagGetter(this, JSRegExp::kIgnoreCase, | 1152 FlagGetter(JSRegExp::kIgnoreCase, v8::Isolate::kRegExpPrototypeOldFlagGetter, |
1119 v8::Isolate::kRegExpPrototypeOldFlagGetter, | 1153 "RegExp.prototype.ignoreCase"); |
1120 "RegExp.prototype.ignoreCase"); | |
1121 } | 1154 } |
1122 | 1155 |
1123 // ES6 21.2.5.7. | 1156 // ES6 21.2.5.7. |
1124 TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) { | 1157 TF_BUILTIN(RegExpPrototypeMultilineGetter, RegExpBuiltinsAssembler) { |
1125 Generate_FlagGetter(this, JSRegExp::kMultiline, | 1158 FlagGetter(JSRegExp::kMultiline, v8::Isolate::kRegExpPrototypeOldFlagGetter, |
1126 v8::Isolate::kRegExpPrototypeOldFlagGetter, | 1159 "RegExp.prototype.multiline"); |
1127 "RegExp.prototype.multiline"); | |
1128 } | 1160 } |
1129 | 1161 |
1130 // ES6 21.2.5.12. | 1162 // ES6 21.2.5.12. |
1131 TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) { | 1163 TF_BUILTIN(RegExpPrototypeStickyGetter, RegExpBuiltinsAssembler) { |
1132 Generate_FlagGetter(this, JSRegExp::kSticky, | 1164 FlagGetter(JSRegExp::kSticky, v8::Isolate::kRegExpPrototypeStickyGetter, |
1133 v8::Isolate::kRegExpPrototypeStickyGetter, | 1165 "RegExp.prototype.sticky"); |
1134 "RegExp.prototype.sticky"); | |
1135 } | 1166 } |
1136 | 1167 |
1137 // ES6 21.2.5.15. | 1168 // ES6 21.2.5.15. |
1138 TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) { | 1169 TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) { |
1139 Generate_FlagGetter(this, JSRegExp::kUnicode, | 1170 FlagGetter(JSRegExp::kUnicode, v8::Isolate::kRegExpPrototypeUnicodeGetter, |
1140 v8::Isolate::kRegExpPrototypeUnicodeGetter, | 1171 "RegExp.prototype.unicode"); |
1141 "RegExp.prototype.unicode"); | |
1142 } | 1172 } |
1143 | 1173 |
1144 // The properties $1..$9 are the first nine capturing substrings of the last | 1174 // The properties $1..$9 are the first nine capturing substrings of the last |
1145 // successful match, or ''. The function RegExpMakeCaptureGetter will be | 1175 // successful match, or ''. The function RegExpMakeCaptureGetter will be |
1146 // called with indices from 1 to 9. | 1176 // called with indices from 1 to 9. |
1147 #define DEFINE_CAPTURE_GETTER(i) \ | 1177 #define DEFINE_CAPTURE_GETTER(i) \ |
1148 BUILTIN(RegExpCapture##i##Getter) { \ | 1178 BUILTIN(RegExpCapture##i##Getter) { \ |
1149 HandleScope scope(isolate); \ | 1179 HandleScope scope(isolate); \ |
1150 return *RegExpUtils::GenericCaptureGetter( \ | 1180 return *RegExpUtils::GenericCaptureGetter( \ |
1151 isolate, isolate->regexp_last_match_info(), i); \ | 1181 isolate, isolate->regexp_last_match_info(), i); \ |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1217 | 1247 |
1218 BUILTIN(RegExpRightContextGetter) { | 1248 BUILTIN(RegExpRightContextGetter) { |
1219 HandleScope scope(isolate); | 1249 HandleScope scope(isolate); |
1220 Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info(); | 1250 Handle<RegExpMatchInfo> match_info = isolate->regexp_last_match_info(); |
1221 const int start_index = match_info->Capture(1); | 1251 const int start_index = match_info->Capture(1); |
1222 Handle<String> last_subject(match_info->LastSubject()); | 1252 Handle<String> last_subject(match_info->LastSubject()); |
1223 const int len = last_subject->length(); | 1253 const int len = last_subject->length(); |
1224 return *isolate->factory()->NewSubString(last_subject, start_index, len); | 1254 return *isolate->factory()->NewSubString(last_subject, start_index, len); |
1225 } | 1255 } |
1226 | 1256 |
1227 namespace { | 1257 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) |
1258 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, | |
1259 Node* string) { | |
1260 Isolate* isolate = this->isolate(); | |
1228 | 1261 |
1229 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) | 1262 Node* const null = NullConstant(); |
1230 Node* RegExpExec(CodeStubAssembler* a, Node* context, Node* recv, | |
1231 Node* string) { | |
1232 Isolate* isolate = a->isolate(); | |
1233 | 1263 |
1234 Node* const null = a->NullConstant(); | 1264 Variable var_result(this, MachineRepresentation::kTagged); |
1265 Label out(this), if_isfastpath(this), if_isslowpath(this); | |
1235 | 1266 |
1236 CVariable var_result(a, MachineRepresentation::kTagged); | 1267 Node* const map = LoadMap(regexp); |
1237 CLabel out(a), if_isfastpath(a), if_isslowpath(a); | 1268 BranchIfFastRegExp(context, map, &if_isfastpath, &if_isslowpath); |
1238 | 1269 |
1239 Node* const map = a->LoadMap(recv); | 1270 Bind(&if_isfastpath); |
1240 BranchIfFastPath(a, context, map, &if_isfastpath, &if_isslowpath); | |
1241 | |
1242 a->Bind(&if_isfastpath); | |
1243 { | 1271 { |
1244 Node* const result = | 1272 Node* const result = RegExpPrototypeExecBody(context, regexp, string, true); |
1245 RegExpPrototypeExecBody(a, context, recv, string, true); | |
1246 var_result.Bind(result); | 1273 var_result.Bind(result); |
1247 a->Goto(&out); | 1274 Goto(&out); |
1248 } | 1275 } |
1249 | 1276 |
1250 a->Bind(&if_isslowpath); | 1277 Bind(&if_isslowpath); |
1251 { | 1278 { |
1252 // Take the slow path of fetching the exec property, calling it, and | 1279 // Take the slow path of fetching the exec property, calling it, and |
1253 // verifying its return value. | 1280 // verifying its return value. |
1254 | 1281 |
1255 // Get the exec property. | 1282 // Get the exec property. |
1256 Node* const name = a->HeapConstant(isolate->factory()->exec_string()); | 1283 Node* const name = HeapConstant(isolate->factory()->exec_string()); |
1257 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 1284 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
1258 Node* const exec = a->CallStub(getproperty_callable, context, recv, name); | 1285 Node* const exec = CallStub(getproperty_callable, context, regexp, name); |
1259 | 1286 |
1260 // Is {exec} callable? | 1287 // Is {exec} callable? |
1261 CLabel if_iscallable(a), if_isnotcallable(a); | 1288 Label if_iscallable(this), if_isnotcallable(this); |
1262 | 1289 |
1263 a->GotoIf(a->TaggedIsSmi(exec), &if_isnotcallable); | 1290 GotoIf(TaggedIsSmi(exec), &if_isnotcallable); |
1264 | 1291 |
1265 Node* const exec_map = a->LoadMap(exec); | 1292 Node* const exec_map = LoadMap(exec); |
1266 a->Branch(a->IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable); | 1293 Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable); |
1267 | 1294 |
1268 a->Bind(&if_iscallable); | 1295 Bind(&if_iscallable); |
1269 { | 1296 { |
1270 Callable call_callable = CodeFactory::Call(isolate); | 1297 Callable call_callable = CodeFactory::Call(isolate); |
1271 Node* const result = | 1298 Node* const result = CallJS(call_callable, context, exec, regexp, string); |
1272 a->CallJS(call_callable, context, exec, recv, string); | |
1273 | 1299 |
1274 var_result.Bind(result); | 1300 var_result.Bind(result); |
1275 a->GotoIf(a->WordEqual(result, null), &out); | 1301 GotoIf(WordEqual(result, null), &out); |
1276 | 1302 |
1277 ThrowIfNotJSReceiver(a, isolate, context, result, | 1303 ThrowIfNotJSReceiver(context, result, |
1278 MessageTemplate::kInvalidRegExpExecResult, "unused"); | 1304 MessageTemplate::kInvalidRegExpExecResult, "unused"); |
1279 | 1305 |
1280 a->Goto(&out); | 1306 Goto(&out); |
1281 } | 1307 } |
1282 | 1308 |
1283 a->Bind(&if_isnotcallable); | 1309 Bind(&if_isnotcallable); |
1284 { | 1310 { |
1285 a->ThrowIfNotInstanceType(context, recv, JS_REGEXP_TYPE, | 1311 ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE, |
1286 "RegExp.prototype.exec"); | 1312 "RegExp.prototype.exec"); |
1287 | 1313 |
1288 Node* const result = | 1314 Node* const result = |
1289 RegExpPrototypeExecBody(a, context, recv, string, false); | 1315 RegExpPrototypeExecBody(context, regexp, string, false); |
1290 var_result.Bind(result); | 1316 var_result.Bind(result); |
1291 a->Goto(&out); | 1317 Goto(&out); |
1292 } | 1318 } |
1293 } | 1319 } |
1294 | 1320 |
1295 a->Bind(&out); | 1321 Bind(&out); |
1296 return var_result.value(); | 1322 return var_result.value(); |
1297 } | 1323 } |
1298 | 1324 |
1299 } // namespace | |
1300 | |
1301 // ES#sec-regexp.prototype.test | 1325 // ES#sec-regexp.prototype.test |
1302 // RegExp.prototype.test ( S ) | 1326 // RegExp.prototype.test ( S ) |
1303 TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) { | 1327 TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) { |
1304 Isolate* const isolate = this->isolate(); | |
1305 | |
1306 Node* const maybe_receiver = Parameter(0); | 1328 Node* const maybe_receiver = Parameter(0); |
1307 Node* const maybe_string = Parameter(1); | 1329 Node* const maybe_string = Parameter(1); |
1308 Node* const context = Parameter(4); | 1330 Node* const context = Parameter(4); |
1309 | 1331 |
1310 // Ensure {maybe_receiver} is a JSReceiver. | 1332 // Ensure {maybe_receiver} is a JSReceiver. |
1311 Node* const map = ThrowIfNotJSReceiver( | 1333 Node* const map = ThrowIfNotJSReceiver( |
1312 this, isolate, context, maybe_receiver, | 1334 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
1313 MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.test"); | 1335 "RegExp.prototype.test"); |
1314 Node* const receiver = maybe_receiver; | 1336 Node* const receiver = maybe_receiver; |
1315 | 1337 |
1316 // Convert {maybe_string} to a String. | 1338 // Convert {maybe_string} to a String. |
1317 Node* const string = ToString(context, maybe_string); | 1339 Node* const string = ToString(context, maybe_string); |
1318 | 1340 |
1319 CLabel fast_path(this), slow_path(this); | 1341 Label fast_path(this), slow_path(this); |
1320 BranchIfFastPath(this, context, map, &fast_path, &slow_path); | 1342 BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
1321 | 1343 |
1322 Bind(&fast_path); | 1344 Bind(&fast_path); |
1323 { | 1345 { |
1324 CLabel if_didnotmatch(this); | 1346 Label if_didnotmatch(this); |
1325 RegExpPrototypeExecBodyWithoutResult(this, context, receiver, string, | 1347 RegExpPrototypeExecBodyWithoutResult(context, receiver, string, |
1326 &if_didnotmatch, true); | 1348 &if_didnotmatch, true); |
1327 Return(TrueConstant()); | 1349 Return(TrueConstant()); |
1328 | 1350 |
1329 Bind(&if_didnotmatch); | 1351 Bind(&if_didnotmatch); |
1330 Return(FalseConstant()); | 1352 Return(FalseConstant()); |
1331 } | 1353 } |
1332 | 1354 |
1333 Bind(&slow_path); | 1355 Bind(&slow_path); |
1334 { | 1356 { |
1335 // Call exec. | 1357 // Call exec. |
1336 Node* const match_indices = RegExpExec(this, context, receiver, string); | 1358 Node* const match_indices = RegExpExec(context, receiver, string); |
1337 | 1359 |
1338 // Return true iff exec matched successfully. | 1360 // Return true iff exec matched successfully. |
1339 Node* const result = Select(WordEqual(match_indices, NullConstant()), | 1361 Node* const result = Select(WordEqual(match_indices, NullConstant()), |
1340 FalseConstant(), TrueConstant()); | 1362 FalseConstant(), TrueConstant()); |
1341 Return(result); | 1363 Return(result); |
1342 } | 1364 } |
1343 } | 1365 } |
1344 | 1366 |
1367 Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node* const string, | |
1368 Node* const index, | |
1369 Node* const is_unicode) { | |
1370 Variable var_result(this, MachineRepresentation::kTagged); | |
1371 | |
1372 // Default to last_index + 1. | |
1373 Node* const index_plus_one = SmiAdd(index, SmiConstant(1)); | |
1374 var_result.Bind(index_plus_one); | |
1375 | |
1376 Label if_isunicode(this), out(this); | |
1377 Branch(is_unicode, &if_isunicode, &out); | |
1378 | |
1379 Bind(&if_isunicode); | |
1380 { | |
1381 Node* const string_length = LoadStringLength(string); | |
1382 GotoUnless(SmiLessThan(index_plus_one, string_length), &out); | |
1383 | |
1384 Node* const lead = StringCharCodeAt(string, index); | |
1385 GotoUnless(Word32Equal(Word32And(lead, Int32Constant(0xFC00)), | |
1386 Int32Constant(0xD800)), | |
1387 &out); | |
1388 | |
1389 Node* const trail = StringCharCodeAt(string, index_plus_one); | |
1390 GotoUnless(Word32Equal(Word32And(trail, Int32Constant(0xFC00)), | |
1391 Int32Constant(0xDC00)), | |
1392 &out); | |
1393 | |
1394 // At a surrogate pair, return index + 2. | |
1395 Node* const index_plus_two = SmiAdd(index, SmiConstant(2)); | |
1396 var_result.Bind(index_plus_two); | |
1397 | |
1398 Goto(&out); | |
1399 } | |
1400 | |
1401 Bind(&out); | |
1402 return var_result.value(); | |
1403 } | |
1404 | |
1345 namespace { | 1405 namespace { |
1346 | 1406 |
1347 Node* AdvanceStringIndex(CodeStubAssembler* a, Node* const string, | |
1348 Node* const index, Node* const is_unicode) { | |
1349 CVariable var_result(a, MachineRepresentation::kTagged); | |
1350 | |
1351 // Default to last_index + 1. | |
1352 Node* const index_plus_one = a->SmiAdd(index, a->SmiConstant(1)); | |
1353 var_result.Bind(index_plus_one); | |
1354 | |
1355 CLabel if_isunicode(a), out(a); | |
1356 a->Branch(is_unicode, &if_isunicode, &out); | |
1357 | |
1358 a->Bind(&if_isunicode); | |
1359 { | |
1360 Node* const string_length = a->LoadStringLength(string); | |
1361 a->GotoUnless(a->SmiLessThan(index_plus_one, string_length), &out); | |
1362 | |
1363 Node* const lead = a->StringCharCodeAt(string, index); | |
1364 a->GotoUnless(a->Word32Equal(a->Word32And(lead, a->Int32Constant(0xFC00)), | |
1365 a->Int32Constant(0xD800)), | |
1366 &out); | |
1367 | |
1368 Node* const trail = a->StringCharCodeAt(string, index_plus_one); | |
1369 a->GotoUnless(a->Word32Equal(a->Word32And(trail, a->Int32Constant(0xFC00)), | |
1370 a->Int32Constant(0xDC00)), | |
1371 &out); | |
1372 | |
1373 // At a surrogate pair, return index + 2. | |
1374 Node* const index_plus_two = a->SmiAdd(index, a->SmiConstant(2)); | |
1375 var_result.Bind(index_plus_two); | |
1376 | |
1377 a->Goto(&out); | |
1378 } | |
1379 | |
1380 a->Bind(&out); | |
1381 return var_result.value(); | |
1382 } | |
1383 | |
1384 // Utility class implementing a growable fixed array through CSA. | 1407 // Utility class implementing a growable fixed array through CSA. |
1385 class GrowableFixedArray { | 1408 class GrowableFixedArray { |
1409 typedef CodeStubAssembler::Label Label; | |
1410 typedef CodeStubAssembler::Variable Variable; | |
1411 | |
1386 public: | 1412 public: |
1387 explicit GrowableFixedArray(CodeStubAssembler* a) | 1413 explicit GrowableFixedArray(CodeStubAssembler* a) |
1388 : assembler_(a), | 1414 : assembler_(a), |
1389 var_array_(a, MachineRepresentation::kTagged), | 1415 var_array_(a, MachineRepresentation::kTagged), |
1390 var_length_(a, MachineType::PointerRepresentation()), | 1416 var_length_(a, MachineType::PointerRepresentation()), |
1391 var_capacity_(a, MachineType::PointerRepresentation()) { | 1417 var_capacity_(a, MachineType::PointerRepresentation()) { |
1392 Initialize(); | 1418 Initialize(); |
1393 } | 1419 } |
1394 | 1420 |
1395 Node* length() const { return var_length_.value(); } | 1421 Node* length() const { return var_length_.value(); } |
1396 | 1422 |
1397 CVariable* var_array() { return &var_array_; } | 1423 Variable* var_array() { return &var_array_; } |
1398 CVariable* var_length() { return &var_length_; } | 1424 Variable* var_length() { return &var_length_; } |
1399 CVariable* var_capacity() { return &var_capacity_; } | 1425 Variable* var_capacity() { return &var_capacity_; } |
1400 | 1426 |
1401 void Push(Node* const value) { | 1427 void Push(Node* const value) { |
1402 CodeStubAssembler* a = assembler_; | 1428 CodeStubAssembler* a = assembler_; |
1403 | 1429 |
1404 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; | 1430 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; |
1405 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | 1431 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
1406 | 1432 |
1407 Node* const length = var_length_.value(); | 1433 Node* const length = var_length_.value(); |
1408 Node* const capacity = var_capacity_.value(); | 1434 Node* const capacity = var_capacity_.value(); |
1409 | 1435 |
1410 CLabel grow(a), store(a); | 1436 Label grow(a), store(a); |
1411 a->Branch(a->IntPtrEqual(capacity, length), &grow, &store); | 1437 a->Branch(a->IntPtrEqual(capacity, length), &grow, &store); |
1412 | 1438 |
1413 a->Bind(&grow); | 1439 a->Bind(&grow); |
1414 { | 1440 { |
1415 Node* const new_capacity = NewCapacity(a, capacity); | 1441 Node* const new_capacity = NewCapacity(a, capacity); |
1416 Node* const new_array = GrowFixedArray(capacity, new_capacity, mode); | 1442 Node* const new_array = GrowFixedArray(capacity, new_capacity, mode); |
1417 | 1443 |
1418 var_capacity_.Bind(new_capacity); | 1444 var_capacity_.Bind(new_capacity); |
1419 var_array_.Bind(new_array); | 1445 var_array_.Bind(new_array); |
1420 a->Goto(&store); | 1446 a->Goto(&store); |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1497 Node* const to_array = a->AllocateFixedArray(kind, new_capacity, mode); | 1523 Node* const to_array = a->AllocateFixedArray(kind, new_capacity, mode); |
1498 a->CopyFixedArrayElements(kind, from_array, kind, to_array, | 1524 a->CopyFixedArrayElements(kind, from_array, kind, to_array, |
1499 current_capacity, new_capacity, barrier_mode, | 1525 current_capacity, new_capacity, barrier_mode, |
1500 mode); | 1526 mode); |
1501 | 1527 |
1502 return to_array; | 1528 return to_array; |
1503 } | 1529 } |
1504 | 1530 |
1505 private: | 1531 private: |
1506 CodeStubAssembler* const assembler_; | 1532 CodeStubAssembler* const assembler_; |
1507 CVariable var_array_; | 1533 Variable var_array_; |
1508 CVariable var_length_; | 1534 Variable var_length_; |
1509 CVariable var_capacity_; | 1535 Variable var_capacity_; |
1510 }; | 1536 }; |
1511 | 1537 |
1512 void RegExpPrototypeMatchBody(CodeStubAssembler* a, Node* const receiver, | 1538 } // namespace |
1513 Node* const string, Node* const context, | |
1514 const bool is_fastpath) { | |
1515 Isolate* const isolate = a->isolate(); | |
1516 | 1539 |
1517 Node* const null = a->NullConstant(); | 1540 void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context, |
1518 Node* const int_zero = a->IntPtrConstant(0); | 1541 Node* const regexp, |
1519 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 1542 Node* const string, |
1543 const bool is_fastpath) { | |
1544 Isolate* const isolate = this->isolate(); | |
1520 | 1545 |
1521 Node* const regexp = receiver; | 1546 Node* const null = NullConstant(); |
1547 Node* const int_zero = IntPtrConstant(0); | |
1548 Node* const smi_zero = SmiConstant(Smi::kZero); | |
1549 | |
1522 Node* const is_global = | 1550 Node* const is_global = |
1523 FlagGetter(a, context, regexp, JSRegExp::kGlobal, is_fastpath); | 1551 FlagGetter(context, regexp, JSRegExp::kGlobal, is_fastpath); |
1524 | 1552 |
1525 CLabel if_isglobal(a), if_isnotglobal(a); | 1553 Label if_isglobal(this), if_isnotglobal(this); |
1526 a->Branch(is_global, &if_isglobal, &if_isnotglobal); | 1554 Branch(is_global, &if_isglobal, &if_isnotglobal); |
1527 | 1555 |
1528 a->Bind(&if_isnotglobal); | 1556 Bind(&if_isnotglobal); |
1529 { | 1557 { |
1530 Node* const result = | 1558 Node* const result = |
1531 is_fastpath ? RegExpPrototypeExecBody(a, context, regexp, string, true) | 1559 is_fastpath ? RegExpPrototypeExecBody(context, regexp, string, true) |
1532 : RegExpExec(a, context, regexp, string); | 1560 : RegExpExec(context, regexp, string); |
1533 a->Return(result); | 1561 Return(result); |
1534 } | 1562 } |
1535 | 1563 |
1536 a->Bind(&if_isglobal); | 1564 Bind(&if_isglobal); |
1537 { | 1565 { |
1538 Node* const is_unicode = | 1566 Node* const is_unicode = |
1539 FlagGetter(a, context, regexp, JSRegExp::kUnicode, is_fastpath); | 1567 FlagGetter(context, regexp, JSRegExp::kUnicode, is_fastpath); |
1540 | 1568 |
1541 StoreLastIndex(a, context, regexp, smi_zero, is_fastpath); | 1569 StoreLastIndex(context, regexp, smi_zero, is_fastpath); |
1542 | 1570 |
1543 // Allocate an array to store the resulting match strings. | 1571 // Allocate an array to store the resulting match strings. |
1544 | 1572 |
1545 GrowableFixedArray array(a); | 1573 GrowableFixedArray array(this); |
1546 | 1574 |
1547 // Loop preparations. Within the loop, collect results from RegExpExec | 1575 // Loop preparations. Within the loop, collect results from RegExpExec |
1548 // and store match strings in the array. | 1576 // and store match strings in the array. |
1549 | 1577 |
1550 CVariable* vars[] = {array.var_array(), array.var_length(), | 1578 Variable* vars[] = {array.var_array(), array.var_length(), |
1551 array.var_capacity()}; | 1579 array.var_capacity()}; |
1552 CLabel loop(a, 3, vars), out(a); | 1580 Label loop(this, 3, vars), out(this); |
1553 a->Goto(&loop); | 1581 Goto(&loop); |
1554 | 1582 |
1555 a->Bind(&loop); | 1583 Bind(&loop); |
1556 { | 1584 { |
1557 CVariable var_match(a, MachineRepresentation::kTagged); | 1585 Variable var_match(this, MachineRepresentation::kTagged); |
1558 | 1586 |
1559 CLabel if_didmatch(a), if_didnotmatch(a); | 1587 Label if_didmatch(this), if_didnotmatch(this); |
1560 if (is_fastpath) { | 1588 if (is_fastpath) { |
1561 // On the fast path, grab the matching string from the raw match index | 1589 // On the fast path, grab the matching string from the raw match index |
1562 // array. | 1590 // array. |
1563 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( | 1591 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( |
1564 a, context, regexp, string, &if_didnotmatch, true); | 1592 context, regexp, string, &if_didnotmatch, true); |
1565 | 1593 |
1566 Node* const match_from = a->LoadFixedArrayElement( | 1594 Node* const match_from = LoadFixedArrayElement( |
1567 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 1595 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
1568 Node* const match_to = a->LoadFixedArrayElement( | 1596 Node* const match_to = LoadFixedArrayElement( |
1569 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); | 1597 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
1570 | 1598 |
1571 Node* match = a->SubString(context, string, match_from, match_to); | 1599 Node* match = SubString(context, string, match_from, match_to); |
1572 var_match.Bind(match); | 1600 var_match.Bind(match); |
1573 | 1601 |
1574 a->Goto(&if_didmatch); | 1602 Goto(&if_didmatch); |
1575 } else { | 1603 } else { |
1576 DCHECK(!is_fastpath); | 1604 DCHECK(!is_fastpath); |
1577 Node* const result = RegExpExec(a, context, regexp, string); | 1605 Node* const result = RegExpExec(context, regexp, string); |
1578 | 1606 |
1579 CLabel load_match(a); | 1607 Label load_match(this); |
1580 a->Branch(a->WordEqual(result, null), &if_didnotmatch, &load_match); | 1608 Branch(WordEqual(result, null), &if_didnotmatch, &load_match); |
1581 | 1609 |
1582 a->Bind(&load_match); | 1610 Bind(&load_match); |
1583 { | 1611 { |
1584 CLabel fast_result(a), slow_result(a); | 1612 Label fast_result(this), slow_result(this); |
1585 BranchIfFastRegExpResult(a, context, a->LoadMap(result), &fast_result, | 1613 BranchIfFastRegExpResult(context, LoadMap(result), &fast_result, |
1586 &slow_result); | 1614 &slow_result); |
1587 | 1615 |
1588 a->Bind(&fast_result); | 1616 Bind(&fast_result); |
1589 { | 1617 { |
1590 Node* const result_fixed_array = a->LoadElements(result); | 1618 Node* const result_fixed_array = LoadElements(result); |
1591 Node* const match = a->LoadFixedArrayElement(result_fixed_array, 0); | 1619 Node* const match = LoadFixedArrayElement(result_fixed_array, 0); |
1592 | 1620 |
1593 // The match is guaranteed to be a string on the fast path. | 1621 // The match is guaranteed to be a string on the fast path. |
1594 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(match))); | 1622 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(match))); |
1595 | 1623 |
1596 var_match.Bind(match); | 1624 var_match.Bind(match); |
1597 a->Goto(&if_didmatch); | 1625 Goto(&if_didmatch); |
1598 } | 1626 } |
1599 | 1627 |
1600 a->Bind(&slow_result); | 1628 Bind(&slow_result); |
1601 { | 1629 { |
1602 // TODO(ishell): Use GetElement stub once it's available. | 1630 // TODO(ishell): Use GetElement stub once it's available. |
1603 Node* const name = smi_zero; | 1631 Node* const name = smi_zero; |
1604 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 1632 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
1605 Node* const match = | 1633 Node* const match = |
1606 a->CallStub(getproperty_callable, context, result, name); | 1634 CallStub(getproperty_callable, context, result, name); |
1607 | 1635 |
1608 var_match.Bind(a->ToString(context, match)); | 1636 var_match.Bind(ToString(context, match)); |
1609 a->Goto(&if_didmatch); | 1637 Goto(&if_didmatch); |
1610 } | 1638 } |
1611 } | 1639 } |
1612 } | 1640 } |
1613 | 1641 |
1614 a->Bind(&if_didnotmatch); | 1642 Bind(&if_didnotmatch); |
1615 { | 1643 { |
1616 // Return null if there were no matches, otherwise just exit the loop. | 1644 // Return null if there were no matches, otherwise just exit the loop. |
1617 a->GotoUnless(a->IntPtrEqual(array.length(), int_zero), &out); | 1645 GotoUnless(IntPtrEqual(array.length(), int_zero), &out); |
1618 a->Return(null); | 1646 Return(null); |
1619 } | 1647 } |
1620 | 1648 |
1621 a->Bind(&if_didmatch); | 1649 Bind(&if_didmatch); |
1622 { | 1650 { |
1623 Node* match = var_match.value(); | 1651 Node* match = var_match.value(); |
1624 | 1652 |
1625 // Store the match, growing the fixed array if needed. | 1653 // Store the match, growing the fixed array if needed. |
1626 | 1654 |
1627 array.Push(match); | 1655 array.Push(match); |
1628 | 1656 |
1629 // Advance last index if the match is the empty string. | 1657 // Advance last index if the match is the empty string. |
1630 | 1658 |
1631 Node* const match_length = a->LoadStringLength(match); | 1659 Node* const match_length = LoadStringLength(match); |
1632 a->GotoUnless(a->SmiEqual(match_length, smi_zero), &loop); | 1660 GotoUnless(SmiEqual(match_length, smi_zero), &loop); |
1633 | 1661 |
1634 Node* last_index = LoadLastIndex(a, context, regexp, is_fastpath); | 1662 Node* last_index = LoadLastIndex(context, regexp, is_fastpath); |
1635 | 1663 |
1636 Callable tolength_callable = CodeFactory::ToLength(isolate); | 1664 Callable tolength_callable = CodeFactory::ToLength(isolate); |
1637 last_index = a->CallStub(tolength_callable, context, last_index); | 1665 last_index = CallStub(tolength_callable, context, last_index); |
1638 | 1666 |
1639 Node* const new_last_index = | 1667 Node* const new_last_index = |
1640 AdvanceStringIndex(a, string, last_index, is_unicode); | 1668 AdvanceStringIndex(string, last_index, is_unicode); |
1641 | 1669 |
1642 StoreLastIndex(a, context, regexp, new_last_index, is_fastpath); | 1670 StoreLastIndex(context, regexp, new_last_index, is_fastpath); |
1643 | 1671 |
1644 a->Goto(&loop); | 1672 Goto(&loop); |
1645 } | 1673 } |
1646 } | 1674 } |
1647 | 1675 |
1648 a->Bind(&out); | 1676 Bind(&out); |
1649 { | 1677 { |
1650 // Wrap the match in a JSArray. | 1678 // Wrap the match in a JSArray. |
1651 | 1679 |
1652 Node* const result = array.ToJSArray(context); | 1680 Node* const result = array.ToJSArray(context); |
1653 a->Return(result); | 1681 Return(result); |
1654 } | 1682 } |
1655 } | 1683 } |
1656 } | 1684 } |
1657 | 1685 |
1658 } // namespace | |
1659 | |
1660 // ES#sec-regexp.prototype-@@match | 1686 // ES#sec-regexp.prototype-@@match |
1661 // RegExp.prototype [ @@match ] ( string ) | 1687 // RegExp.prototype [ @@match ] ( string ) |
1662 TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) { | 1688 TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) { |
1663 Node* const maybe_receiver = Parameter(0); | 1689 Node* const maybe_receiver = Parameter(0); |
1664 Node* const maybe_string = Parameter(1); | 1690 Node* const maybe_string = Parameter(1); |
1665 Node* const context = Parameter(4); | 1691 Node* const context = Parameter(4); |
1666 | 1692 |
1667 // Ensure {maybe_receiver} is a JSReceiver. | 1693 // Ensure {maybe_receiver} is a JSReceiver. |
1668 Node* const map = ThrowIfNotJSReceiver( | 1694 Node* const map = ThrowIfNotJSReceiver( |
1669 this, isolate(), context, maybe_receiver, | 1695 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
1670 MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.@@match"); | 1696 "RegExp.prototype.@@match"); |
1671 Node* const receiver = maybe_receiver; | 1697 Node* const receiver = maybe_receiver; |
1672 | 1698 |
1673 // Convert {maybe_string} to a String. | 1699 // Convert {maybe_string} to a String. |
1674 Node* const string = ToString(context, maybe_string); | 1700 Node* const string = ToString(context, maybe_string); |
1675 | 1701 |
1676 CLabel fast_path(this), slow_path(this); | 1702 Label fast_path(this), slow_path(this); |
1677 BranchIfFastPath(this, context, map, &fast_path, &slow_path); | 1703 BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
1678 | 1704 |
1679 Bind(&fast_path); | 1705 Bind(&fast_path); |
1680 RegExpPrototypeMatchBody(this, receiver, string, context, true); | 1706 RegExpPrototypeMatchBody(context, receiver, string, true); |
1681 | 1707 |
1682 Bind(&slow_path); | 1708 Bind(&slow_path); |
1683 RegExpPrototypeMatchBody(this, receiver, string, context, false); | 1709 RegExpPrototypeMatchBody(context, receiver, string, false); |
1684 } | 1710 } |
1685 | 1711 |
1686 namespace { | 1712 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast( |
1687 | 1713 Node* const context, Node* const regexp, Node* const string) { |
1688 void RegExpPrototypeSearchBodyFast(CodeStubAssembler* a, Node* const receiver, | |
1689 Node* const string, Node* const context) { | |
1690 // Grab the initial value of last index. | 1714 // Grab the initial value of last index. |
1691 Node* const previous_last_index = FastLoadLastIndex(a, receiver); | 1715 Node* const previous_last_index = FastLoadLastIndex(regexp); |
1692 | 1716 |
1693 // Ensure last index is 0. | 1717 // Ensure last index is 0. |
1694 FastStoreLastIndex(a, receiver, a->SmiConstant(Smi::kZero)); | 1718 FastStoreLastIndex(regexp, SmiConstant(Smi::kZero)); |
1695 | 1719 |
1696 // Call exec. | 1720 // Call exec. |
1697 CLabel if_didnotmatch(a); | 1721 Label if_didnotmatch(this); |
1698 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( | 1722 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( |
1699 a, context, receiver, string, &if_didnotmatch, true); | 1723 context, regexp, string, &if_didnotmatch, true); |
1700 | 1724 |
1701 // Successful match. | 1725 // Successful match. |
1702 { | 1726 { |
1703 // Reset last index. | 1727 // Reset last index. |
1704 FastStoreLastIndex(a, receiver, previous_last_index); | 1728 FastStoreLastIndex(regexp, previous_last_index); |
1705 | 1729 |
1706 // Return the index of the match. | 1730 // Return the index of the match. |
1707 Node* const index = a->LoadFixedArrayElement( | 1731 Node* const index = LoadFixedArrayElement( |
1708 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 1732 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
1709 a->Return(index); | 1733 Return(index); |
1710 } | 1734 } |
1711 | 1735 |
1712 a->Bind(&if_didnotmatch); | 1736 Bind(&if_didnotmatch); |
1713 { | 1737 { |
1714 // Reset last index and return -1. | 1738 // Reset last index and return -1. |
1715 FastStoreLastIndex(a, receiver, previous_last_index); | 1739 FastStoreLastIndex(regexp, previous_last_index); |
1716 a->Return(a->SmiConstant(-1)); | 1740 Return(SmiConstant(-1)); |
1717 } | 1741 } |
1718 } | 1742 } |
1719 | 1743 |
1720 void RegExpPrototypeSearchBodySlow(CodeStubAssembler* a, Node* const receiver, | 1744 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow( |
1721 Node* const string, Node* const context) { | 1745 Node* const context, Node* const regexp, Node* const string) { |
1722 Isolate* const isolate = a->isolate(); | 1746 Isolate* const isolate = this->isolate(); |
1723 | 1747 |
1724 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 1748 Node* const smi_zero = SmiConstant(Smi::kZero); |
1725 | 1749 |
1726 // Grab the initial value of last index. | 1750 // Grab the initial value of last index. |
1727 Node* const previous_last_index = SlowLoadLastIndex(a, context, receiver); | 1751 Node* const previous_last_index = SlowLoadLastIndex(context, regexp); |
1728 | 1752 |
1729 // Ensure last index is 0. | 1753 // Ensure last index is 0. |
1730 { | 1754 { |
1731 CLabel next(a); | 1755 Label next(this); |
1732 a->GotoIf(a->SameValue(previous_last_index, smi_zero, context), &next); | 1756 GotoIf(SameValue(previous_last_index, smi_zero, context), &next); |
1733 | 1757 |
1734 SlowStoreLastIndex(a, context, receiver, smi_zero); | 1758 SlowStoreLastIndex(context, regexp, smi_zero); |
1735 a->Goto(&next); | 1759 Goto(&next); |
1736 a->Bind(&next); | 1760 Bind(&next); |
1737 } | 1761 } |
1738 | 1762 |
1739 // Call exec. | 1763 // Call exec. |
1740 Node* const exec_result = RegExpExec(a, context, receiver, string); | 1764 Node* const exec_result = RegExpExec(context, regexp, string); |
1741 | 1765 |
1742 // Reset last index if necessary. | 1766 // Reset last index if necessary. |
1743 { | 1767 { |
1744 CLabel next(a); | 1768 Label next(this); |
1745 Node* const current_last_index = SlowLoadLastIndex(a, context, receiver); | 1769 Node* const current_last_index = SlowLoadLastIndex(context, regexp); |
1746 | 1770 |
1747 a->GotoIf(a->SameValue(current_last_index, previous_last_index, context), | 1771 GotoIf(SameValue(current_last_index, previous_last_index, context), &next); |
1748 &next); | 1772 |
1749 | 1773 SlowStoreLastIndex(context, regexp, previous_last_index); |
1750 SlowStoreLastIndex(a, context, receiver, previous_last_index); | 1774 Goto(&next); |
1751 a->Goto(&next); | 1775 |
1752 | 1776 Bind(&next); |
1753 a->Bind(&next); | |
1754 } | 1777 } |
1755 | 1778 |
1756 // Return -1 if no match was found. | 1779 // Return -1 if no match was found. |
1757 { | 1780 { |
1758 CLabel next(a); | 1781 Label next(this); |
1759 a->GotoUnless(a->WordEqual(exec_result, a->NullConstant()), &next); | 1782 GotoUnless(WordEqual(exec_result, NullConstant()), &next); |
1760 a->Return(a->SmiConstant(-1)); | 1783 Return(SmiConstant(-1)); |
1761 a->Bind(&next); | 1784 Bind(&next); |
1762 } | 1785 } |
1763 | 1786 |
1764 // Return the index of the match. | 1787 // Return the index of the match. |
1765 { | 1788 { |
1766 CLabel fast_result(a), slow_result(a, CLabel::kDeferred); | 1789 Label fast_result(this), slow_result(this, Label::kDeferred); |
1767 BranchIfFastRegExpResult(a, context, a->LoadMap(exec_result), &fast_result, | 1790 BranchIfFastRegExpResult(context, LoadMap(exec_result), &fast_result, |
1768 &slow_result); | 1791 &slow_result); |
1769 | 1792 |
1770 a->Bind(&fast_result); | 1793 Bind(&fast_result); |
1771 { | 1794 { |
1772 Node* const index = | 1795 Node* const index = |
1773 a->LoadObjectField(exec_result, JSRegExpResult::kIndexOffset); | 1796 LoadObjectField(exec_result, JSRegExpResult::kIndexOffset); |
1774 a->Return(index); | 1797 Return(index); |
1775 } | 1798 } |
1776 | 1799 |
1777 a->Bind(&slow_result); | 1800 Bind(&slow_result); |
1778 { | 1801 { |
1779 Node* const name = a->HeapConstant(isolate->factory()->index_string()); | 1802 Node* const name = HeapConstant(isolate->factory()->index_string()); |
1780 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 1803 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
1781 Node* const index = | 1804 Node* const index = |
1782 a->CallStub(getproperty_callable, context, exec_result, name); | 1805 CallStub(getproperty_callable, context, exec_result, name); |
1783 a->Return(index); | 1806 Return(index); |
1784 } | 1807 } |
1785 } | 1808 } |
1786 } | 1809 } |
1787 | |
1788 } // namespace | |
1789 | 1810 |
1790 // ES#sec-regexp.prototype-@@search | 1811 // ES#sec-regexp.prototype-@@search |
1791 // RegExp.prototype [ @@search ] ( string ) | 1812 // RegExp.prototype [ @@search ] ( string ) |
1792 TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) { | 1813 TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) { |
1793 Isolate* const isolate = this->isolate(); | |
1794 | |
1795 Node* const maybe_receiver = Parameter(0); | 1814 Node* const maybe_receiver = Parameter(0); |
1796 Node* const maybe_string = Parameter(1); | 1815 Node* const maybe_string = Parameter(1); |
1797 Node* const context = Parameter(4); | 1816 Node* const context = Parameter(4); |
1798 | 1817 |
1799 // Ensure {maybe_receiver} is a JSReceiver. | 1818 // Ensure {maybe_receiver} is a JSReceiver. |
1800 Node* const map = | 1819 Node* const map = ThrowIfNotJSReceiver( |
1801 ThrowIfNotJSReceiver(this, isolate, context, maybe_receiver, | 1820 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
1802 MessageTemplate::kIncompatibleMethodReceiver, | 1821 "RegExp.prototype.@@search"); |
1803 "RegExp.prototype.@@search"); | |
1804 Node* const receiver = maybe_receiver; | 1822 Node* const receiver = maybe_receiver; |
1805 | 1823 |
1806 // Convert {maybe_string} to a String. | 1824 // Convert {maybe_string} to a String. |
1807 Node* const string = ToString(context, maybe_string); | 1825 Node* const string = ToString(context, maybe_string); |
1808 | 1826 |
1809 CLabel fast_path(this), slow_path(this); | 1827 Label fast_path(this), slow_path(this); |
1810 BranchIfFastPath(this, context, map, &fast_path, &slow_path); | 1828 BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
1811 | 1829 |
1812 Bind(&fast_path); | 1830 Bind(&fast_path); |
1813 RegExpPrototypeSearchBodyFast(this, receiver, string, context); | 1831 RegExpPrototypeSearchBodyFast(context, receiver, string); |
1814 | 1832 |
1815 Bind(&slow_path); | 1833 Bind(&slow_path); |
1816 RegExpPrototypeSearchBodySlow(this, receiver, string, context); | 1834 RegExpPrototypeSearchBodySlow(context, receiver, string); |
1817 } | 1835 } |
1818 | |
1819 namespace { | |
1820 | 1836 |
1821 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, | 1837 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, |
1822 // {string} is a String, and {limit} is a Smi. | 1838 // {string} is a String, and {limit} is a Smi. |
1823 void Generate_RegExpPrototypeSplitBody(CodeStubAssembler* a, Node* const regexp, | 1839 void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context, |
1824 Node* const string, Node* const limit, | 1840 Node* const regexp, |
1825 Node* const context) { | 1841 Node* const string, |
1826 Isolate* isolate = a->isolate(); | 1842 Node* const limit) { |
1827 | 1843 Isolate* isolate = this->isolate(); |
1828 Node* const null = a->NullConstant(); | 1844 |
1829 Node* const smi_zero = a->SmiConstant(0); | 1845 Node* const null = NullConstant(); |
1830 Node* const int_zero = a->IntPtrConstant(0); | 1846 Node* const smi_zero = SmiConstant(0); |
1831 Node* const int_limit = a->SmiUntag(limit); | 1847 Node* const int_zero = IntPtrConstant(0); |
1848 Node* const int_limit = SmiUntag(limit); | |
1832 | 1849 |
1833 const ElementsKind kind = FAST_ELEMENTS; | 1850 const ElementsKind kind = FAST_ELEMENTS; |
1834 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | 1851 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
1835 | 1852 |
1836 Node* const allocation_site = nullptr; | 1853 Node* const allocation_site = nullptr; |
1837 Node* const native_context = a->LoadNativeContext(context); | 1854 Node* const native_context = LoadNativeContext(context); |
1838 Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context); | 1855 Node* const array_map = LoadJSArrayElementsMap(kind, native_context); |
1839 | 1856 |
1840 CLabel return_empty_array(a, CLabel::kDeferred); | 1857 Label return_empty_array(this, Label::kDeferred); |
1841 | 1858 |
1842 // If limit is zero, return an empty array. | 1859 // If limit is zero, return an empty array. |
1843 { | 1860 { |
1844 CLabel next(a), if_limitiszero(a, CLabel::kDeferred); | 1861 Label next(this), if_limitiszero(this, Label::kDeferred); |
1845 a->Branch(a->SmiEqual(limit, smi_zero), &return_empty_array, &next); | 1862 Branch(SmiEqual(limit, smi_zero), &return_empty_array, &next); |
1846 a->Bind(&next); | 1863 Bind(&next); |
1847 } | 1864 } |
1848 | 1865 |
1849 Node* const string_length = a->LoadStringLength(string); | 1866 Node* const string_length = LoadStringLength(string); |
1850 | 1867 |
1851 // If passed the empty {string}, return either an empty array or a singleton | 1868 // If passed the empty {string}, return either an empty array or a singleton |
1852 // array depending on whether the {regexp} matches. | 1869 // array depending on whether the {regexp} matches. |
1853 { | 1870 { |
1854 CLabel next(a), if_stringisempty(a, CLabel::kDeferred); | 1871 Label next(this), if_stringisempty(this, Label::kDeferred); |
1855 a->Branch(a->SmiEqual(string_length, smi_zero), &if_stringisempty, &next); | 1872 Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next); |
1856 | 1873 |
1857 a->Bind(&if_stringisempty); | 1874 Bind(&if_stringisempty); |
1858 { | 1875 { |
1859 Node* const last_match_info = a->LoadContextElement( | 1876 Node* const last_match_info = LoadContextElement( |
1860 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 1877 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
1861 | 1878 |
1862 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 1879 Callable exec_callable = CodeFactory::RegExpExec(isolate); |
1863 Node* const match_indices = a->CallStub( | 1880 Node* const match_indices = CallStub(exec_callable, context, regexp, |
1864 exec_callable, context, regexp, string, smi_zero, last_match_info); | 1881 string, smi_zero, last_match_info); |
1865 | 1882 |
1866 CLabel return_singleton_array(a); | 1883 Label return_singleton_array(this); |
1867 a->Branch(a->WordEqual(match_indices, null), &return_singleton_array, | 1884 Branch(WordEqual(match_indices, null), &return_singleton_array, |
1868 &return_empty_array); | 1885 &return_empty_array); |
1869 | 1886 |
1870 a->Bind(&return_singleton_array); | 1887 Bind(&return_singleton_array); |
1871 { | 1888 { |
1872 Node* const length = a->SmiConstant(1); | 1889 Node* const length = SmiConstant(1); |
1873 Node* const capacity = a->IntPtrConstant(1); | 1890 Node* const capacity = IntPtrConstant(1); |
1874 Node* const result = a->AllocateJSArray(kind, array_map, capacity, | 1891 Node* const result = AllocateJSArray(kind, array_map, capacity, length, |
1875 length, allocation_site, mode); | 1892 allocation_site, mode); |
1876 | 1893 |
1877 Node* const fixed_array = a->LoadElements(result); | 1894 Node* const fixed_array = LoadElements(result); |
1878 a->StoreFixedArrayElement(fixed_array, 0, string); | 1895 StoreFixedArrayElement(fixed_array, 0, string); |
1879 | 1896 |
1880 a->Return(result); | 1897 Return(result); |
1881 } | 1898 } |
1882 } | 1899 } |
1883 | 1900 |
1884 a->Bind(&next); | 1901 Bind(&next); |
1885 } | 1902 } |
1886 | 1903 |
1887 // Loop preparations. | 1904 // Loop preparations. |
1888 | 1905 |
1889 GrowableFixedArray array(a); | 1906 GrowableFixedArray array(this); |
1890 | 1907 |
1891 CVariable var_last_matched_until(a, MachineRepresentation::kTagged); | 1908 Variable var_last_matched_until(this, MachineRepresentation::kTagged); |
1892 CVariable var_next_search_from(a, MachineRepresentation::kTagged); | 1909 Variable var_next_search_from(this, MachineRepresentation::kTagged); |
1893 | 1910 |
1894 var_last_matched_until.Bind(smi_zero); | 1911 var_last_matched_until.Bind(smi_zero); |
1895 var_next_search_from.Bind(smi_zero); | 1912 var_next_search_from.Bind(smi_zero); |
1896 | 1913 |
1897 CVariable* vars[] = {array.var_array(), array.var_length(), | 1914 Variable* vars[] = {array.var_array(), array.var_length(), |
1898 array.var_capacity(), &var_last_matched_until, | 1915 array.var_capacity(), &var_last_matched_until, |
1899 &var_next_search_from}; | 1916 &var_next_search_from}; |
1900 const int vars_count = sizeof(vars) / sizeof(vars[0]); | 1917 const int vars_count = sizeof(vars) / sizeof(vars[0]); |
1901 CLabel loop(a, vars_count, vars), push_suffix_and_out(a), out(a); | 1918 Label loop(this, vars_count, vars), push_suffix_and_out(this), out(this); |
1902 a->Goto(&loop); | 1919 Goto(&loop); |
1903 | 1920 |
1904 a->Bind(&loop); | 1921 Bind(&loop); |
1905 { | 1922 { |
1906 Node* const next_search_from = var_next_search_from.value(); | 1923 Node* const next_search_from = var_next_search_from.value(); |
1907 Node* const last_matched_until = var_last_matched_until.value(); | 1924 Node* const last_matched_until = var_last_matched_until.value(); |
1908 | 1925 |
1909 // We're done if we've reached the end of the string. | 1926 // We're done if we've reached the end of the string. |
1910 { | 1927 { |
1911 CLabel next(a); | 1928 Label next(this); |
1912 a->Branch(a->SmiEqual(next_search_from, string_length), | 1929 Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out, |
1913 &push_suffix_and_out, &next); | 1930 &next); |
1914 a->Bind(&next); | 1931 Bind(&next); |
1915 } | 1932 } |
1916 | 1933 |
1917 // Search for the given {regexp}. | 1934 // Search for the given {regexp}. |
1918 | 1935 |
1919 Node* const last_match_info = a->LoadContextElement( | 1936 Node* const last_match_info = LoadContextElement( |
1920 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 1937 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
1921 | 1938 |
1922 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 1939 Callable exec_callable = CodeFactory::RegExpExec(isolate); |
1923 Node* const match_indices = | 1940 Node* const match_indices = CallStub(exec_callable, context, regexp, string, |
1924 a->CallStub(exec_callable, context, regexp, string, next_search_from, | 1941 next_search_from, last_match_info); |
1925 last_match_info); | |
1926 | 1942 |
1927 // We're done if no match was found. | 1943 // We're done if no match was found. |
1928 { | 1944 { |
1929 CLabel next(a); | 1945 Label next(this); |
1930 a->Branch(a->WordEqual(match_indices, null), &push_suffix_and_out, &next); | 1946 Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next); |
1931 a->Bind(&next); | 1947 Bind(&next); |
1932 } | 1948 } |
1933 | 1949 |
1934 Node* const match_from = a->LoadFixedArrayElement( | 1950 Node* const match_from = LoadFixedArrayElement( |
1935 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 1951 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
1936 | 1952 |
1937 // We're done if the match starts beyond the string. | 1953 // We're done if the match starts beyond the string. |
1938 { | 1954 { |
1939 CLabel next(a); | 1955 Label next(this); |
1940 a->Branch(a->WordEqual(match_from, string_length), &push_suffix_and_out, | 1956 Branch(WordEqual(match_from, string_length), &push_suffix_and_out, &next); |
1941 &next); | 1957 Bind(&next); |
1942 a->Bind(&next); | 1958 } |
1943 } | 1959 |
1944 | 1960 Node* const match_to = LoadFixedArrayElement( |
1945 Node* const match_to = a->LoadFixedArrayElement( | |
1946 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); | 1961 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
1947 | 1962 |
1948 // Advance index and continue if the match is empty. | 1963 // Advance index and continue if the match is empty. |
1949 { | 1964 { |
1950 CLabel next(a); | 1965 Label next(this); |
1951 | 1966 |
1952 a->GotoUnless(a->SmiEqual(match_to, next_search_from), &next); | 1967 GotoUnless(SmiEqual(match_to, next_search_from), &next); |
1953 a->GotoUnless(a->SmiEqual(match_to, last_matched_until), &next); | 1968 GotoUnless(SmiEqual(match_to, last_matched_until), &next); |
1954 | 1969 |
1955 Node* const is_unicode = FastFlagGetter(a, regexp, JSRegExp::kUnicode); | 1970 Node* const is_unicode = FastFlagGetter(regexp, JSRegExp::kUnicode); |
1956 Node* const new_next_search_from = | 1971 Node* const new_next_search_from = |
1957 AdvanceStringIndex(a, string, next_search_from, is_unicode); | 1972 AdvanceStringIndex(string, next_search_from, is_unicode); |
1958 var_next_search_from.Bind(new_next_search_from); | 1973 var_next_search_from.Bind(new_next_search_from); |
1959 a->Goto(&loop); | 1974 Goto(&loop); |
1960 | 1975 |
1961 a->Bind(&next); | 1976 Bind(&next); |
1962 } | 1977 } |
1963 | 1978 |
1964 // A valid match was found, add the new substring to the array. | 1979 // A valid match was found, add the new substring to the array. |
1965 { | 1980 { |
1966 Node* const from = last_matched_until; | 1981 Node* const from = last_matched_until; |
1967 Node* const to = match_from; | 1982 Node* const to = match_from; |
1968 | 1983 |
1969 Node* const substr = a->SubString(context, string, from, to); | 1984 Node* const substr = SubString(context, string, from, to); |
1970 array.Push(substr); | 1985 array.Push(substr); |
1971 | 1986 |
1972 a->GotoIf(a->WordEqual(array.length(), int_limit), &out); | 1987 GotoIf(WordEqual(array.length(), int_limit), &out); |
1973 } | 1988 } |
1974 | 1989 |
1975 // Add all captures to the array. | 1990 // Add all captures to the array. |
1976 { | 1991 { |
1977 Node* const num_registers = a->LoadFixedArrayElement( | 1992 Node* const num_registers = LoadFixedArrayElement( |
1978 match_indices, RegExpMatchInfo::kNumberOfCapturesIndex); | 1993 match_indices, RegExpMatchInfo::kNumberOfCapturesIndex); |
1979 Node* const int_num_registers = a->SmiUntag(num_registers); | 1994 Node* const int_num_registers = SmiUntag(num_registers); |
1980 | 1995 |
1981 CVariable var_reg(a, MachineType::PointerRepresentation()); | 1996 Variable var_reg(this, MachineType::PointerRepresentation()); |
1982 var_reg.Bind(a->IntPtrConstant(2)); | 1997 var_reg.Bind(IntPtrConstant(2)); |
1983 | 1998 |
1984 CVariable* vars[] = {array.var_array(), array.var_length(), | 1999 Variable* vars[] = {array.var_array(), array.var_length(), |
1985 array.var_capacity(), &var_reg}; | 2000 array.var_capacity(), &var_reg}; |
1986 const int vars_count = sizeof(vars) / sizeof(vars[0]); | 2001 const int vars_count = sizeof(vars) / sizeof(vars[0]); |
1987 CLabel nested_loop(a, vars_count, vars), nested_loop_out(a); | 2002 Label nested_loop(this, vars_count, vars), nested_loop_out(this); |
1988 a->Branch(a->IntPtrLessThan(var_reg.value(), int_num_registers), | 2003 Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop, |
1989 &nested_loop, &nested_loop_out); | 2004 &nested_loop_out); |
1990 | 2005 |
1991 a->Bind(&nested_loop); | 2006 Bind(&nested_loop); |
1992 { | 2007 { |
1993 Node* const reg = var_reg.value(); | 2008 Node* const reg = var_reg.value(); |
1994 Node* const from = a->LoadFixedArrayElement( | 2009 Node* const from = LoadFixedArrayElement( |
1995 match_indices, reg, | 2010 match_indices, reg, |
1996 RegExpMatchInfo::kFirstCaptureIndex * kPointerSize, mode); | 2011 RegExpMatchInfo::kFirstCaptureIndex * kPointerSize, mode); |
1997 Node* const to = a->LoadFixedArrayElement( | 2012 Node* const to = LoadFixedArrayElement( |
1998 match_indices, reg, | 2013 match_indices, reg, |
1999 (RegExpMatchInfo::kFirstCaptureIndex + 1) * kPointerSize, mode); | 2014 (RegExpMatchInfo::kFirstCaptureIndex + 1) * kPointerSize, mode); |
2000 | 2015 |
2001 CLabel select_capture(a), select_undefined(a), store_value(a); | 2016 Label select_capture(this), select_undefined(this), store_value(this); |
2002 CVariable var_value(a, MachineRepresentation::kTagged); | 2017 Variable var_value(this, MachineRepresentation::kTagged); |
2003 a->Branch(a->SmiEqual(to, a->SmiConstant(-1)), &select_undefined, | 2018 Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined, |
2004 &select_capture); | 2019 &select_capture); |
2005 | 2020 |
2006 a->Bind(&select_capture); | 2021 Bind(&select_capture); |
2007 { | 2022 { |
2008 Node* const substr = a->SubString(context, string, from, to); | 2023 Node* const substr = SubString(context, string, from, to); |
2009 var_value.Bind(substr); | 2024 var_value.Bind(substr); |
2010 a->Goto(&store_value); | 2025 Goto(&store_value); |
2011 } | 2026 } |
2012 | 2027 |
2013 a->Bind(&select_undefined); | 2028 Bind(&select_undefined); |
2014 { | 2029 { |
2015 Node* const undefined = a->UndefinedConstant(); | 2030 Node* const undefined = UndefinedConstant(); |
2016 var_value.Bind(undefined); | 2031 var_value.Bind(undefined); |
2017 a->Goto(&store_value); | 2032 Goto(&store_value); |
2018 } | 2033 } |
2019 | 2034 |
2020 a->Bind(&store_value); | 2035 Bind(&store_value); |
2021 { | 2036 { |
2022 array.Push(var_value.value()); | 2037 array.Push(var_value.value()); |
2023 a->GotoIf(a->WordEqual(array.length(), int_limit), &out); | 2038 GotoIf(WordEqual(array.length(), int_limit), &out); |
2024 | 2039 |
2025 Node* const new_reg = a->IntPtrAdd(reg, a->IntPtrConstant(2)); | 2040 Node* const new_reg = IntPtrAdd(reg, IntPtrConstant(2)); |
2026 var_reg.Bind(new_reg); | 2041 var_reg.Bind(new_reg); |
2027 | 2042 |
2028 a->Branch(a->IntPtrLessThan(new_reg, int_num_registers), &nested_loop, | 2043 Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop, |
2029 &nested_loop_out); | 2044 &nested_loop_out); |
2030 } | 2045 } |
2031 } | 2046 } |
2032 | 2047 |
2033 a->Bind(&nested_loop_out); | 2048 Bind(&nested_loop_out); |
2034 } | 2049 } |
2035 | 2050 |
2036 var_last_matched_until.Bind(match_to); | 2051 var_last_matched_until.Bind(match_to); |
2037 var_next_search_from.Bind(match_to); | 2052 var_next_search_from.Bind(match_to); |
2038 a->Goto(&loop); | 2053 Goto(&loop); |
2039 } | 2054 } |
2040 | 2055 |
2041 a->Bind(&push_suffix_and_out); | 2056 Bind(&push_suffix_and_out); |
2042 { | 2057 { |
2043 Node* const from = var_last_matched_until.value(); | 2058 Node* const from = var_last_matched_until.value(); |
2044 Node* const to = string_length; | 2059 Node* const to = string_length; |
2045 | 2060 |
2046 Node* const substr = a->SubString(context, string, from, to); | 2061 Node* const substr = SubString(context, string, from, to); |
2047 array.Push(substr); | 2062 array.Push(substr); |
2048 | 2063 |
2049 a->Goto(&out); | 2064 Goto(&out); |
2050 } | 2065 } |
2051 | 2066 |
2052 a->Bind(&out); | 2067 Bind(&out); |
2053 { | 2068 { |
2054 Node* const result = array.ToJSArray(context); | 2069 Node* const result = array.ToJSArray(context); |
2055 a->Return(result); | 2070 Return(result); |
2056 } | 2071 } |
2057 | 2072 |
2058 a->Bind(&return_empty_array); | 2073 Bind(&return_empty_array); |
2059 { | 2074 { |
2060 Node* const length = smi_zero; | 2075 Node* const length = smi_zero; |
2061 Node* const capacity = int_zero; | 2076 Node* const capacity = int_zero; |
2062 Node* const result = a->AllocateJSArray(kind, array_map, capacity, length, | 2077 Node* const result = AllocateJSArray(kind, array_map, capacity, length, |
2063 allocation_site, mode); | 2078 allocation_site, mode); |
2064 a->Return(result); | 2079 Return(result); |
2065 } | 2080 } |
2066 } | 2081 } |
2067 | 2082 |
2068 } // namespace | |
2069 | |
2070 // ES#sec-regexp.prototype-@@split | 2083 // ES#sec-regexp.prototype-@@split |
2071 // RegExp.prototype [ @@split ] ( string, limit ) | 2084 // RegExp.prototype [ @@split ] ( string, limit ) |
2072 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { | 2085 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { |
2073 Isolate* const isolate = this->isolate(); | |
2074 | |
2075 Node* const undefined = UndefinedConstant(); | |
2076 | |
2077 Node* const maybe_receiver = Parameter(0); | 2086 Node* const maybe_receiver = Parameter(0); |
2078 Node* const maybe_string = Parameter(1); | 2087 Node* const maybe_string = Parameter(1); |
2079 Node* const maybe_limit = Parameter(2); | 2088 Node* const maybe_limit = Parameter(2); |
2080 Node* const context = Parameter(5); | 2089 Node* const context = Parameter(5); |
2081 | 2090 |
2082 // Ensure {maybe_receiver} is a JSReceiver. | 2091 // Ensure {maybe_receiver} is a JSReceiver. |
2083 Node* const map = ThrowIfNotJSReceiver( | 2092 Node* const map = ThrowIfNotJSReceiver( |
2084 this, isolate, context, maybe_receiver, | 2093 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
2085 MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.@@split"); | 2094 "RegExp.prototype.@@split"); |
2086 Node* const receiver = maybe_receiver; | 2095 Node* const receiver = maybe_receiver; |
2087 | 2096 |
2088 // Convert {maybe_string} to a String. | 2097 // Convert {maybe_string} to a String. |
2089 Node* const string = ToString(context, maybe_string); | 2098 Node* const string = ToString(context, maybe_string); |
2090 | 2099 |
2091 CLabel fast_path(this), slow_path(this); | 2100 Label fast_path(this), slow_path(this); |
2092 BranchIfFastPath(this, context, map, &fast_path, &slow_path); | 2101 BranchIfFastRegExp(context, map, &fast_path, &slow_path); |
2093 | 2102 |
2094 Bind(&fast_path); | 2103 Bind(&fast_path); |
2095 { | 2104 { |
2105 // TODO(jgruber): Even if map checks send us to the fast path, we still need | |
2106 // to verify the constructor property and jump to the slow path if it has | |
2107 // been changed. | |
2108 | |
2096 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. | 2109 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. |
2097 CVariable var_limit(this, MachineRepresentation::kTagged); | 2110 Variable var_limit(this, MachineRepresentation::kTagged); |
2098 CLabel if_limitissmimax(this), limit_done(this); | 2111 Label if_limitissmimax(this), limit_done(this); |
2099 | 2112 |
2100 GotoIf(WordEqual(maybe_limit, undefined), &if_limitissmimax); | 2113 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); |
2101 | 2114 |
2102 { | 2115 { |
2103 Node* const limit = ToUint32(context, maybe_limit); | 2116 Node* const limit = ToUint32(context, maybe_limit); |
2104 GotoUnless(TaggedIsSmi(limit), &if_limitissmimax); | 2117 GotoUnless(TaggedIsSmi(limit), &if_limitissmimax); |
2105 | 2118 |
2106 var_limit.Bind(limit); | 2119 var_limit.Bind(limit); |
2107 Goto(&limit_done); | 2120 Goto(&limit_done); |
2108 } | 2121 } |
2109 | 2122 |
2110 Bind(&if_limitissmimax); | 2123 Bind(&if_limitissmimax); |
2111 { | 2124 { |
2112 // TODO(jgruber): In this case, we can probably generation of limit checks | 2125 // TODO(jgruber): In this case, we can probably generation of limit checks |
2113 // in Generate_RegExpPrototypeSplitBody. | 2126 // in Generate_RegExpPrototypeSplitBody. |
2114 Node* const smi_max = SmiConstant(Smi::kMaxValue); | 2127 Node* const smi_max = SmiConstant(Smi::kMaxValue); |
2115 var_limit.Bind(smi_max); | 2128 var_limit.Bind(smi_max); |
2116 Goto(&limit_done); | 2129 Goto(&limit_done); |
2117 } | 2130 } |
2118 | 2131 |
2119 Bind(&limit_done); | 2132 Bind(&limit_done); |
2120 { | 2133 { |
2121 Node* const limit = var_limit.value(); | 2134 Node* const limit = var_limit.value(); |
2122 Generate_RegExpPrototypeSplitBody(this, receiver, string, limit, context); | 2135 RegExpPrototypeSplitBody(context, receiver, string, limit); |
2123 } | 2136 } |
2124 } | 2137 } |
2125 | 2138 |
2126 Bind(&slow_path); | 2139 Bind(&slow_path); |
2127 { | 2140 { |
2128 Node* const result = CallRuntime(Runtime::kRegExpSplit, context, receiver, | 2141 Node* const result = CallRuntime(Runtime::kRegExpSplit, context, receiver, |
2129 string, maybe_limit); | 2142 string, maybe_limit); |
2130 Return(result); | 2143 Return(result); |
2131 } | 2144 } |
2132 } | 2145 } |
2133 | 2146 |
2134 namespace { | 2147 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( |
2135 | 2148 Node* context, Node* regexp, Node* string, Node* replace_callable) { |
2136 Node* ReplaceGlobalCallableFastPath(CodeStubAssembler* a, Node* context, | |
2137 Node* regexp, Node* subject_string, | |
2138 Node* replace_callable) { | |
2139 // The fast path is reached only if {receiver} is a global unmodified | 2149 // The fast path is reached only if {receiver} is a global unmodified |
2140 // JSRegExp instance and {replace_callable} is callable. | 2150 // JSRegExp instance and {replace_callable} is callable. |
2141 | 2151 |
2142 Isolate* const isolate = a->isolate(); | 2152 Isolate* const isolate = this->isolate(); |
2143 | 2153 |
2144 Node* const null = a->NullConstant(); | 2154 Node* const null = NullConstant(); |
2145 Node* const undefined = a->UndefinedConstant(); | 2155 Node* const undefined = UndefinedConstant(); |
2146 Node* const int_zero = a->IntPtrConstant(0); | 2156 Node* const int_zero = IntPtrConstant(0); |
2147 Node* const int_one = a->IntPtrConstant(1); | 2157 Node* const int_one = IntPtrConstant(1); |
2148 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 2158 Node* const smi_zero = SmiConstant(Smi::kZero); |
2149 | 2159 |
2150 Node* const native_context = a->LoadNativeContext(context); | 2160 Node* const native_context = LoadNativeContext(context); |
2151 | 2161 |
2152 CLabel out(a); | 2162 Label out(this); |
2153 CVariable var_result(a, MachineRepresentation::kTagged); | 2163 Variable var_result(this, MachineRepresentation::kTagged); |
2154 | 2164 |
2155 // Set last index to 0. | 2165 // Set last index to 0. |
2156 FastStoreLastIndex(a, regexp, smi_zero); | 2166 FastStoreLastIndex(regexp, smi_zero); |
2157 | 2167 |
2158 // Allocate {result_array}. | 2168 // Allocate {result_array}. |
2159 Node* result_array; | 2169 Node* result_array; |
2160 { | 2170 { |
2161 ElementsKind kind = FAST_ELEMENTS; | 2171 ElementsKind kind = FAST_ELEMENTS; |
2162 Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context); | 2172 Node* const array_map = LoadJSArrayElementsMap(kind, native_context); |
2163 Node* const capacity = a->IntPtrConstant(16); | 2173 Node* const capacity = IntPtrConstant(16); |
2164 Node* const length = smi_zero; | 2174 Node* const length = smi_zero; |
2165 Node* const allocation_site = nullptr; | 2175 Node* const allocation_site = nullptr; |
2166 ParameterMode capacity_mode = CodeStubAssembler::INTPTR_PARAMETERS; | 2176 ParameterMode capacity_mode = CodeStubAssembler::INTPTR_PARAMETERS; |
2167 | 2177 |
2168 result_array = a->AllocateJSArray(kind, array_map, capacity, length, | 2178 result_array = AllocateJSArray(kind, array_map, capacity, length, |
2169 allocation_site, capacity_mode); | 2179 allocation_site, capacity_mode); |
2170 } | 2180 } |
2171 | 2181 |
2172 // Call into runtime for RegExpExecMultiple. | 2182 // Call into runtime for RegExpExecMultiple. |
2173 Node* last_match_info = a->LoadContextElement( | 2183 Node* last_match_info = |
2174 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2184 LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
2175 Node* const res = | 2185 Node* const res = CallRuntime(Runtime::kRegExpExecMultiple, context, regexp, |
2176 a->CallRuntime(Runtime::kRegExpExecMultiple, context, regexp, | 2186 string, last_match_info, result_array); |
2177 subject_string, last_match_info, result_array); | |
2178 | 2187 |
2179 // Reset last index to 0. | 2188 // Reset last index to 0. |
2180 FastStoreLastIndex(a, regexp, smi_zero); | 2189 FastStoreLastIndex(regexp, smi_zero); |
2181 | 2190 |
2182 // If no matches, return the subject string. | 2191 // If no matches, return the subject string. |
2183 var_result.Bind(subject_string); | 2192 var_result.Bind(string); |
2184 a->GotoIf(a->WordEqual(res, null), &out); | 2193 GotoIf(WordEqual(res, null), &out); |
2185 | 2194 |
2186 // Reload last match info since it might have changed. | 2195 // Reload last match info since it might have changed. |
2187 last_match_info = a->LoadContextElement( | 2196 last_match_info = |
2188 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2197 LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
2189 | 2198 |
2190 Node* const res_length = a->LoadJSArrayLength(res); | 2199 Node* const res_length = LoadJSArrayLength(res); |
2191 Node* const res_elems = a->LoadElements(res); | 2200 Node* const res_elems = LoadElements(res); |
2192 CSA_ASSERT(a, a->HasInstanceType(res_elems, FIXED_ARRAY_TYPE)); | 2201 CSA_ASSERT(this, HasInstanceType(res_elems, FIXED_ARRAY_TYPE)); |
2193 | 2202 |
2194 Node* const num_capture_registers = a->LoadFixedArrayElement( | 2203 Node* const num_capture_registers = LoadFixedArrayElement( |
2195 last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex); | 2204 last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex); |
2196 | 2205 |
2197 CLabel if_hasexplicitcaptures(a), if_noexplicitcaptures(a), create_result(a); | 2206 Label if_hasexplicitcaptures(this), if_noexplicitcaptures(this), |
2198 a->Branch(a->SmiEqual(num_capture_registers, a->SmiConstant(Smi::FromInt(2))), | 2207 create_result(this); |
2199 &if_noexplicitcaptures, &if_hasexplicitcaptures); | 2208 Branch(SmiEqual(num_capture_registers, SmiConstant(Smi::FromInt(2))), |
2209 &if_noexplicitcaptures, &if_hasexplicitcaptures); | |
2200 | 2210 |
2201 a->Bind(&if_noexplicitcaptures); | 2211 Bind(&if_noexplicitcaptures); |
2202 { | 2212 { |
2203 // If the number of captures is two then there are no explicit captures in | 2213 // If the number of captures is two then there are no explicit captures in |
2204 // the regexp, just the implicit capture that captures the whole match. In | 2214 // the regexp, just the implicit capture that captures the whole match. In |
2205 // this case we can simplify quite a bit and end up with something faster. | 2215 // this case we can simplify quite a bit and end up with something faster. |
2206 // The builder will consist of some integers that indicate slices of the | 2216 // The builder will consist of some integers that indicate slices of the |
2207 // input string and some replacements that were returned from the replace | 2217 // input string and some replacements that were returned from the replace |
2208 // function. | 2218 // function. |
2209 | 2219 |
2210 CVariable var_match_start(a, MachineRepresentation::kTagged); | 2220 Variable var_match_start(this, MachineRepresentation::kTagged); |
2211 var_match_start.Bind(smi_zero); | 2221 var_match_start.Bind(smi_zero); |
2212 | 2222 |
2213 Node* const end = a->SmiUntag(res_length); | 2223 Node* const end = SmiUntag(res_length); |
2214 CVariable var_i(a, MachineType::PointerRepresentation()); | 2224 Variable var_i(this, MachineType::PointerRepresentation()); |
2215 var_i.Bind(int_zero); | 2225 var_i.Bind(int_zero); |
2216 | 2226 |
2217 CVariable* vars[] = {&var_i, &var_match_start}; | 2227 Variable* vars[] = {&var_i, &var_match_start}; |
2218 CLabel loop(a, 2, vars); | 2228 Label loop(this, 2, vars); |
2219 a->Goto(&loop); | 2229 Goto(&loop); |
2220 a->Bind(&loop); | 2230 Bind(&loop); |
2221 { | 2231 { |
2222 Node* const i = var_i.value(); | 2232 Node* const i = var_i.value(); |
2223 a->GotoUnless(a->IntPtrLessThan(i, end), &create_result); | 2233 GotoUnless(IntPtrLessThan(i, end), &create_result); |
2224 | 2234 |
2225 ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | 2235 ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
2226 Node* const elem = a->LoadFixedArrayElement(res_elems, i, 0, mode); | 2236 Node* const elem = LoadFixedArrayElement(res_elems, i, 0, mode); |
2227 | 2237 |
2228 CLabel if_issmi(a), if_isstring(a), loop_epilogue(a); | 2238 Label if_issmi(this), if_isstring(this), loop_epilogue(this); |
2229 a->Branch(a->TaggedIsSmi(elem), &if_issmi, &if_isstring); | 2239 Branch(TaggedIsSmi(elem), &if_issmi, &if_isstring); |
2230 | 2240 |
2231 a->Bind(&if_issmi); | 2241 Bind(&if_issmi); |
2232 { | 2242 { |
2233 // Integers represent slices of the original string. | 2243 // Integers represent slices of the original string. |
2234 CLabel if_isnegativeorzero(a), if_ispositive(a); | 2244 Label if_isnegativeorzero(this), if_ispositive(this); |
2235 a->BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero, | 2245 BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero, |
2236 &if_ispositive); | 2246 &if_ispositive); |
2237 | 2247 |
2238 a->Bind(&if_ispositive); | 2248 Bind(&if_ispositive); |
2239 { | 2249 { |
2240 Node* const int_elem = a->SmiUntag(elem); | 2250 Node* const int_elem = SmiUntag(elem); |
2241 Node* const new_match_start = | 2251 Node* const new_match_start = |
2242 a->IntPtrAdd(a->WordShr(int_elem, a->IntPtrConstant(11)), | 2252 IntPtrAdd(WordShr(int_elem, IntPtrConstant(11)), |
2243 a->WordAnd(int_elem, a->IntPtrConstant(0x7ff))); | 2253 WordAnd(int_elem, IntPtrConstant(0x7ff))); |
2244 var_match_start.Bind(a->SmiTag(new_match_start)); | 2254 var_match_start.Bind(SmiTag(new_match_start)); |
2245 a->Goto(&loop_epilogue); | 2255 Goto(&loop_epilogue); |
2246 } | 2256 } |
2247 | 2257 |
2248 a->Bind(&if_isnegativeorzero); | 2258 Bind(&if_isnegativeorzero); |
2249 { | 2259 { |
2250 Node* const next_i = a->IntPtrAdd(i, int_one); | 2260 Node* const next_i = IntPtrAdd(i, int_one); |
2251 var_i.Bind(next_i); | 2261 var_i.Bind(next_i); |
2252 | 2262 |
2253 Node* const next_elem = | 2263 Node* const next_elem = |
2254 a->LoadFixedArrayElement(res_elems, next_i, 0, mode); | 2264 LoadFixedArrayElement(res_elems, next_i, 0, mode); |
2255 | 2265 |
2256 Node* const new_match_start = a->SmiSub(next_elem, elem); | 2266 Node* const new_match_start = SmiSub(next_elem, elem); |
2257 var_match_start.Bind(new_match_start); | 2267 var_match_start.Bind(new_match_start); |
2258 a->Goto(&loop_epilogue); | 2268 Goto(&loop_epilogue); |
2259 } | 2269 } |
2260 } | 2270 } |
2261 | 2271 |
2262 a->Bind(&if_isstring); | 2272 Bind(&if_isstring); |
2263 { | 2273 { |
2264 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(elem))); | 2274 CSA_ASSERT(this, IsStringInstanceType(LoadInstanceType(elem))); |
2265 | 2275 |
2266 Callable call_callable = CodeFactory::Call(isolate); | 2276 Callable call_callable = CodeFactory::Call(isolate); |
2267 Node* const replacement_obj = | 2277 Node* const replacement_obj = |
2268 a->CallJS(call_callable, context, replace_callable, undefined, elem, | 2278 CallJS(call_callable, context, replace_callable, undefined, elem, |
2269 var_match_start.value(), subject_string); | 2279 var_match_start.value(), string); |
2270 | 2280 |
2271 Node* const replacement_str = a->ToString(context, replacement_obj); | 2281 Node* const replacement_str = ToString(context, replacement_obj); |
2272 a->StoreFixedArrayElement(res_elems, i, replacement_str); | 2282 StoreFixedArrayElement(res_elems, i, replacement_str); |
2273 | 2283 |
2274 Node* const elem_length = a->LoadStringLength(elem); | 2284 Node* const elem_length = LoadStringLength(elem); |
2275 Node* const new_match_start = | 2285 Node* const new_match_start = |
2276 a->SmiAdd(var_match_start.value(), elem_length); | 2286 SmiAdd(var_match_start.value(), elem_length); |
2277 var_match_start.Bind(new_match_start); | 2287 var_match_start.Bind(new_match_start); |
2278 | 2288 |
2279 a->Goto(&loop_epilogue); | 2289 Goto(&loop_epilogue); |
2280 } | 2290 } |
2281 | 2291 |
2282 a->Bind(&loop_epilogue); | 2292 Bind(&loop_epilogue); |
2283 { | 2293 { |
2284 var_i.Bind(a->IntPtrAdd(var_i.value(), int_one)); | 2294 var_i.Bind(IntPtrAdd(var_i.value(), int_one)); |
2285 a->Goto(&loop); | 2295 Goto(&loop); |
2286 } | 2296 } |
2287 } | 2297 } |
2288 } | 2298 } |
2289 | 2299 |
2290 a->Bind(&if_hasexplicitcaptures); | 2300 Bind(&if_hasexplicitcaptures); |
2291 { | 2301 { |
2292 ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | 2302 ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
2293 | 2303 |
2294 Node* const from = int_zero; | 2304 Node* const from = int_zero; |
2295 Node* const to = a->SmiUntag(res_length); | 2305 Node* const to = SmiUntag(res_length); |
2296 const int increment = 1; | 2306 const int increment = 1; |
2297 | 2307 |
2298 a->BuildFastLoop( | 2308 BuildFastLoop( |
2299 MachineType::PointerRepresentation(), from, to, | 2309 MachineType::PointerRepresentation(), from, to, |
2300 [res_elems, isolate, native_context, context, undefined, | 2310 [this, res_elems, isolate, native_context, context, undefined, |
2301 replace_callable, mode](CodeStubAssembler* a, Node* index) { | 2311 replace_callable, mode](CodeStubAssembler* a, Node* index) { |
2302 Node* const elem = | 2312 Node* const elem = LoadFixedArrayElement(res_elems, index, 0, mode); |
2303 a->LoadFixedArrayElement(res_elems, index, 0, mode); | 2313 |
2304 | 2314 Label do_continue(this); |
2305 CLabel do_continue(a); | 2315 GotoIf(TaggedIsSmi(elem), &do_continue); |
2306 a->GotoIf(a->TaggedIsSmi(elem), &do_continue); | |
2307 | 2316 |
2308 // elem must be an Array. | 2317 // elem must be an Array. |
2309 // Use the apply argument as backing for global RegExp properties. | 2318 // Use the apply argument as backing for global RegExp properties. |
2310 | 2319 |
2311 CSA_ASSERT(a, a->HasInstanceType(elem, JS_ARRAY_TYPE)); | 2320 CSA_ASSERT(this, HasInstanceType(elem, JS_ARRAY_TYPE)); |
2312 | 2321 |
2313 // TODO(jgruber): Remove indirection through Call->ReflectApply. | 2322 // TODO(jgruber): Remove indirection through Call->ReflectApply. |
2314 Callable call_callable = CodeFactory::Call(isolate); | 2323 Callable call_callable = CodeFactory::Call(isolate); |
2315 Node* const reflect_apply = a->LoadContextElement( | 2324 Node* const reflect_apply = |
2316 native_context, Context::REFLECT_APPLY_INDEX); | 2325 LoadContextElement(native_context, Context::REFLECT_APPLY_INDEX); |
2317 | 2326 |
2318 Node* const replacement_obj = | 2327 Node* const replacement_obj = |
2319 a->CallJS(call_callable, context, reflect_apply, undefined, | 2328 CallJS(call_callable, context, reflect_apply, undefined, |
2320 replace_callable, undefined, elem); | 2329 replace_callable, undefined, elem); |
2321 | 2330 |
2322 // Overwrite the i'th element in the results with the string we got | 2331 // Overwrite the i'th element in the results with the string we got |
2323 // back from the callback function. | 2332 // back from the callback function. |
2324 | 2333 |
2325 Node* const replacement_str = a->ToString(context, replacement_obj); | 2334 Node* const replacement_str = ToString(context, replacement_obj); |
2326 a->StoreFixedArrayElement(res_elems, index, replacement_str, | 2335 StoreFixedArrayElement(res_elems, index, replacement_str, |
2327 UPDATE_WRITE_BARRIER, 0, mode); | 2336 UPDATE_WRITE_BARRIER, 0, mode); |
2328 | 2337 |
2329 a->Goto(&do_continue); | 2338 Goto(&do_continue); |
2330 a->Bind(&do_continue); | 2339 Bind(&do_continue); |
2331 }, | 2340 }, |
2332 increment, CodeStubAssembler::IndexAdvanceMode::kPost); | 2341 increment, CodeStubAssembler::IndexAdvanceMode::kPost); |
2333 | 2342 |
2334 a->Goto(&create_result); | 2343 Goto(&create_result); |
2335 } | 2344 } |
2336 | 2345 |
2337 a->Bind(&create_result); | 2346 Bind(&create_result); |
2338 { | 2347 { |
2339 Node* const result = a->CallRuntime(Runtime::kStringBuilderConcat, context, | 2348 Node* const result = CallRuntime(Runtime::kStringBuilderConcat, context, |
2340 res, res_length, subject_string); | 2349 res, res_length, string); |
2341 var_result.Bind(result); | 2350 var_result.Bind(result); |
2342 a->Goto(&out); | 2351 Goto(&out); |
2343 } | 2352 } |
2344 | 2353 |
2345 a->Bind(&out); | 2354 Bind(&out); |
2346 return var_result.value(); | 2355 return var_result.value(); |
2347 } | 2356 } |
2348 | 2357 |
2349 Node* ReplaceSimpleStringFastPath(CodeStubAssembler* a, Node* context, | 2358 Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath( |
2350 Node* regexp, Node* subject_string, | 2359 Node* context, Node* regexp, Node* string, Node* replace_string) { |
2351 Node* replace_string) { | |
2352 // The fast path is reached only if {receiver} is an unmodified | 2360 // The fast path is reached only if {receiver} is an unmodified |
2353 // JSRegExp instance, {replace_value} is non-callable, and | 2361 // JSRegExp instance, {replace_value} is non-callable, and |
2354 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple | 2362 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple |
2355 // string replacement. | 2363 // string replacement. |
2356 | 2364 |
2357 Isolate* const isolate = a->isolate(); | 2365 Isolate* const isolate = this->isolate(); |
2358 | 2366 |
2359 Node* const null = a->NullConstant(); | 2367 Node* const null = NullConstant(); |
2360 Node* const int_zero = a->IntPtrConstant(0); | 2368 Node* const int_zero = IntPtrConstant(0); |
2361 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 2369 Node* const smi_zero = SmiConstant(Smi::kZero); |
2362 | 2370 |
2363 CLabel out(a); | 2371 Label out(this); |
2364 CVariable var_result(a, MachineRepresentation::kTagged); | 2372 Variable var_result(this, MachineRepresentation::kTagged); |
2365 | 2373 |
2366 // Load the last match info. | 2374 // Load the last match info. |
2367 Node* const native_context = a->LoadNativeContext(context); | 2375 Node* const native_context = LoadNativeContext(context); |
2368 Node* const last_match_info = a->LoadContextElement( | 2376 Node* const last_match_info = |
2369 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2377 LoadContextElement(native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
2370 | 2378 |
2371 // Is {regexp} global? | 2379 // Is {regexp} global? |
2372 CLabel if_isglobal(a), if_isnonglobal(a); | 2380 Label if_isglobal(this), if_isnonglobal(this); |
2373 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); | 2381 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
2374 Node* const is_global = | 2382 Node* const is_global = |
2375 a->WordAnd(a->SmiUntag(flags), a->IntPtrConstant(JSRegExp::kGlobal)); | 2383 WordAnd(SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal)); |
2376 a->Branch(a->WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal); | 2384 Branch(WordEqual(is_global, int_zero), &if_isnonglobal, &if_isglobal); |
2377 | 2385 |
2378 a->Bind(&if_isglobal); | 2386 Bind(&if_isglobal); |
2379 { | 2387 { |
2380 // Hand off global regexps to runtime. | 2388 // Hand off global regexps to runtime. |
2381 FastStoreLastIndex(a, regexp, smi_zero); | 2389 FastStoreLastIndex(regexp, smi_zero); |
2382 Node* const result = | 2390 Node* const result = |
2383 a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, | 2391 CallRuntime(Runtime::kStringReplaceGlobalRegExpWithString, context, |
2384 subject_string, regexp, replace_string, last_match_info); | 2392 string, regexp, replace_string, last_match_info); |
2385 var_result.Bind(result); | 2393 var_result.Bind(result); |
2386 a->Goto(&out); | 2394 Goto(&out); |
2387 } | 2395 } |
2388 | 2396 |
2389 a->Bind(&if_isnonglobal); | 2397 Bind(&if_isnonglobal); |
2390 { | 2398 { |
2391 // Run exec, then manually construct the resulting string. | 2399 // Run exec, then manually construct the resulting string. |
2392 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 2400 Callable exec_callable = CodeFactory::RegExpExec(isolate); |
2393 Node* const match_indices = | 2401 Node* const match_indices = CallStub(exec_callable, context, regexp, string, |
2394 a->CallStub(exec_callable, context, regexp, subject_string, smi_zero, | 2402 smi_zero, last_match_info); |
2395 last_match_info); | 2403 |
2396 | 2404 Label if_matched(this), if_didnotmatch(this); |
2397 CLabel if_matched(a), if_didnotmatch(a); | 2405 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); |
2398 a->Branch(a->WordEqual(match_indices, null), &if_didnotmatch, &if_matched); | 2406 |
2399 | 2407 Bind(&if_didnotmatch); |
2400 a->Bind(&if_didnotmatch); | |
2401 { | 2408 { |
2402 FastStoreLastIndex(a, regexp, smi_zero); | 2409 FastStoreLastIndex(regexp, smi_zero); |
2403 var_result.Bind(subject_string); | 2410 var_result.Bind(string); |
2404 a->Goto(&out); | 2411 Goto(&out); |
2405 } | 2412 } |
2406 | 2413 |
2407 a->Bind(&if_matched); | 2414 Bind(&if_matched); |
2408 { | 2415 { |
2409 Node* const subject_start = smi_zero; | 2416 Node* const subject_start = smi_zero; |
2410 Node* const match_start = a->LoadFixedArrayElement( | 2417 Node* const match_start = LoadFixedArrayElement( |
2411 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 2418 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
2412 Node* const match_end = a->LoadFixedArrayElement( | 2419 Node* const match_end = LoadFixedArrayElement( |
2413 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); | 2420 match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
2414 Node* const subject_end = a->LoadStringLength(subject_string); | 2421 Node* const subject_end = LoadStringLength(string); |
2415 | 2422 |
2416 CLabel if_replaceisempty(a), if_replaceisnotempty(a); | 2423 Label if_replaceisempty(this), if_replaceisnotempty(this); |
2417 Node* const replace_length = a->LoadStringLength(replace_string); | 2424 Node* const replace_length = LoadStringLength(replace_string); |
2418 a->Branch(a->SmiEqual(replace_length, smi_zero), &if_replaceisempty, | 2425 Branch(SmiEqual(replace_length, smi_zero), &if_replaceisempty, |
2419 &if_replaceisnotempty); | 2426 &if_replaceisnotempty); |
2420 | 2427 |
2421 a->Bind(&if_replaceisempty); | 2428 Bind(&if_replaceisempty); |
2422 { | 2429 { |
2423 // TODO(jgruber): We could skip many of the checks that using SubString | 2430 // TODO(jgruber): We could skip many of the checks that using SubString |
2424 // here entails. | 2431 // here entails. |
2425 | 2432 |
2426 Node* const first_part = | 2433 Node* const first_part = |
2427 a->SubString(context, subject_string, subject_start, match_start); | 2434 SubString(context, string, subject_start, match_start); |
2428 Node* const second_part = | 2435 Node* const second_part = |
2429 a->SubString(context, subject_string, match_end, subject_end); | 2436 SubString(context, string, match_end, subject_end); |
2430 | 2437 |
2431 Node* const result = a->StringAdd(context, first_part, second_part); | 2438 Node* const result = StringAdd(context, first_part, second_part); |
2432 var_result.Bind(result); | 2439 var_result.Bind(result); |
2433 a->Goto(&out); | 2440 Goto(&out); |
2434 } | 2441 } |
2435 | 2442 |
2436 a->Bind(&if_replaceisnotempty); | 2443 Bind(&if_replaceisnotempty); |
2437 { | 2444 { |
2438 Node* const first_part = | 2445 Node* const first_part = |
2439 a->SubString(context, subject_string, subject_start, match_start); | 2446 SubString(context, string, subject_start, match_start); |
2440 Node* const second_part = replace_string; | 2447 Node* const second_part = replace_string; |
2441 Node* const third_part = | 2448 Node* const third_part = |
2442 a->SubString(context, subject_string, match_end, subject_end); | 2449 SubString(context, string, match_end, subject_end); |
2443 | 2450 |
2444 Node* result = a->StringAdd(context, first_part, second_part); | 2451 Node* result = StringAdd(context, first_part, second_part); |
2445 result = a->StringAdd(context, result, third_part); | 2452 result = StringAdd(context, result, third_part); |
2446 | 2453 |
2447 var_result.Bind(result); | 2454 var_result.Bind(result); |
2448 a->Goto(&out); | 2455 Goto(&out); |
2449 } | 2456 } |
2450 } | 2457 } |
2451 } | 2458 } |
2452 | 2459 |
2453 a->Bind(&out); | 2460 Bind(&out); |
2454 return var_result.value(); | 2461 return var_result.value(); |
2455 } | 2462 } |
2456 | 2463 |
2457 } // namespace | |
2458 | |
2459 // ES#sec-regexp.prototype-@@replace | 2464 // ES#sec-regexp.prototype-@@replace |
2460 // RegExp.prototype [ @@replace ] ( string, replaceValue ) | 2465 // RegExp.prototype [ @@replace ] ( string, replaceValue ) |
2461 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { | 2466 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { |
2462 Isolate* const isolate = this->isolate(); | |
2463 | |
2464 Node* const maybe_receiver = Parameter(0); | 2467 Node* const maybe_receiver = Parameter(0); |
2465 Node* const maybe_string = Parameter(1); | 2468 Node* const maybe_string = Parameter(1); |
2466 Node* const replace_value = Parameter(2); | 2469 Node* const replace_value = Parameter(2); |
2467 Node* const context = Parameter(5); | 2470 Node* const context = Parameter(5); |
2468 | 2471 |
2469 Node* const int_zero = IntPtrConstant(0); | 2472 Node* const int_zero = IntPtrConstant(0); |
2470 | 2473 |
2471 // Ensure {maybe_receiver} is a JSReceiver. | 2474 // Ensure {maybe_receiver} is a JSReceiver. |
2472 Node* const map = | 2475 Node* const map = ThrowIfNotJSReceiver( |
2473 ThrowIfNotJSReceiver(this, isolate, context, maybe_receiver, | 2476 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, |
2474 MessageTemplate::kIncompatibleMethodReceiver, | 2477 "RegExp.prototype.@@replace"); |
2475 "RegExp.prototype.@@replace"); | |
2476 Node* const receiver = maybe_receiver; | 2478 Node* const receiver = maybe_receiver; |
2477 | 2479 |
2478 // Convert {maybe_string} to a String. | 2480 // Convert {maybe_string} to a String. |
2479 Callable tostring_callable = CodeFactory::ToString(isolate); | 2481 Callable tostring_callable = CodeFactory::ToString(isolate()); |
2480 Node* const string = CallStub(tostring_callable, context, maybe_string); | 2482 Node* const string = CallStub(tostring_callable, context, maybe_string); |
2481 | 2483 |
2482 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? | 2484 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? |
2483 CLabel checkreplacecallable(this), runtime(this, CLabel::kDeferred), | 2485 Label checkreplacecallable(this), runtime(this, Label::kDeferred), |
2484 fastpath(this); | 2486 fastpath(this); |
2485 BranchIfFastPath(this, context, map, &checkreplacecallable, &runtime); | 2487 BranchIfFastRegExp(context, map, &checkreplacecallable, &runtime); |
2486 | 2488 |
2487 Bind(&checkreplacecallable); | 2489 Bind(&checkreplacecallable); |
2488 Node* const regexp = receiver; | 2490 Node* const regexp = receiver; |
2489 | 2491 |
2490 // 2. Is {replace_value} callable? | 2492 // 2. Is {replace_value} callable? |
2491 CLabel checkreplacestring(this), if_iscallable(this); | 2493 Label checkreplacestring(this), if_iscallable(this); |
2492 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); | 2494 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); |
2493 | 2495 |
2494 Node* const replace_value_map = LoadMap(replace_value); | 2496 Node* const replace_value_map = LoadMap(replace_value); |
2495 Branch(IsCallableMap(replace_value_map), &if_iscallable, &checkreplacestring); | 2497 Branch(IsCallableMap(replace_value_map), &if_iscallable, &checkreplacestring); |
2496 | 2498 |
2497 // 3. Does ToString({replace_value}) contain '$'? | 2499 // 3. Does ToString({replace_value}) contain '$'? |
2498 Bind(&checkreplacestring); | 2500 Bind(&checkreplacestring); |
2499 { | 2501 { |
2500 Node* const replace_string = | 2502 Node* const replace_string = |
2501 CallStub(tostring_callable, context, replace_value); | 2503 CallStub(tostring_callable, context, replace_value); |
2502 | 2504 |
2503 Node* const dollar_char = IntPtrConstant('$'); | 2505 Node* const dollar_char = IntPtrConstant('$'); |
2504 Node* const smi_minusone = SmiConstant(Smi::FromInt(-1)); | 2506 Node* const smi_minusone = SmiConstant(Smi::FromInt(-1)); |
2505 GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char, | 2507 GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char, |
2506 int_zero), | 2508 int_zero), |
2507 smi_minusone), | 2509 smi_minusone), |
2508 &runtime); | 2510 &runtime); |
2509 | 2511 |
2510 Return(ReplaceSimpleStringFastPath(this, context, regexp, string, | 2512 Return( |
2511 replace_string)); | 2513 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); |
2512 } | 2514 } |
2513 | 2515 |
2514 // {regexp} is unmodified and {replace_value} is callable. | 2516 // {regexp} is unmodified and {replace_value} is callable. |
2515 Bind(&if_iscallable); | 2517 Bind(&if_iscallable); |
2516 { | 2518 { |
2517 Node* const replace_callable = replace_value; | 2519 Node* const replace_callable = replace_value; |
2518 | 2520 |
2519 // Check if the {regexp} is global. | 2521 // Check if the {regexp} is global. |
2520 CLabel if_isglobal(this), if_isnotglobal(this); | 2522 Label if_isglobal(this), if_isnotglobal(this); |
2521 Node* const is_global = FastFlagGetter(this, regexp, JSRegExp::kGlobal); | 2523 Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal); |
2522 Branch(is_global, &if_isglobal, &if_isnotglobal); | 2524 Branch(is_global, &if_isglobal, &if_isnotglobal); |
2523 | 2525 |
2524 Bind(&if_isglobal); | 2526 Bind(&if_isglobal); |
2525 { | 2527 { |
2526 Node* const result = ReplaceGlobalCallableFastPath( | 2528 Node* const result = ReplaceGlobalCallableFastPath( |
2527 this, context, regexp, string, replace_callable); | 2529 context, regexp, string, replace_callable); |
2528 Return(result); | 2530 Return(result); |
2529 } | 2531 } |
2530 | 2532 |
2531 Bind(&if_isnotglobal); | 2533 Bind(&if_isnotglobal); |
2532 { | 2534 { |
2533 Node* const result = | 2535 Node* const result = |
2534 CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, | 2536 CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, |
2535 context, string, regexp, replace_callable); | 2537 context, string, regexp, replace_callable); |
2536 Return(result); | 2538 Return(result); |
2537 } | 2539 } |
2538 } | 2540 } |
2539 | 2541 |
2540 Bind(&runtime); | 2542 Bind(&runtime); |
2541 { | 2543 { |
2542 Node* const result = CallRuntime(Runtime::kRegExpReplace, context, receiver, | 2544 Node* const result = CallRuntime(Runtime::kRegExpReplace, context, receiver, |
2543 string, replace_value); | 2545 string, replace_value); |
2544 Return(result); | 2546 Return(result); |
2545 } | 2547 } |
2546 } | 2548 } |
2547 | 2549 |
2548 // Simple string matching functionality for internal use which does not modify | 2550 // Simple string matching functionality for internal use which does not modify |
2549 // the last match info. | 2551 // the last match info. |
2550 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { | 2552 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { |
2551 Isolate* const isolate = this->isolate(); | |
2552 | |
2553 Node* const regexp = Parameter(1); | 2553 Node* const regexp = Parameter(1); |
2554 Node* const string = Parameter(2); | 2554 Node* const string = Parameter(2); |
2555 Node* const context = Parameter(5); | 2555 Node* const context = Parameter(5); |
2556 | 2556 |
2557 Node* const null = NullConstant(); | 2557 Node* const null = NullConstant(); |
2558 Node* const smi_zero = SmiConstant(Smi::FromInt(0)); | 2558 Node* const smi_zero = SmiConstant(Smi::FromInt(0)); |
2559 | 2559 |
2560 Node* const native_context = LoadNativeContext(context); | 2560 Node* const native_context = LoadNativeContext(context); |
2561 Node* const internal_match_info = LoadContextElement( | 2561 Node* const internal_match_info = LoadContextElement( |
2562 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); | 2562 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); |
2563 | 2563 |
2564 Callable exec_callable = CodeFactory::RegExpExec(isolate); | 2564 Callable exec_callable = CodeFactory::RegExpExec(isolate()); |
2565 Node* const match_indices = CallStub(exec_callable, context, regexp, string, | 2565 Node* const match_indices = CallStub(exec_callable, context, regexp, string, |
2566 smi_zero, internal_match_info); | 2566 smi_zero, internal_match_info); |
2567 | 2567 |
2568 CLabel if_matched(this), if_didnotmatch(this); | 2568 Label if_matched(this), if_didnotmatch(this); |
2569 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); | 2569 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); |
2570 | 2570 |
2571 Bind(&if_didnotmatch); | 2571 Bind(&if_didnotmatch); |
2572 Return(null); | 2572 Return(null); |
2573 | 2573 |
2574 Bind(&if_matched); | 2574 Bind(&if_matched); |
2575 { | 2575 { |
2576 Node* result = ConstructNewResultFromMatchInfo(isolate, this, context, | 2576 Node* result = |
2577 match_indices, string); | 2577 ConstructNewResultFromMatchInfo(context, match_indices, string); |
2578 Return(result); | 2578 Return(result); |
2579 } | 2579 } |
2580 } | 2580 } |
2581 | 2581 |
2582 } // namespace internal | 2582 } // namespace internal |
2583 } // namespace v8 | 2583 } // namespace v8 |
OLD | NEW |