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" |
(...skipping 531 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
542 Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map)); | 542 Node* const proto_map = a->LoadMap(a->LoadMapPrototype(map)); |
543 Node* const proto_has_initialmap = | 543 Node* const proto_has_initialmap = |
544 a->WordEqual(proto_map, initial_proto_initial_map); | 544 a->WordEqual(proto_map, initial_proto_initial_map); |
545 | 545 |
546 // TODO(ishell): Update this check once map changes for constant field | 546 // TODO(ishell): Update this check once map changes for constant field |
547 // tracking are landing. | 547 // tracking are landing. |
548 | 548 |
549 a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); | 549 a->Branch(proto_has_initialmap, if_isunmodified, if_ismodified); |
550 } | 550 } |
551 | 551 |
| 552 void BranchIfFastRegExpResult(CodeStubAssembler* a, Node* context, Node* map, |
| 553 CLabel* if_isunmodified, CLabel* if_ismodified) { |
| 554 Node* const native_context = a->LoadNativeContext(context); |
| 555 Node* const initial_regexp_result_map = |
| 556 a->LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); |
| 557 |
| 558 a->Branch(a->WordEqual(map, initial_regexp_result_map), if_isunmodified, |
| 559 if_ismodified); |
| 560 } |
| 561 |
552 } // namespace | 562 } // namespace |
553 | 563 |
554 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeAssemblerState* state) { | 564 void Builtins::Generate_RegExpPrototypeFlagsGetter(CodeAssemblerState* state) { |
555 CodeStubAssembler a(state); | 565 CodeStubAssembler a(state); |
556 | 566 |
557 Node* const receiver = a.Parameter(0); | 567 Node* const receiver = a.Parameter(0); |
558 Node* const context = a.Parameter(3); | 568 Node* const context = a.Parameter(3); |
559 | 569 |
560 Isolate* isolate = a.isolate(); | 570 Isolate* isolate = a.isolate(); |
561 Node* const int_zero = a.IntPtrConstant(0); | 571 Node* const int_zero = a.IntPtrConstant(0); |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
756 Node* FastFlagGetter(CodeStubAssembler* a, Node* const regexp, | 766 Node* FastFlagGetter(CodeStubAssembler* a, Node* const regexp, |
757 JSRegExp::Flag flag) { | 767 JSRegExp::Flag flag) { |
758 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 768 Node* const smi_zero = a->SmiConstant(Smi::kZero); |
759 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); | 769 Node* const flags = a->LoadObjectField(regexp, JSRegExp::kFlagsOffset); |
760 Node* const mask = a->SmiConstant(Smi::FromInt(flag)); | 770 Node* const mask = a->SmiConstant(Smi::FromInt(flag)); |
761 Node* const is_flag_set = a->WordNotEqual(a->WordAnd(flags, mask), smi_zero); | 771 Node* const is_flag_set = a->WordNotEqual(a->WordAnd(flags, mask), smi_zero); |
762 | 772 |
763 return is_flag_set; | 773 return is_flag_set; |
764 } | 774 } |
765 | 775 |
| 776 // Load through the GetProperty stub. |
| 777 compiler::Node* SlowFlagGetter(CodeStubAssembler* a, |
| 778 compiler::Node* const context, |
| 779 compiler::Node* const regexp, |
| 780 JSRegExp::Flag flag) { |
| 781 Factory* factory = a->isolate()->factory(); |
| 782 |
| 783 CLabel out(a); |
| 784 CVariable var_result(a, MachineType::PointerRepresentation()); |
| 785 |
| 786 Node* name; |
| 787 |
| 788 switch (flag) { |
| 789 case JSRegExp::kGlobal: |
| 790 name = a->HeapConstant(factory->global_string()); |
| 791 break; |
| 792 case JSRegExp::kIgnoreCase: |
| 793 name = a->HeapConstant(factory->ignoreCase_string()); |
| 794 break; |
| 795 case JSRegExp::kMultiline: |
| 796 name = a->HeapConstant(factory->multiline_string()); |
| 797 break; |
| 798 case JSRegExp::kSticky: |
| 799 name = a->HeapConstant(factory->sticky_string()); |
| 800 break; |
| 801 case JSRegExp::kUnicode: |
| 802 name = a->HeapConstant(factory->unicode_string()); |
| 803 break; |
| 804 default: |
| 805 UNREACHABLE(); |
| 806 } |
| 807 |
| 808 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); |
| 809 Node* const value = a->CallStub(getproperty_callable, context, regexp, name); |
| 810 |
| 811 CLabel if_true(a), if_false(a); |
| 812 a->BranchIfToBooleanIsTrue(value, &if_true, &if_false); |
| 813 |
| 814 a->Bind(&if_true); |
| 815 { |
| 816 var_result.Bind(a->IntPtrConstant(1)); |
| 817 a->Goto(&out); |
| 818 } |
| 819 |
| 820 a->Bind(&if_false); |
| 821 { |
| 822 var_result.Bind(a->IntPtrConstant(0)); |
| 823 a->Goto(&out); |
| 824 } |
| 825 |
| 826 a->Bind(&out); |
| 827 return var_result.value(); |
| 828 } |
| 829 |
| 830 compiler::Node* FlagGetter(CodeStubAssembler* a, compiler::Node* const context, |
| 831 compiler::Node* const regexp, JSRegExp::Flag flag, |
| 832 bool is_fastpath) { |
| 833 return is_fastpath ? FastFlagGetter(a, regexp, flag) |
| 834 : SlowFlagGetter(a, context, regexp, flag); |
| 835 } |
| 836 |
766 void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag, | 837 void Generate_FlagGetter(CodeStubAssembler* a, JSRegExp::Flag flag, |
767 v8::Isolate::UseCounterFeature counter, | 838 v8::Isolate::UseCounterFeature counter, |
768 const char* method_name) { | 839 const char* method_name) { |
769 Node* const receiver = a->Parameter(0); | 840 Node* const receiver = a->Parameter(0); |
770 Node* const context = a->Parameter(3); | 841 Node* const context = a->Parameter(3); |
771 | 842 |
772 Isolate* isolate = a->isolate(); | 843 Isolate* isolate = a->isolate(); |
773 | 844 |
774 // Check whether we have an unmodified regexp instance. | 845 // Check whether we have an unmodified regexp instance. |
775 CLabel if_isunmodifiedjsregexp(a), | 846 CLabel if_isunmodifiedjsregexp(a), |
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1042 | 1113 |
1043 // Call exec. | 1114 // Call exec. |
1044 Node* const match_indices = RegExpExec(&a, context, receiver, string); | 1115 Node* const match_indices = RegExpExec(&a, context, receiver, string); |
1045 | 1116 |
1046 // Return true iff exec matched successfully. | 1117 // Return true iff exec matched successfully. |
1047 Node* const result = a.Select(a.WordEqual(match_indices, a.NullConstant()), | 1118 Node* const result = a.Select(a.WordEqual(match_indices, a.NullConstant()), |
1048 a.FalseConstant(), a.TrueConstant()); | 1119 a.FalseConstant(), a.TrueConstant()); |
1049 a.Return(result); | 1120 a.Return(result); |
1050 } | 1121 } |
1051 | 1122 |
| 1123 namespace { |
| 1124 |
| 1125 Node* AdvanceStringIndex(CodeStubAssembler* a, Node* const string, |
| 1126 Node* const index, Node* const is_unicode) { |
| 1127 CVariable var_result(a, MachineRepresentation::kTagged); |
| 1128 |
| 1129 // Default to last_index + 1. |
| 1130 Node* const index_plus_one = a->SmiAdd(index, a->SmiConstant(1)); |
| 1131 var_result.Bind(index_plus_one); |
| 1132 |
| 1133 CLabel if_isunicode(a), out(a); |
| 1134 a->Branch(is_unicode, &if_isunicode, &out); |
| 1135 |
| 1136 a->Bind(&if_isunicode); |
| 1137 { |
| 1138 Node* const string_length = a->LoadStringLength(string); |
| 1139 a->GotoUnless(a->SmiLessThan(index_plus_one, string_length), &out); |
| 1140 |
| 1141 Node* const lead = a->StringCharCodeAt(string, index); |
| 1142 a->GotoUnless(a->Word32Equal(a->Word32And(lead, a->Int32Constant(0xFC00)), |
| 1143 a->Int32Constant(0xD800)), |
| 1144 &out); |
| 1145 |
| 1146 Node* const trail = a->StringCharCodeAt(string, index_plus_one); |
| 1147 a->GotoUnless(a->Word32Equal(a->Word32And(trail, a->Int32Constant(0xFC00)), |
| 1148 a->Int32Constant(0xDC00)), |
| 1149 &out); |
| 1150 |
| 1151 // At a surrogate pair, return index + 2. |
| 1152 Node* const index_plus_two = a->SmiAdd(index, a->SmiConstant(2)); |
| 1153 var_result.Bind(index_plus_two); |
| 1154 |
| 1155 a->Goto(&out); |
| 1156 } |
| 1157 |
| 1158 a->Bind(&out); |
| 1159 return var_result.value(); |
| 1160 } |
| 1161 |
| 1162 // Utility class implementing a growable fixed array through CSA. |
| 1163 class GrowableFixedArray { |
| 1164 public: |
| 1165 explicit GrowableFixedArray(CodeStubAssembler* a) |
| 1166 : assembler_(a), |
| 1167 var_array_(a, MachineRepresentation::kTagged), |
| 1168 var_length_(a, MachineType::PointerRepresentation()), |
| 1169 var_capacity_(a, MachineType::PointerRepresentation()) { |
| 1170 Initialize(); |
| 1171 } |
| 1172 |
| 1173 Node* length() const { return var_length_.value(); } |
| 1174 |
| 1175 CVariable* var_array() { return &var_array_; } |
| 1176 CVariable* var_length() { return &var_length_; } |
| 1177 CVariable* var_capacity() { return &var_capacity_; } |
| 1178 |
| 1179 void Push(Node* const value) { |
| 1180 CodeStubAssembler* a = assembler_; |
| 1181 |
| 1182 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; |
| 1183 const CodeStubAssembler::ParameterMode mode = |
| 1184 CodeStubAssembler::INTPTR_PARAMETERS; |
| 1185 |
| 1186 Node* const length = var_length_.value(); |
| 1187 Node* const capacity = var_capacity_.value(); |
| 1188 |
| 1189 CLabel grow(a), store(a); |
| 1190 a->Branch(a->IntPtrEqual(capacity, length), &grow, &store); |
| 1191 |
| 1192 a->Bind(&grow); |
| 1193 { |
| 1194 Node* const new_capacity = NewCapacity(a, capacity); |
| 1195 Node* const new_array = GrowFixedArray(capacity, new_capacity, mode); |
| 1196 |
| 1197 var_capacity_.Bind(new_capacity); |
| 1198 var_array_.Bind(new_array); |
| 1199 a->Goto(&store); |
| 1200 } |
| 1201 |
| 1202 a->Bind(&store); |
| 1203 { |
| 1204 Node* const array = var_array_.value(); |
| 1205 a->StoreFixedArrayElement(array, length, value, barrier_mode, 0, mode); |
| 1206 |
| 1207 Node* const new_length = a->IntPtrAdd(length, a->IntPtrConstant(1)); |
| 1208 var_length_.Bind(new_length); |
| 1209 } |
| 1210 } |
| 1211 |
| 1212 Node* ToJSArray(Node* const context) { |
| 1213 CodeStubAssembler* a = assembler_; |
| 1214 |
| 1215 const ElementsKind kind = FAST_ELEMENTS; |
| 1216 |
| 1217 Node* const native_context = a->LoadNativeContext(context); |
| 1218 Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context); |
| 1219 |
| 1220 Node* const result_length = a->SmiTag(length()); |
| 1221 Node* const result = a->AllocateUninitializedJSArrayWithoutElements( |
| 1222 kind, array_map, result_length, nullptr); |
| 1223 |
| 1224 // Note: We do not currently shrink the fixed array. |
| 1225 |
| 1226 a->StoreObjectField(result, JSObject::kElementsOffset, var_array_.value()); |
| 1227 |
| 1228 return result; |
| 1229 } |
| 1230 |
| 1231 private: |
| 1232 void Initialize() { |
| 1233 CodeStubAssembler* a = assembler_; |
| 1234 |
| 1235 const ElementsKind kind = FAST_ELEMENTS; |
| 1236 const CodeStubAssembler::ParameterMode mode = |
| 1237 CodeStubAssembler::INTPTR_PARAMETERS; |
| 1238 |
| 1239 static const int kInitialArraySize = 8; |
| 1240 Node* const capacity = a->IntPtrConstant(kInitialArraySize); |
| 1241 Node* const array = a->AllocateFixedArray(kind, capacity, mode); |
| 1242 |
| 1243 a->FillFixedArrayWithValue(kind, array, a->IntPtrConstant(0), capacity, |
| 1244 Heap::kTheHoleValueRootIndex, mode); |
| 1245 |
| 1246 var_array_.Bind(array); |
| 1247 var_capacity_.Bind(capacity); |
| 1248 var_length_.Bind(a->IntPtrConstant(0)); |
| 1249 } |
| 1250 |
| 1251 Node* NewCapacity(CodeStubAssembler* a, Node* const current_capacity) { |
| 1252 CSA_ASSERT(a, a->IntPtrGreaterThan(current_capacity, a->IntPtrConstant(0))); |
| 1253 |
| 1254 // Growth rate is analog to JSObject::NewElementsCapacity: |
| 1255 // new_capacity = (current_capacity + (current_capacity >> 1)) + 16. |
| 1256 |
| 1257 Node* const new_capacity = a->IntPtrAdd( |
| 1258 a->IntPtrAdd(current_capacity, a->WordShr(current_capacity, 1)), |
| 1259 a->IntPtrConstant(16)); |
| 1260 |
| 1261 return new_capacity; |
| 1262 } |
| 1263 |
| 1264 Node* GrowFixedArray(Node* const current_capacity, Node* const new_capacity, |
| 1265 CodeStubAssembler::ParameterMode mode) { |
| 1266 DCHECK(mode == CodeStubAssembler::INTPTR_PARAMETERS); |
| 1267 |
| 1268 CodeStubAssembler* a = assembler_; |
| 1269 |
| 1270 CSA_ASSERT(a, a->IntPtrGreaterThan(current_capacity, a->IntPtrConstant(0))); |
| 1271 CSA_ASSERT(a, a->IntPtrGreaterThan(new_capacity, current_capacity)); |
| 1272 |
| 1273 const ElementsKind kind = FAST_ELEMENTS; |
| 1274 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER; |
| 1275 |
| 1276 Node* const from_array = var_array_.value(); |
| 1277 Node* const to_array = a->AllocateFixedArray(kind, new_capacity, mode); |
| 1278 a->CopyFixedArrayElements(kind, from_array, kind, to_array, |
| 1279 current_capacity, new_capacity, barrier_mode, |
| 1280 mode); |
| 1281 |
| 1282 return to_array; |
| 1283 } |
| 1284 |
| 1285 private: |
| 1286 CodeStubAssembler* const assembler_; |
| 1287 CVariable var_array_; |
| 1288 CVariable var_length_; |
| 1289 CVariable var_capacity_; |
| 1290 }; |
| 1291 |
| 1292 void Generate_RegExpPrototypeMatchBody(CodeStubAssembler* a, |
| 1293 Node* const receiver, Node* const string, |
| 1294 Node* const context, |
| 1295 const bool is_fastpath) { |
| 1296 Isolate* const isolate = a->isolate(); |
| 1297 |
| 1298 Node* const null = a->NullConstant(); |
| 1299 Node* const int_zero = a->IntPtrConstant(0); |
| 1300 Node* const smi_zero = a->SmiConstant(Smi::kZero); |
| 1301 |
| 1302 Node* const regexp = receiver; |
| 1303 Node* const is_global = |
| 1304 FlagGetter(a, context, regexp, JSRegExp::kGlobal, is_fastpath); |
| 1305 |
| 1306 CLabel if_isglobal(a), if_isnotglobal(a); |
| 1307 a->Branch(is_global, &if_isglobal, &if_isnotglobal); |
| 1308 |
| 1309 a->Bind(&if_isnotglobal); |
| 1310 { |
| 1311 Node* const result = |
| 1312 is_fastpath ? RegExpPrototypeExecInternal(a, context, regexp, string) |
| 1313 : RegExpExec(a, context, regexp, string); |
| 1314 a->Return(result); |
| 1315 } |
| 1316 |
| 1317 a->Bind(&if_isglobal); |
| 1318 { |
| 1319 Node* const is_unicode = |
| 1320 FlagGetter(a, context, regexp, JSRegExp::kUnicode, is_fastpath); |
| 1321 |
| 1322 if (is_fastpath) { |
| 1323 FastStoreLastIndex(a, context, regexp, smi_zero); |
| 1324 } else { |
| 1325 SlowStoreLastIndex(a, context, regexp, smi_zero); |
| 1326 } |
| 1327 |
| 1328 // Allocate an array to store the resulting match strings. |
| 1329 |
| 1330 GrowableFixedArray array(a); |
| 1331 |
| 1332 // Loop preparations. Within the loop, collect results from RegExpExec |
| 1333 // and store match strings in the array. |
| 1334 |
| 1335 CVariable* vars[] = {array.var_array(), array.var_length(), |
| 1336 array.var_capacity()}; |
| 1337 CLabel loop(a, 3, vars), out(a); |
| 1338 a->Goto(&loop); |
| 1339 |
| 1340 a->Bind(&loop); |
| 1341 { |
| 1342 Node* const result = |
| 1343 is_fastpath ? RegExpPrototypeExecInternal(a, context, regexp, string) |
| 1344 : RegExpExec(a, context, regexp, string); |
| 1345 |
| 1346 CLabel if_didmatch(a), if_didnotmatch(a); |
| 1347 a->Branch(a->WordEqual(result, null), &if_didnotmatch, &if_didmatch); |
| 1348 |
| 1349 a->Bind(&if_didnotmatch); |
| 1350 { |
| 1351 // Return null if there were no matches, otherwise just exit the loop. |
| 1352 a->GotoUnless(a->IntPtrEqual(array.length(), int_zero), &out); |
| 1353 a->Return(null); |
| 1354 } |
| 1355 |
| 1356 a->Bind(&if_didmatch); |
| 1357 { |
| 1358 Node* match = nullptr; |
| 1359 if (is_fastpath) { |
| 1360 // TODO(jgruber): We could optimize further here and in other |
| 1361 // methods (e.g. @@search) by bypassing RegExp result construction. |
| 1362 Node* const result_fixed_array = a->LoadElements(result); |
| 1363 const CodeStubAssembler::ParameterMode mode = |
| 1364 CodeStubAssembler::INTPTR_PARAMETERS; |
| 1365 match = |
| 1366 a->LoadFixedArrayElement(result_fixed_array, int_zero, 0, mode); |
| 1367 |
| 1368 // The match is guaranteed to be a string on the fast path. |
| 1369 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(match))); |
| 1370 } else { |
| 1371 DCHECK(!is_fastpath); |
| 1372 |
| 1373 CVariable var_match(a, MachineRepresentation::kTagged); |
| 1374 CLabel fast_result(a), slow_result(a), match_loaded(a); |
| 1375 BranchIfFastRegExpResult(a, context, a->LoadMap(result), &fast_result, |
| 1376 &slow_result); |
| 1377 |
| 1378 a->Bind(&fast_result); |
| 1379 { |
| 1380 // TODO(jgruber): We could optimize further here and in other |
| 1381 // methods (e.g. @@search) by bypassing RegExp result construction. |
| 1382 Node* const result_fixed_array = a->LoadElements(result); |
| 1383 const CodeStubAssembler::ParameterMode mode = |
| 1384 CodeStubAssembler::INTPTR_PARAMETERS; |
| 1385 Node* const match = |
| 1386 a->LoadFixedArrayElement(result_fixed_array, int_zero, 0, mode); |
| 1387 |
| 1388 // The match is guaranteed to be a string on the fast path. |
| 1389 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(match))); |
| 1390 |
| 1391 var_match.Bind(match); |
| 1392 a->Goto(&match_loaded); |
| 1393 } |
| 1394 |
| 1395 a->Bind(&slow_result); |
| 1396 { |
| 1397 // TODO(ishell): Use GetElement stub once it's available. |
| 1398 Node* const name = smi_zero; |
| 1399 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 1400 Node* const match = |
| 1401 a->CallStub(getproperty_callable, context, result, name); |
| 1402 |
| 1403 var_match.Bind(match); |
| 1404 a->Goto(&match_loaded); |
| 1405 } |
| 1406 |
| 1407 a->Bind(&match_loaded); |
| 1408 match = a->ToString(context, var_match.value()); |
| 1409 } |
| 1410 DCHECK(match != nullptr); |
| 1411 |
| 1412 // Store the match, growing the fixed array if needed. |
| 1413 |
| 1414 array.Push(match); |
| 1415 |
| 1416 // Advance last index if the match is the empty string. |
| 1417 |
| 1418 Node* const match_length = a->LoadStringLength(match); |
| 1419 a->GotoUnless(a->SmiEqual(match_length, smi_zero), &loop); |
| 1420 |
| 1421 Node* last_index = is_fastpath ? FastLoadLastIndex(a, context, regexp) |
| 1422 : SlowLoadLastIndex(a, context, regexp); |
| 1423 |
| 1424 Callable tolength_callable = CodeFactory::ToLength(isolate); |
| 1425 last_index = a->CallStub(tolength_callable, context, last_index); |
| 1426 |
| 1427 Node* const new_last_index = |
| 1428 AdvanceStringIndex(a, string, last_index, is_unicode); |
| 1429 |
| 1430 if (is_fastpath) { |
| 1431 FastStoreLastIndex(a, context, regexp, new_last_index); |
| 1432 } else { |
| 1433 SlowStoreLastIndex(a, context, regexp, new_last_index); |
| 1434 } |
| 1435 |
| 1436 a->Goto(&loop); |
| 1437 } |
| 1438 } |
| 1439 |
| 1440 a->Bind(&out); |
| 1441 { |
| 1442 // Wrap the match in a JSArray. |
| 1443 |
| 1444 Node* const result = array.ToJSArray(context); |
| 1445 a->Return(result); |
| 1446 } |
| 1447 } |
| 1448 } |
| 1449 |
| 1450 } // namespace |
| 1451 |
1052 // ES#sec-regexp.prototype-@@match | 1452 // ES#sec-regexp.prototype-@@match |
1053 // RegExp.prototype [ @@match ] ( string ) | 1453 // RegExp.prototype [ @@match ] ( string ) |
1054 BUILTIN(RegExpPrototypeMatch) { | 1454 void Builtins::Generate_RegExpPrototypeMatch(CodeAssemblerState* state) { |
1055 HandleScope scope(isolate); | 1455 CodeStubAssembler a(state); |
1056 CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.@@match"); | 1456 |
1057 | 1457 Node* const maybe_receiver = a.Parameter(0); |
1058 Handle<Object> string_obj = args.atOrUndefined(isolate, 1); | 1458 Node* const maybe_string = a.Parameter(1); |
1059 | 1459 Node* const context = a.Parameter(4); |
1060 Handle<String> string; | 1460 |
1061 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string, | 1461 // Ensure {maybe_receiver} is a JSReceiver. |
1062 Object::ToString(isolate, string_obj)); | 1462 Node* const map = ThrowIfNotJSReceiver( |
1063 | 1463 &a, a.isolate(), context, maybe_receiver, |
1064 Handle<Object> global_obj; | 1464 MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.@@match"); |
1065 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | 1465 Node* const receiver = maybe_receiver; |
1066 isolate, global_obj, | 1466 |
1067 JSReceiver::GetProperty(recv, isolate->factory()->global_string())); | 1467 // Convert {maybe_string} to a String. |
1068 const bool global = global_obj->BooleanValue(); | 1468 Node* const string = a.ToString(context, maybe_string); |
1069 | 1469 |
1070 if (!global) { | 1470 CLabel fast_path(&a), slow_path(&a); |
1071 RETURN_RESULT_OR_FAILURE( | 1471 BranchIfFastPath(&a, context, map, &fast_path, &slow_path); |
1072 isolate, | 1472 |
1073 RegExpUtils::RegExpExec(isolate, recv, string, | 1473 a.Bind(&fast_path); |
1074 isolate->factory()->undefined_value())); | 1474 Generate_RegExpPrototypeMatchBody(&a, receiver, string, context, true); |
1075 } | 1475 |
1076 | 1476 a.Bind(&slow_path); |
1077 Handle<Object> unicode_obj; | 1477 Generate_RegExpPrototypeMatchBody(&a, receiver, string, context, false); |
1078 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1079 isolate, unicode_obj, | |
1080 JSReceiver::GetProperty(recv, isolate->factory()->unicode_string())); | |
1081 const bool unicode = unicode_obj->BooleanValue(); | |
1082 | |
1083 RETURN_FAILURE_ON_EXCEPTION(isolate, | |
1084 RegExpUtils::SetLastIndex(isolate, recv, 0)); | |
1085 | |
1086 static const int kInitialArraySize = 8; | |
1087 Handle<FixedArray> elems = | |
1088 isolate->factory()->NewFixedArrayWithHoles(kInitialArraySize); | |
1089 | |
1090 int n = 0; | |
1091 for (;; n++) { | |
1092 Handle<Object> result; | |
1093 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
1094 isolate, result, | |
1095 RegExpUtils::RegExpExec(isolate, recv, string, | |
1096 isolate->factory()->undefined_value())); | |
1097 | |
1098 if (result->IsNull(isolate)) { | |
1099 if (n == 0) return isolate->heap()->null_value(); | |
1100 break; | |
1101 } | |
1102 | |
1103 Handle<Object> match_obj; | |
1104 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match_obj, | |
1105 Object::GetElement(isolate, result, 0)); | |
1106 | |
1107 Handle<String> match; | |
1108 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, match, | |
1109 Object::ToString(isolate, match_obj)); | |
1110 | |
1111 elems = FixedArray::SetAndGrow(elems, n, match); | |
1112 | |
1113 if (match->length() == 0) { | |
1114 RETURN_FAILURE_ON_EXCEPTION(isolate, RegExpUtils::SetAdvancedStringIndex( | |
1115 isolate, recv, string, unicode)); | |
1116 } | |
1117 } | |
1118 | |
1119 elems->Shrink(n); | |
1120 return *isolate->factory()->NewJSArrayWithElements(elems); | |
1121 } | 1478 } |
1122 | 1479 |
1123 namespace { | 1480 namespace { |
1124 | 1481 |
1125 void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, | 1482 void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, |
1126 Node* const receiver, | 1483 Node* const receiver, |
1127 Node* const string, Node* const context, | 1484 Node* const string, Node* const context, |
1128 bool is_fastpath) { | 1485 bool is_fastpath) { |
1129 Isolate* const isolate = a->isolate(); | 1486 Isolate* const isolate = a->isolate(); |
1130 | 1487 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1169 | 1526 |
1170 // Return -1 if no match was found. | 1527 // Return -1 if no match was found. |
1171 { | 1528 { |
1172 CLabel next(a); | 1529 CLabel next(a); |
1173 a->GotoUnless(a->WordEqual(match_indices, a->NullConstant()), &next); | 1530 a->GotoUnless(a->WordEqual(match_indices, a->NullConstant()), &next); |
1174 a->Return(a->SmiConstant(-1)); | 1531 a->Return(a->SmiConstant(-1)); |
1175 a->Bind(&next); | 1532 a->Bind(&next); |
1176 } | 1533 } |
1177 | 1534 |
1178 // Return the index of the match. | 1535 // Return the index of the match. |
1179 { | 1536 if (is_fastpath) { |
| 1537 Node* const index = a->LoadObjectField( |
| 1538 match_indices, JSRegExpResult::kIndexOffset, MachineType::AnyTagged()); |
| 1539 a->Return(index); |
| 1540 } else { |
| 1541 DCHECK(!is_fastpath); |
| 1542 |
1180 CLabel fast_result(a), slow_result(a, CLabel::kDeferred); | 1543 CLabel fast_result(a), slow_result(a, CLabel::kDeferred); |
1181 | 1544 BranchIfFastRegExpResult(a, context, a->LoadMap(match_indices), |
1182 Node* const native_context = a->LoadNativeContext(context); | 1545 &fast_result, &slow_result); |
1183 Node* const initial_regexp_result_map = | |
1184 a->LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); | |
1185 Node* const match_indices_map = a->LoadMap(match_indices); | |
1186 | |
1187 a->Branch(a->WordEqual(match_indices_map, initial_regexp_result_map), | |
1188 &fast_result, &slow_result); | |
1189 | 1546 |
1190 a->Bind(&fast_result); | 1547 a->Bind(&fast_result); |
1191 { | 1548 { |
1192 Node* const index = | 1549 Node* const index = |
1193 a->LoadObjectField(match_indices, JSRegExpResult::kIndexOffset, | 1550 a->LoadObjectField(match_indices, JSRegExpResult::kIndexOffset, |
1194 MachineType::AnyTagged()); | 1551 MachineType::AnyTagged()); |
1195 a->Return(index); | 1552 a->Return(index); |
1196 } | 1553 } |
1197 | 1554 |
1198 a->Bind(&slow_result); | 1555 a->Bind(&slow_result); |
(...skipping 841 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2040 a.Bind(&if_matched); | 2397 a.Bind(&if_matched); |
2041 { | 2398 { |
2042 Node* result = ConstructNewResultFromMatchInfo(isolate, &a, context, | 2399 Node* result = ConstructNewResultFromMatchInfo(isolate, &a, context, |
2043 match_indices, string); | 2400 match_indices, string); |
2044 a.Return(result); | 2401 a.Return(result); |
2045 } | 2402 } |
2046 } | 2403 } |
2047 | 2404 |
2048 } // namespace internal | 2405 } // namespace internal |
2049 } // namespace v8 | 2406 } // namespace v8 |
OLD | NEW |