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* context, Node* match_info, | 36 Node* ConstructNewResultFromMatchInfo(Node* const context, Node* const regexp, |
37 Node* string); | 37 Node* const match_info, |
| 38 Node* const string); |
38 | 39 |
39 Node* RegExpPrototypeExecBodyWithoutResult(Node* const context, | 40 Node* RegExpPrototypeExecBodyWithoutResult(Node* const context, |
40 Node* const regexp, | 41 Node* const regexp, |
41 Node* const string, | 42 Node* const string, |
42 Label* if_didnotmatch, | 43 Label* if_didnotmatch, |
43 const bool is_fastpath); | 44 const bool is_fastpath); |
44 Node* RegExpPrototypeExecBody(Node* const context, Node* const regexp, | 45 Node* RegExpPrototypeExecBody(Node* const context, Node* const regexp, |
45 Node* const string, const bool is_fastpath); | 46 Node* const string, const bool is_fastpath); |
46 | 47 |
47 Node* ThrowIfNotJSReceiver(Node* context, Node* maybe_receiver, | 48 Node* ThrowIfNotJSReceiver(Node* context, Node* maybe_receiver, |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 | 135 |
135 void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp, | 136 void RegExpBuiltinsAssembler::StoreLastIndex(Node* context, Node* regexp, |
136 Node* value, bool is_fastpath) { | 137 Node* value, bool is_fastpath) { |
137 if (is_fastpath) { | 138 if (is_fastpath) { |
138 FastStoreLastIndex(regexp, value); | 139 FastStoreLastIndex(regexp, value); |
139 } else { | 140 } else { |
140 SlowStoreLastIndex(context, regexp, value); | 141 SlowStoreLastIndex(context, regexp, value); |
141 } | 142 } |
142 } | 143 } |
143 | 144 |
144 Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(Node* context, | 145 Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo( |
145 Node* match_info, | 146 Node* const context, Node* const regexp, Node* const match_info, |
146 Node* string) { | 147 Node* const string) { |
147 Label out(this); | 148 Label named_captures(this), out(this); |
148 | 149 |
149 Node* const num_indices = SmiUntag(LoadFixedArrayElement( | 150 Node* const num_indices = SmiUntag(LoadFixedArrayElement( |
150 match_info, RegExpMatchInfo::kNumberOfCapturesIndex)); | 151 match_info, RegExpMatchInfo::kNumberOfCapturesIndex)); |
151 Node* const num_results = SmiTag(WordShr(num_indices, 1)); | 152 Node* const num_results = SmiTag(WordShr(num_indices, 1)); |
152 Node* const start = | 153 Node* const start = |
153 LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex); | 154 LoadFixedArrayElement(match_info, RegExpMatchInfo::kFirstCaptureIndex); |
154 Node* const end = LoadFixedArrayElement( | 155 Node* const end = LoadFixedArrayElement( |
155 match_info, RegExpMatchInfo::kFirstCaptureIndex + 1); | 156 match_info, RegExpMatchInfo::kFirstCaptureIndex + 1); |
156 | 157 |
157 // Calculate the substring of the first match before creating the result array | 158 // Calculate the substring of the first match before creating the result array |
158 // to avoid an unnecessary write barrier storing the first result. | 159 // to avoid an unnecessary write barrier storing the first result. |
159 Node* const first = SubString(context, string, start, end); | 160 Node* const first = SubString(context, string, start, end); |
160 | 161 |
161 Node* const result = | 162 Node* const result = |
162 AllocateRegExpResult(context, num_results, start, string); | 163 AllocateRegExpResult(context, num_results, start, string); |
163 Node* const result_elements = LoadElements(result); | 164 Node* const result_elements = LoadElements(result); |
164 | 165 |
165 StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER); | 166 StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER); |
166 | 167 |
167 GotoIf(SmiEqual(num_results, SmiConstant(Smi::FromInt(1))), &out); | 168 // If no captures exist we can skip named capture handling as well. |
| 169 GotoIf(SmiEqual(num_results, SmiConstant(1)), &out); |
168 | 170 |
169 // Store all remaining captures. | 171 // Store all remaining captures. |
170 Node* const limit = IntPtrAdd( | 172 Node* const limit = IntPtrAdd( |
171 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices); | 173 IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), num_indices); |
172 | 174 |
173 Variable var_from_cursor(this, MachineType::PointerRepresentation()); | 175 Variable var_from_cursor(this, MachineType::PointerRepresentation()); |
174 Variable var_to_cursor(this, MachineType::PointerRepresentation()); | 176 Variable var_to_cursor(this, MachineType::PointerRepresentation()); |
175 | 177 |
176 var_from_cursor.Bind(IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2)); | 178 var_from_cursor.Bind(IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2)); |
177 var_to_cursor.Bind(IntPtrConstant(1)); | 179 var_to_cursor.Bind(IntPtrConstant(1)); |
178 | 180 |
179 Variable* vars[] = {&var_from_cursor, &var_to_cursor}; | 181 Variable* vars[] = {&var_from_cursor, &var_to_cursor}; |
180 Label loop(this, 2, vars); | 182 Label loop(this, 2, vars); |
181 | 183 |
182 Goto(&loop); | 184 Goto(&loop); |
183 Bind(&loop); | 185 Bind(&loop); |
184 { | 186 { |
185 Node* const from_cursor = var_from_cursor.value(); | 187 Node* const from_cursor = var_from_cursor.value(); |
186 Node* const to_cursor = var_to_cursor.value(); | 188 Node* const to_cursor = var_to_cursor.value(); |
187 Node* const start = LoadFixedArrayElement(match_info, from_cursor); | 189 Node* const start = LoadFixedArrayElement(match_info, from_cursor); |
188 | 190 |
189 Label next_iter(this); | 191 Label next_iter(this); |
190 GotoIf(SmiEqual(start, SmiConstant(Smi::FromInt(-1))), &next_iter); | 192 GotoIf(SmiEqual(start, SmiConstant(-1)), &next_iter); |
191 | 193 |
192 Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1)); | 194 Node* const from_cursor_plus1 = IntPtrAdd(from_cursor, IntPtrConstant(1)); |
193 Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1); | 195 Node* const end = LoadFixedArrayElement(match_info, from_cursor_plus1); |
194 | 196 |
195 Node* const capture = SubString(context, string, start, end); | 197 Node* const capture = SubString(context, string, start, end); |
196 StoreFixedArrayElement(result_elements, to_cursor, capture); | 198 StoreFixedArrayElement(result_elements, to_cursor, capture); |
197 Goto(&next_iter); | 199 Goto(&next_iter); |
198 | 200 |
199 Bind(&next_iter); | 201 Bind(&next_iter); |
200 var_from_cursor.Bind(IntPtrAdd(from_cursor, IntPtrConstant(2))); | 202 var_from_cursor.Bind(IntPtrAdd(from_cursor, IntPtrConstant(2))); |
201 var_to_cursor.Bind(IntPtrAdd(to_cursor, IntPtrConstant(1))); | 203 var_to_cursor.Bind(IntPtrAdd(to_cursor, IntPtrConstant(1))); |
202 Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop, &out); | 204 Branch(UintPtrLessThan(var_from_cursor.value(), limit), &loop, |
| 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 map = LoadContextElement( |
| 235 native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP); |
| 236 Node* const properties = |
| 237 AllocateNameDictionary(NameDictionary::kInitialCapacity); |
| 238 |
| 239 Node* const group_object = AllocateJSObjectFromMap(map, properties); |
| 240 |
| 241 // Store it on the result as a 'group' property. |
| 242 |
| 243 { |
| 244 Node* const name = HeapConstant(isolate()->factory()->group_string()); |
| 245 CallRuntime(Runtime::kCreateDataProperty, context, result, name, |
| 246 group_object); |
| 247 } |
| 248 |
| 249 // One or more named captures exist, add a property for each one. |
| 250 |
| 251 CSA_ASSERT(this, HasInstanceType(names, FIXED_ARRAY_TYPE)); |
| 252 Node* const names_length = LoadAndUntagFixedArrayBaseLength(names); |
| 253 CSA_ASSERT(this, IntPtrGreaterThan(names_length, IntPtrConstant(0))); |
| 254 |
| 255 Variable var_i(this, MachineType::PointerRepresentation()); |
| 256 var_i.Bind(IntPtrConstant(0)); |
| 257 |
| 258 Variable* vars[] = {&var_i}; |
| 259 const int vars_count = sizeof(vars) / sizeof(vars[0]); |
| 260 Label loop(this, vars_count, vars); |
| 261 |
| 262 Goto(&loop); |
| 263 Bind(&loop); |
| 264 { |
| 265 Node* const i = var_i.value(); |
| 266 Node* const i_plus_1 = IntPtrAdd(i, IntPtrConstant(1)); |
| 267 Node* const i_plus_2 = IntPtrAdd(i_plus_1, IntPtrConstant(1)); |
| 268 |
| 269 Node* const name = LoadFixedArrayElement(names, i); |
| 270 Node* const index = LoadFixedArrayElement(names, i_plus_1); |
| 271 Node* const capture = |
| 272 LoadFixedArrayElement(result_elements, SmiUntag(index)); |
| 273 |
| 274 CallRuntime(Runtime::kCreateDataProperty, context, group_object, name, |
| 275 capture); |
| 276 |
| 277 var_i.Bind(i_plus_2); |
| 278 Branch(IntPtrGreaterThanOrEqual(var_i.value(), names_length), &out, |
| 279 &loop); |
| 280 } |
203 } | 281 } |
204 | 282 |
205 Bind(&out); | 283 Bind(&out); |
206 return result; | 284 return result; |
207 } | 285 } |
208 | 286 |
209 // ES#sec-regexp.prototype.exec | 287 // ES#sec-regexp.prototype.exec |
210 // RegExp.prototype.exec ( string ) | 288 // RegExp.prototype.exec ( string ) |
211 // Implements the core of RegExp.prototype.exec but without actually | 289 // Implements the core of RegExp.prototype.exec but without actually |
212 // constructing the JSRegExpResult. Returns either null (if the RegExp did not | 290 // constructing the JSRegExpResult. Returns either null (if the RegExp did not |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
345 Variable var_result(this, MachineRepresentation::kTagged); | 423 Variable var_result(this, MachineRepresentation::kTagged); |
346 | 424 |
347 Label if_didnotmatch(this), out(this); | 425 Label if_didnotmatch(this), out(this); |
348 Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult( | 426 Node* const indices_or_null = RegExpPrototypeExecBodyWithoutResult( |
349 context, regexp, string, &if_didnotmatch, is_fastpath); | 427 context, regexp, string, &if_didnotmatch, is_fastpath); |
350 | 428 |
351 // Successful match. | 429 // Successful match. |
352 { | 430 { |
353 Node* const match_indices = indices_or_null; | 431 Node* const match_indices = indices_or_null; |
354 Node* const result = | 432 Node* const result = |
355 ConstructNewResultFromMatchInfo(context, match_indices, string); | 433 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); |
356 var_result.Bind(result); | 434 var_result.Bind(result); |
357 Goto(&out); | 435 Goto(&out); |
358 } | 436 } |
359 | 437 |
360 Bind(&if_didnotmatch); | 438 Bind(&if_didnotmatch); |
361 { | 439 { |
362 var_result.Bind(null); | 440 var_result.Bind(null); |
363 Goto(&out); | 441 Goto(&out); |
364 } | 442 } |
365 | 443 |
(...skipping 2149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2515 | 2593 |
2516 Label if_matched(this), if_didnotmatch(this); | 2594 Label if_matched(this), if_didnotmatch(this); |
2517 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); | 2595 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); |
2518 | 2596 |
2519 Bind(&if_didnotmatch); | 2597 Bind(&if_didnotmatch); |
2520 Return(null); | 2598 Return(null); |
2521 | 2599 |
2522 Bind(&if_matched); | 2600 Bind(&if_matched); |
2523 { | 2601 { |
2524 Node* result = | 2602 Node* result = |
2525 ConstructNewResultFromMatchInfo(context, match_indices, string); | 2603 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); |
2526 Return(result); | 2604 Return(result); |
2527 } | 2605 } |
2528 } | 2606 } |
2529 | 2607 |
2530 } // namespace internal | 2608 } // namespace internal |
2531 } // namespace v8 | 2609 } // namespace v8 |
OLD | NEW |