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-constructor.h" | 5 #include "src/builtins/builtins-constructor.h" |
6 #include "src/builtins/builtins-utils.h" | 6 #include "src/builtins/builtins-utils.h" |
7 #include "src/builtins/builtins.h" | 7 #include "src/builtins/builtins.h" |
8 #include "src/code-factory.h" | 8 #include "src/code-factory.h" |
9 #include "src/code-stub-assembler.h" | 9 #include "src/code-stub-assembler.h" |
10 #include "src/regexp/jsregexp.h" | 10 #include "src/regexp/jsregexp.h" |
(...skipping 15 matching lines...) Expand all Loading... |
26 protected: | 26 protected: |
27 Node* FastLoadLastIndex(Node* regexp); | 27 Node* FastLoadLastIndex(Node* regexp); |
28 Node* SlowLoadLastIndex(Node* context, Node* regexp); | 28 Node* SlowLoadLastIndex(Node* context, Node* regexp); |
29 Node* LoadLastIndex(Node* context, Node* regexp, bool is_fastpath); | 29 Node* LoadLastIndex(Node* context, Node* regexp, bool is_fastpath); |
30 | 30 |
31 void FastStoreLastIndex(Node* regexp, Node* value); | 31 void FastStoreLastIndex(Node* regexp, Node* value); |
32 void SlowStoreLastIndex(Node* context, Node* regexp, Node* value); | 32 void SlowStoreLastIndex(Node* context, Node* regexp, Node* value); |
33 void StoreLastIndex(Node* context, Node* regexp, Node* value, | 33 void StoreLastIndex(Node* context, Node* regexp, Node* value, |
34 bool is_fastpath); | 34 bool is_fastpath); |
35 | 35 |
36 Node* ConstructNewResultFromMatchInfo(Node* const context, Node* const regexp, | 36 Node* ConstructNewResultFromMatchInfo(Node* context, Node* match_info, |
37 Node* const match_info, | 37 Node* string); |
38 Node* const string); | |
39 | 38 |
40 Node* RegExpPrototypeExecBodyWithoutResult(Node* const context, | 39 Node* RegExpPrototypeExecBodyWithoutResult(Node* const context, |
41 Node* const regexp, | 40 Node* const regexp, |
42 Node* const string, | 41 Node* const string, |
43 Label* if_didnotmatch, | 42 Label* if_didnotmatch, |
44 const bool is_fastpath); | 43 const bool is_fastpath); |
45 Node* RegExpPrototypeExecBody(Node* const context, Node* const regexp, | 44 Node* RegExpPrototypeExecBody(Node* const context, Node* const regexp, |
46 Node* const string, const bool is_fastpath); | 45 Node* const string, const bool is_fastpath); |
47 | 46 |
48 Node* ThrowIfNotJSReceiver(Node* context, Node* maybe_receiver, | 47 Node* ThrowIfNotJSReceiver(Node* context, Node* maybe_receiver, |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 | 134 |
136 void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp, | 135 void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp, |
137 Node* value, bool is_fastpath) { | 136 Node* value, bool is_fastpath) { |
138 if (is_fastpath) { | 137 if (is_fastpath) { |
139 FastStoreLastIndex(regexp, value); | 138 FastStoreLastIndex(regexp, value); |
140 } else { | 139 } else { |
141 SlowStoreLastIndex(context, regexp, value); | 140 SlowStoreLastIndex(context, regexp, value); |
142 } | 141 } |
143 } | 142 } |
144 | 143 |
145 Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo( | 144 Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(Node* context, |
146 Node* const context, Node* const regexp, Node* const match_info, | 145 Node* match_info, |
147 Node* const string) { | 146 Node* string) { |
148 Label named_captures(this), out(this); | 147 Label out(this); |
149 | 148 |
150 Node* const num_indices = SmiUntag(LoadFixedArrayElement( | 149 Node* const num_indices = SmiUntag(LoadFixedArrayElement( |
151 match_info, RegExpMatchInfo::kNumberOfCapturesIndex)); | 150 match_info, RegExpMatchInfo::kNumberOfCapturesIndex)); |
152 Node* const num_results = SmiTag(WordShr(num_indices, 1)); | 151 Node* const num_results = SmiTag(WordShr(num_indices, 1)); |
153 Node* const start = | 152 Node* const start = |
154 LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex); | 153 LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex); |
155 Node* const end = LoadFixedArrayElement( | 154 Node* const end = LoadFixedArrayElement( |
156 match_info, RegExpMatchInfo::kFirstCaptureIndex + 1); | 155 match_info, RegExpMatchInfo::kFirstCaptureIndex + 1); |
157 | 156 |
158 // Calculate the substring of the first match before creating the result array | 157 // Calculate the substring of the first match before creating the result array |
159 // to avoid an unnecessary write barrier storing the first result. | 158 // to avoid an unnecessary write barrier storing the first result. |
160 Node* const first = SubString(context, string, start, end); | 159 Node* const first = SubString(context, string, start, end); |
161 | 160 |
162 Node* const result = | 161 Node* const result = |
163 AllocateRegExpResult(context, num_results, start, string); | 162 AllocateRegExpResult(context, num_results, start, string); |
164 Node* const result_elements = LoadElements(result); | 163 Node* const result_elements = LoadElements(result); |
165 | 164 |
166 StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER); | 165 StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER); |
167 | 166 |
168 // If no captures exist we can skip named capture handling as well. | 167 GotoIf(SmiEqual(num_results, SmiConstant(Smi::FromInt(1))), &out); |
169 GotoIf(SmiEqual(num_results, SmiConstant(1)), &out); | |
170 | 168 |
171 // Store all remaining captures. | 169 // Store all remaining captures. |
172 Node* const limit = IntPtrAdd( | 170 Node* const limit = IntPtrAdd( |
173 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices); | 171 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices); |
174 | 172 |
175 Variable var_from_cursor(this, MachineType::PointerRepresentation()); | 173 Variable var_from_cursor(this, MachineType::PointerRepresentation()); |
176 Variable var_to_cursor(this, MachineType::PointerRepresentation()); | 174 Variable var_to_cursor(this, MachineType::PointerRepresentation()); |
177 | 175 |
178 var_from_cursor.Bind(IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2)); | 176 var_from_cursor.Bind(IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2)); |
179 var_to_cursor.Bind(IntPtrConstant(1)); | 177 var_to_cursor.Bind(IntPtrConstant(1)); |
180 | 178 |
181 Variable* vars[] = {&var_from_cursor, &var_to_cursor}; | 179 Variable* vars[] = {&var_from_cursor, &var_to_cursor}; |
182 Label loop(this, 2, vars); | 180 Label loop(this, 2, vars); |
183 | 181 |
184 Goto(&loop); | 182 Goto(&loop); |
185 Bind(&loop); | 183 Bind(&loop); |
186 { | 184 { |
187 Node* const from_cursor = var_from_cursor.value(); | 185 Node* const from_cursor = var_from_cursor.value(); |
188 Node* const to_cursor = var_to_cursor.value(); | 186 Node* const to_cursor = var_to_cursor.value(); |
189 Node* const start = LoadFixedArrayElement(match_info, from_cursor); | 187 Node* const start = LoadFixedArrayElement(match_info, from_cursor); |
190 | 188 |
191 Label next_iter(this); | 189 Label next_iter(this); |
192 GotoIf(SmiEqual(start, SmiConstant(-1)), &next_iter); | 190 GotoIf(SmiEqual(start, SmiConstant(Smi::FromInt(-1))), &next_iter); |
193 | 191 |
194 Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1)); | 192 Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1)); |
195 Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1); | 193 Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1); |
196 | 194 |
197 Node* const capture = SubString(context, string, start, end); | 195 Node* const capture = SubString(context, string, start, end); |
198 StoreFixedArrayElement(result_elements, to_cursor, capture); | 196 StoreFixedArrayElement(result_elements, to_cursor, capture); |
199 Goto(&next_iter); | 197 Goto(&next_iter); |
200 | 198 |
201 Bind(&next_iter); | 199 Bind(&next_iter); |
202 var_from_cursor.Bind(IntPtrAdd(from_cursor, IntPtrConstant(2))); | 200 var_from_cursor.Bind(IntPtrAdd(from_cursor, IntPtrConstant(2))); |
203 var_to_cursor.Bind(IntPtrAdd(to_cursor, IntPtrConstant(1))); | 201 var_to_cursor.Bind(IntPtrAdd(to_cursor, IntPtrConstant(1))); |
204 Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop, | 202 Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out); |
205 &named_captures); | |
206 } | |
207 | |
208 Bind(&named_captures); | |
209 { | |
210 // We reach this point only if captures exist, implying that this is an | |
211 // IRREGEXP JSRegExp. | |
212 | |
213 CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE)); | |
214 CSA_ASSERT(this, SmiGreaterThan(num_results, SmiConstant(1))); | |
215 | |
216 // Preparations for named capture properties. Exit early if the result does | |
217 // not have any named captures to minimize performance impact. | |
218 | |
219 Node* const data = LoadObjectField(regexp, JSRegExp::kDataOffset); | |
220 CSA_ASSERT(this, SmiEqual(LoadFixedArrayElement(data, JSRegExp::kTagIndex), | |
221 SmiConstant(JSRegExp::IRREGEXP))); | |
222 | |
223 // The names fixed array associates names at even indices with a capture | |
224 // index at odd indices. | |
225 Node* const names = | |
226 LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureNameMapIndex); | |
227 GotoIf(SmiEqual(names, SmiConstant(0)), &out); | |
228 | |
229 // Allocate a new object to store the named capture properties. | |
230 // TODO(jgruber): Could be optimized by adding the object map to the heap | |
231 // root list. | |
232 | |
233 Node* const native_context = LoadNativeContext(context); | |
234 Node* const object_function = | |
235 LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX); | |
236 Node* const object_function_map = LoadObjectField( | |
237 object_function, JSFunction::kPrototypeOrInitialMapOffset); | |
238 | |
239 Node* const group_object = AllocateJSObjectFromMap(object_function_map); | |
240 | |
241 // Store it on the result as a 'group' property. | |
242 | |
243 { | |
244 Node* const name = HeapConstant(isolate()->factory()->group_string()); | |
245 Node* const language_mode = SmiConstant(Smi::FromInt(STRICT)); | |
246 CallRuntime(Runtime::kSetProperty, context, result, name, group_object, | |
247 language_mode); | |
248 } | |
249 | |
250 // One or more named captures exist, add a property for each one. | |
251 | |
252 CSA_ASSERT(this, HasInstanceType(names, FIXED_ARRAY_TYPE)); | |
253 Node* const names_length = LoadAndUntagFixedArrayBaseLength(names); | |
254 CSA_ASSERT(this, IntPtrGreaterThan(names_length, IntPtrConstant(0))); | |
255 | |
256 Variable var_i(this, MachineType::PointerRepresentation()); | |
257 var_i.Bind(IntPtrConstant(0)); | |
258 | |
259 Variable* vars[] = {&var_i}; | |
260 const int vars_count = sizeof(vars) / sizeof(vars[0]); | |
261 Label loop(this, vars_count, vars); | |
262 | |
263 Goto(&loop); | |
264 Bind(&loop); | |
265 { | |
266 Node* const i = var_i.value(); | |
267 Node* const i_plus_1 = IntPtrAdd(i, IntPtrConstant(1)); | |
268 Node* const i_plus_2 = IntPtrAdd(i_plus_1, IntPtrConstant(1)); | |
269 | |
270 Node* const name = LoadFixedArrayElement(names, i); | |
271 Node* const index = LoadFixedArrayElement(names, i_plus_1); | |
272 Node* const capture = | |
273 LoadFixedArrayElement(result_elements, SmiUntag(index)); | |
274 | |
275 Node* const language_mode = SmiConstant(Smi::FromInt(STRICT)); | |
276 CallRuntime(Runtime::kSetProperty, context, group_object, name, capture, | |
277 language_mode); | |
278 | |
279 var_i.Bind(i_plus_2); | |
280 Branch(IntPtrGreaterThanOrEqual(var_i.value(), names_length), &out, | |
281 &loop); | |
282 } | |
283 } | 203 } |
284 | 204 |
285 Bind(&out); | 205 Bind(&out); |
286 return result; | 206 return result; |
287 } | 207 } |
288 | 208 |
289 // ES#sec-regexp.prototype.exec | 209 // ES#sec-regexp.prototype.exec |
290 // RegExp.prototype.exec ( string ) | 210 // RegExp.prototype.exec ( string ) |
291 // Implements the core of RegExp.prototype.exec but without actually | 211 // Implements the core of RegExp.prototype.exec but without actually |
292 // constructing the JSRegExpResult. Returns either null (if the RegExp did not | 212 // constructing the JSRegExpResult. Returns either null (if the RegExp did not |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
425 Variable var_result(this, MachineRepresentation::kTagged); | 345 Variable var_result(this, MachineRepresentation::kTagged); |
426 | 346 |
427 Label if_didnotmatch(this), out(this); | 347 Label if_didnotmatch(this), out(this); |
428 Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult( | 348 Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult( |
429 context, regexp, string, &if_didnotmatch, is_fastpath); | 349 context, regexp, string, &if_didnotmatch, is_fastpath); |
430 | 350 |
431 // Successful match. | 351 // Successful match. |
432 { | 352 { |
433 Node* const match_indices = indices_or_null; | 353 Node* const match_indices = indices_or_null; |
434 Node* const result = | 354 Node* const result = |
435 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); | 355 ConstructNewResultFromMatchInfo(context, match_indices, string); |
436 var_result.Bind(result); | 356 var_result.Bind(result); |
437 Goto(&out); | 357 Goto(&out); |
438 } | 358 } |
439 | 359 |
440 Bind(&if_didnotmatch); | 360 Bind(&if_didnotmatch); |
441 { | 361 { |
442 var_result.Bind(null); | 362 var_result.Bind(null); |
443 Goto(&out); | 363 Goto(&out); |
444 } | 364 } |
445 | 365 |
(...skipping 2149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2595 | 2515 |
2596 Label if_matched(this), if_didnotmatch(this); | 2516 Label if_matched(this), if_didnotmatch(this); |
2597 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); | 2517 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); |
2598 | 2518 |
2599 Bind(&if_didnotmatch); | 2519 Bind(&if_didnotmatch); |
2600 Return(null); | 2520 Return(null); |
2601 | 2521 |
2602 Bind(&if_matched); | 2522 Bind(&if_matched); |
2603 { | 2523 { |
2604 Node* result = | 2524 Node* result = |
2605 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); | 2525 ConstructNewResultFromMatchInfo(context, match_indices, string); |
2606 Return(result); | 2526 Return(result); |
2607 } | 2527 } |
2608 } | 2528 } |
2609 | 2529 |
2610 } // namespace internal | 2530 } // namespace internal |
2611 } // namespace v8 | 2531 } // namespace v8 |
OLD | NEW |