| 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 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 219 | 219 |
| 220 void StoreLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, | 220 void StoreLastIndex(CodeStubAssembler* a, Node* context, Node* regexp, |
| 221 Node* value, bool is_fastpath) { | 221 Node* value, bool is_fastpath) { |
| 222 if (is_fastpath) { | 222 if (is_fastpath) { |
| 223 FastStoreLastIndex(a, regexp, value); | 223 FastStoreLastIndex(a, regexp, value); |
| 224 } else { | 224 } else { |
| 225 SlowStoreLastIndex(a, context, regexp, value); | 225 SlowStoreLastIndex(a, context, regexp, value); |
| 226 } | 226 } |
| 227 } | 227 } |
| 228 | 228 |
| 229 Node* LoadMatchInfoField(CodeStubAssembler* a, Node* const match_info, |
| 230 const int index) { |
| 231 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
| 232 Node* const result = |
| 233 a->LoadFixedArrayElement(match_info, a->IntPtrConstant(index), 0, mode); |
| 234 return result; |
| 235 } |
| 236 |
| 229 Node* ConstructNewResultFromMatchInfo(Isolate* isolate, CodeStubAssembler* a, | 237 Node* ConstructNewResultFromMatchInfo(Isolate* isolate, CodeStubAssembler* a, |
| 230 Node* context, Node* match_info, | 238 Node* context, Node* match_info, |
| 231 Node* string) { | 239 Node* string) { |
| 232 CLabel out(a); | 240 CLabel out(a); |
| 233 | 241 |
| 234 ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | 242 Node* const num_indices = a->SmiUntag(LoadMatchInfoField( |
| 235 Node* const num_indices = a->SmiUntag(a->LoadFixedArrayElement( | 243 a, match_info, RegExpMatchInfo::kNumberOfCapturesIndex)); |
| 236 match_info, a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0, | |
| 237 mode)); | |
| 238 Node* const num_results = a->SmiTag(a->WordShr(num_indices, 1)); | 244 Node* const num_results = a->SmiTag(a->WordShr(num_indices, 1)); |
| 239 Node* const start = a->LoadFixedArrayElement( | 245 Node* const start = |
| 240 match_info, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), 0, | 246 LoadMatchInfoField(a, match_info, RegExpMatchInfo::kFirstCaptureIndex); |
| 241 mode); | 247 Node* const end = LoadMatchInfoField(a, match_info, |
| 242 Node* const end = a->LoadFixedArrayElement( | 248 RegExpMatchInfo::kFirstCaptureIndex + 1); |
| 243 match_info, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1), 0, | |
| 244 mode); | |
| 245 | 249 |
| 246 // Calculate the substring of the first match before creating the result array | 250 // Calculate the substring of the first match before creating the result array |
| 247 // to avoid an unnecessary write barrier storing the first result. | 251 // to avoid an unnecessary write barrier storing the first result. |
| 248 Node* const first = a->SubString(context, string, start, end); | 252 Node* const first = a->SubString(context, string, start, end); |
| 249 | 253 |
| 250 Node* const result = | 254 Node* const result = |
| 251 a->AllocateRegExpResult(context, num_results, start, string); | 255 a->AllocateRegExpResult(context, num_results, start, string); |
| 252 Node* const result_elements = a->LoadElements(result); | 256 Node* const result_elements = a->LoadElements(result); |
| 253 | 257 |
| 254 a->StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER); | 258 a->StoreFixedArrayElement(result_elements, 0, first, SKIP_WRITE_BARRIER); |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 | 413 |
| 410 StoreLastIndex(a, context, regexp, smi_zero, is_fastpath); | 414 StoreLastIndex(a, context, regexp, smi_zero, is_fastpath); |
| 411 a->Goto(if_didnotmatch); | 415 a->Goto(if_didnotmatch); |
| 412 } | 416 } |
| 413 | 417 |
| 414 a->Bind(&successful_match); | 418 a->Bind(&successful_match); |
| 415 { | 419 { |
| 416 a->GotoUnless(should_update_last_index, &out); | 420 a->GotoUnless(should_update_last_index, &out); |
| 417 | 421 |
| 418 // Update the new last index from {match_indices}. | 422 // Update the new last index from {match_indices}. |
| 419 Node* const new_lastindex = a->LoadFixedArrayElement( | 423 Node* const new_lastindex = LoadMatchInfoField( |
| 420 match_indices, | 424 a, match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
| 421 a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1)); | |
| 422 | 425 |
| 423 StoreLastIndex(a, context, regexp, new_lastindex, is_fastpath); | 426 StoreLastIndex(a, context, regexp, new_lastindex, is_fastpath); |
| 424 a->Goto(&out); | 427 a->Goto(&out); |
| 425 } | 428 } |
| 426 | 429 |
| 427 a->Bind(&out); | 430 a->Bind(&out); |
| 428 return var_result.value(); | 431 return var_result.value(); |
| 429 } | 432 } |
| 430 | 433 |
| 431 // ES#sec-regexp.prototype.exec | 434 // ES#sec-regexp.prototype.exec |
| (...skipping 22 matching lines...) Expand all Loading... |
| 454 a->Bind(&if_didnotmatch); | 457 a->Bind(&if_didnotmatch); |
| 455 { | 458 { |
| 456 var_result.Bind(null); | 459 var_result.Bind(null); |
| 457 a->Goto(&out); | 460 a->Goto(&out); |
| 458 } | 461 } |
| 459 | 462 |
| 460 a->Bind(&out); | 463 a->Bind(&out); |
| 461 return var_result.value(); | 464 return var_result.value(); |
| 462 } | 465 } |
| 463 | 466 |
| 464 } // namespace | |
| 465 | |
| 466 namespace { | |
| 467 | |
| 468 Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, | 467 Node* ThrowIfNotJSReceiver(CodeStubAssembler* a, Isolate* isolate, |
| 469 Node* context, Node* value, | 468 Node* context, Node* value, |
| 470 MessageTemplate::Template msg_template, | 469 MessageTemplate::Template msg_template, |
| 471 char const* method_name) { | 470 char const* method_name) { |
| 472 CLabel out(a), throw_exception(a, CLabel::kDeferred); | 471 CLabel out(a), throw_exception(a, CLabel::kDeferred); |
| 473 CVariable var_value_map(a, MachineRepresentation::kTagged); | 472 CVariable var_value_map(a, MachineRepresentation::kTagged); |
| 474 | 473 |
| 475 a->GotoIf(a->TaggedIsSmi(value), &throw_exception); | 474 a->GotoIf(a->TaggedIsSmi(value), &throw_exception); |
| 476 | 475 |
| 477 // Load the instance type of the {value}. | 476 // Load the instance type of the {value}. |
| (...skipping 650 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1128 void Builtins::Generate_RegExpPrototypeTest(CodeAssemblerState* state) { | 1127 void Builtins::Generate_RegExpPrototypeTest(CodeAssemblerState* state) { |
| 1129 CodeStubAssembler a(state); | 1128 CodeStubAssembler a(state); |
| 1130 | 1129 |
| 1131 Isolate* const isolate = a.isolate(); | 1130 Isolate* const isolate = a.isolate(); |
| 1132 | 1131 |
| 1133 Node* const maybe_receiver = a.Parameter(0); | 1132 Node* const maybe_receiver = a.Parameter(0); |
| 1134 Node* const maybe_string = a.Parameter(1); | 1133 Node* const maybe_string = a.Parameter(1); |
| 1135 Node* const context = a.Parameter(4); | 1134 Node* const context = a.Parameter(4); |
| 1136 | 1135 |
| 1137 // Ensure {maybe_receiver} is a JSReceiver. | 1136 // Ensure {maybe_receiver} is a JSReceiver. |
| 1138 ThrowIfNotJSReceiver(&a, isolate, context, maybe_receiver, | 1137 Node* const map = ThrowIfNotJSReceiver( |
| 1139 MessageTemplate::kIncompatibleMethodReceiver, | 1138 &a, isolate, context, maybe_receiver, |
| 1140 "RegExp.prototype.test"); | 1139 MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.test"); |
| 1141 Node* const receiver = maybe_receiver; | 1140 Node* const receiver = maybe_receiver; |
| 1142 | 1141 |
| 1143 // Convert {maybe_string} to a String. | 1142 // Convert {maybe_string} to a String. |
| 1144 Node* const string = a.ToString(context, maybe_string); | 1143 Node* const string = a.ToString(context, maybe_string); |
| 1145 | 1144 |
| 1146 // Call exec. | 1145 CLabel fast_path(&a), slow_path(&a); |
| 1147 Node* const match_indices = RegExpExec(&a, context, receiver, string); | 1146 BranchIfFastPath(&a, context, map, &fast_path, &slow_path); |
| 1148 | 1147 |
| 1149 // Return true iff exec matched successfully. | 1148 a.Bind(&fast_path); |
| 1150 Node* const result = a.Select(a.WordEqual(match_indices, a.NullConstant()), | 1149 { |
| 1151 a.FalseConstant(), a.TrueConstant()); | 1150 CLabel if_didnotmatch(&a); |
| 1152 a.Return(result); | 1151 RegExpPrototypeExecBodyWithoutResult(&a, context, receiver, string, |
| 1152 &if_didnotmatch, true); |
| 1153 a.Return(a.TrueConstant()); |
| 1154 |
| 1155 a.Bind(&if_didnotmatch); |
| 1156 a.Return(a.FalseConstant()); |
| 1157 } |
| 1158 |
| 1159 a.Bind(&slow_path); |
| 1160 { |
| 1161 // Call exec. |
| 1162 Node* const match_indices = RegExpExec(&a, context, receiver, string); |
| 1163 |
| 1164 // Return true iff exec matched successfully. |
| 1165 Node* const result = a.Select(a.WordEqual(match_indices, a.NullConstant()), |
| 1166 a.FalseConstant(), a.TrueConstant()); |
| 1167 a.Return(result); |
| 1168 } |
| 1153 } | 1169 } |
| 1154 | 1170 |
| 1155 namespace { | 1171 namespace { |
| 1156 | 1172 |
| 1157 Node* AdvanceStringIndex(CodeStubAssembler* a, Node* const string, | 1173 Node* AdvanceStringIndex(CodeStubAssembler* a, Node* const string, |
| 1158 Node* const index, Node* const is_unicode) { | 1174 Node* const index, Node* const is_unicode) { |
| 1159 CVariable var_result(a, MachineRepresentation::kTagged); | 1175 CVariable var_result(a, MachineRepresentation::kTagged); |
| 1160 | 1176 |
| 1161 // Default to last_index + 1. | 1177 // Default to last_index + 1. |
| 1162 Node* const index_plus_one = a->SmiAdd(index, a->SmiConstant(1)); | 1178 Node* const index_plus_one = a->SmiAdd(index, a->SmiConstant(1)); |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1312 return to_array; | 1328 return to_array; |
| 1313 } | 1329 } |
| 1314 | 1330 |
| 1315 private: | 1331 private: |
| 1316 CodeStubAssembler* const assembler_; | 1332 CodeStubAssembler* const assembler_; |
| 1317 CVariable var_array_; | 1333 CVariable var_array_; |
| 1318 CVariable var_length_; | 1334 CVariable var_length_; |
| 1319 CVariable var_capacity_; | 1335 CVariable var_capacity_; |
| 1320 }; | 1336 }; |
| 1321 | 1337 |
| 1322 void Generate_RegExpPrototypeMatchBody(CodeStubAssembler* a, | 1338 void RegExpPrototypeMatchBody(CodeStubAssembler* a, Node* const receiver, |
| 1323 Node* const receiver, Node* const string, | 1339 Node* const string, Node* const context, |
| 1324 Node* const context, | 1340 const bool is_fastpath) { |
| 1325 const bool is_fastpath) { | |
| 1326 Isolate* const isolate = a->isolate(); | 1341 Isolate* const isolate = a->isolate(); |
| 1327 | 1342 |
| 1328 Node* const null = a->NullConstant(); | 1343 Node* const null = a->NullConstant(); |
| 1329 Node* const int_zero = a->IntPtrConstant(0); | 1344 Node* const int_zero = a->IntPtrConstant(0); |
| 1330 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 1345 Node* const smi_zero = a->SmiConstant(Smi::kZero); |
| 1331 | 1346 |
| 1332 Node* const regexp = receiver; | 1347 Node* const regexp = receiver; |
| 1333 Node* const is_global = | 1348 Node* const is_global = |
| 1334 FlagGetter(a, context, regexp, JSRegExp::kGlobal, is_fastpath); | 1349 FlagGetter(a, context, regexp, JSRegExp::kGlobal, is_fastpath); |
| 1335 | 1350 |
| (...skipping 22 matching lines...) Expand all Loading... |
| 1358 // Loop preparations. Within the loop, collect results from RegExpExec | 1373 // Loop preparations. Within the loop, collect results from RegExpExec |
| 1359 // and store match strings in the array. | 1374 // and store match strings in the array. |
| 1360 | 1375 |
| 1361 CVariable* vars[] = {array.var_array(), array.var_length(), | 1376 CVariable* vars[] = {array.var_array(), array.var_length(), |
| 1362 array.var_capacity()}; | 1377 array.var_capacity()}; |
| 1363 CLabel loop(a, 3, vars), out(a); | 1378 CLabel loop(a, 3, vars), out(a); |
| 1364 a->Goto(&loop); | 1379 a->Goto(&loop); |
| 1365 | 1380 |
| 1366 a->Bind(&loop); | 1381 a->Bind(&loop); |
| 1367 { | 1382 { |
| 1368 Node* const result = is_fastpath ? RegExpPrototypeExecBody( | 1383 CVariable var_match(a, MachineRepresentation::kTagged); |
| 1369 a, context, regexp, string, true) | |
| 1370 : RegExpExec(a, context, regexp, string); | |
| 1371 | 1384 |
| 1372 CLabel if_didmatch(a), if_didnotmatch(a); | 1385 CLabel if_didmatch(a), if_didnotmatch(a); |
| 1373 a->Branch(a->WordEqual(result, null), &if_didnotmatch, &if_didmatch); | 1386 if (is_fastpath) { |
| 1387 // On the fast path, grab the matching string from the raw match index |
| 1388 // array. |
| 1389 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( |
| 1390 a, context, regexp, string, &if_didnotmatch, true); |
| 1374 | 1391 |
| 1375 a->Bind(&if_didnotmatch); | 1392 Node* const match_from = LoadMatchInfoField( |
| 1376 { | 1393 a, match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
| 1377 // Return null if there were no matches, otherwise just exit the loop. | 1394 Node* const match_to = LoadMatchInfoField( |
| 1378 a->GotoUnless(a->IntPtrEqual(array.length(), int_zero), &out); | 1395 a, match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
| 1379 a->Return(null); | |
| 1380 } | |
| 1381 | 1396 |
| 1382 a->Bind(&if_didmatch); | 1397 Node* match = a->SubString(context, string, match_from, match_to); |
| 1383 { | 1398 var_match.Bind(match); |
| 1384 Node* match = nullptr; | |
| 1385 if (is_fastpath) { | |
| 1386 // TODO(jgruber): We could optimize further here and in other | |
| 1387 // methods (e.g. @@search) by bypassing RegExp result construction. | |
| 1388 Node* const result_fixed_array = a->LoadElements(result); | |
| 1389 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | |
| 1390 match = | |
| 1391 a->LoadFixedArrayElement(result_fixed_array, int_zero, 0, mode); | |
| 1392 | 1399 |
| 1393 // The match is guaranteed to be a string on the fast path. | 1400 a->Goto(&if_didmatch); |
| 1394 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(match))); | 1401 } else { |
| 1395 } else { | 1402 DCHECK(!is_fastpath); |
| 1396 DCHECK(!is_fastpath); | 1403 Node* const result = RegExpExec(a, context, regexp, string); |
| 1397 | 1404 |
| 1398 CVariable var_match(a, MachineRepresentation::kTagged); | 1405 CLabel load_match(a); |
| 1399 CLabel fast_result(a), slow_result(a), match_loaded(a); | 1406 a->Branch(a->WordEqual(result, null), &if_didnotmatch, &load_match); |
| 1407 |
| 1408 a->Bind(&load_match); |
| 1409 { |
| 1410 CLabel fast_result(a), slow_result(a); |
| 1400 BranchIfFastRegExpResult(a, context, a->LoadMap(result), &fast_result, | 1411 BranchIfFastRegExpResult(a, context, a->LoadMap(result), &fast_result, |
| 1401 &slow_result); | 1412 &slow_result); |
| 1402 | 1413 |
| 1403 a->Bind(&fast_result); | 1414 a->Bind(&fast_result); |
| 1404 { | 1415 { |
| 1405 // TODO(jgruber): We could optimize further here and in other | |
| 1406 // methods (e.g. @@search) by bypassing RegExp result construction. | |
| 1407 Node* const result_fixed_array = a->LoadElements(result); | 1416 Node* const result_fixed_array = a->LoadElements(result); |
| 1408 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | 1417 const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; |
| 1409 Node* const match = | 1418 Node* const match = |
| 1410 a->LoadFixedArrayElement(result_fixed_array, int_zero, 0, mode); | 1419 a->LoadFixedArrayElement(result_fixed_array, int_zero, 0, mode); |
| 1411 | 1420 |
| 1412 // The match is guaranteed to be a string on the fast path. | 1421 // The match is guaranteed to be a string on the fast path. |
| 1413 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(match))); | 1422 CSA_ASSERT(a, a->IsStringInstanceType(a->LoadInstanceType(match))); |
| 1414 | 1423 |
| 1415 var_match.Bind(match); | 1424 var_match.Bind(match); |
| 1416 a->Goto(&match_loaded); | 1425 a->Goto(&if_didmatch); |
| 1417 } | 1426 } |
| 1418 | 1427 |
| 1419 a->Bind(&slow_result); | 1428 a->Bind(&slow_result); |
| 1420 { | 1429 { |
| 1421 // TODO(ishell): Use GetElement stub once it's available. | 1430 // TODO(ishell): Use GetElement stub once it's available. |
| 1422 Node* const name = smi_zero; | 1431 Node* const name = smi_zero; |
| 1423 Callable getproperty_callable = CodeFactory::GetProperty(isolate); | 1432 Callable getproperty_callable = CodeFactory::GetProperty(isolate); |
| 1424 Node* const match = | 1433 Node* const match = |
| 1425 a->CallStub(getproperty_callable, context, result, name); | 1434 a->CallStub(getproperty_callable, context, result, name); |
| 1426 | 1435 |
| 1427 var_match.Bind(match); | 1436 var_match.Bind(a->ToString(context, match)); |
| 1428 a->Goto(&match_loaded); | 1437 a->Goto(&if_didmatch); |
| 1429 } | 1438 } |
| 1439 } |
| 1440 } |
| 1430 | 1441 |
| 1431 a->Bind(&match_loaded); | 1442 a->Bind(&if_didnotmatch); |
| 1432 match = a->ToString(context, var_match.value()); | 1443 { |
| 1433 } | 1444 // Return null if there were no matches, otherwise just exit the loop. |
| 1434 DCHECK(match != nullptr); | 1445 a->GotoUnless(a->IntPtrEqual(array.length(), int_zero), &out); |
| 1446 a->Return(null); |
| 1447 } |
| 1448 |
| 1449 a->Bind(&if_didmatch); |
| 1450 { |
| 1451 Node* match = var_match.value(); |
| 1435 | 1452 |
| 1436 // Store the match, growing the fixed array if needed. | 1453 // Store the match, growing the fixed array if needed. |
| 1437 | 1454 |
| 1438 array.Push(match); | 1455 array.Push(match); |
| 1439 | 1456 |
| 1440 // Advance last index if the match is the empty string. | 1457 // Advance last index if the match is the empty string. |
| 1441 | 1458 |
| 1442 Node* const match_length = a->LoadStringLength(match); | 1459 Node* const match_length = a->LoadStringLength(match); |
| 1443 a->GotoUnless(a->SmiEqual(match_length, smi_zero), &loop); | 1460 a->GotoUnless(a->SmiEqual(match_length, smi_zero), &loop); |
| 1444 | 1461 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1483 MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.@@match"); | 1500 MessageTemplate::kIncompatibleMethodReceiver, "RegExp.prototype.@@match"); |
| 1484 Node* const receiver = maybe_receiver; | 1501 Node* const receiver = maybe_receiver; |
| 1485 | 1502 |
| 1486 // Convert {maybe_string} to a String. | 1503 // Convert {maybe_string} to a String. |
| 1487 Node* const string = a.ToString(context, maybe_string); | 1504 Node* const string = a.ToString(context, maybe_string); |
| 1488 | 1505 |
| 1489 CLabel fast_path(&a), slow_path(&a); | 1506 CLabel fast_path(&a), slow_path(&a); |
| 1490 BranchIfFastPath(&a, context, map, &fast_path, &slow_path); | 1507 BranchIfFastPath(&a, context, map, &fast_path, &slow_path); |
| 1491 | 1508 |
| 1492 a.Bind(&fast_path); | 1509 a.Bind(&fast_path); |
| 1493 Generate_RegExpPrototypeMatchBody(&a, receiver, string, context, true); | 1510 RegExpPrototypeMatchBody(&a, receiver, string, context, true); |
| 1494 | 1511 |
| 1495 a.Bind(&slow_path); | 1512 a.Bind(&slow_path); |
| 1496 Generate_RegExpPrototypeMatchBody(&a, receiver, string, context, false); | 1513 RegExpPrototypeMatchBody(&a, receiver, string, context, false); |
| 1497 } | 1514 } |
| 1498 | 1515 |
| 1499 namespace { | 1516 namespace { |
| 1500 | 1517 |
| 1501 void Generate_RegExpPrototypeSearchBody(CodeStubAssembler* a, | 1518 void RegExpPrototypeSearchBodyFast(CodeStubAssembler* a, Node* const receiver, |
| 1502 Node* const receiver, | 1519 Node* const string, Node* const context) { |
| 1503 Node* const string, Node* const context, | 1520 // Grab the initial value of last index. |
| 1504 bool is_fastpath) { | 1521 Node* const previous_last_index = FastLoadLastIndex(a, receiver); |
| 1522 |
| 1523 // Ensure last index is 0. |
| 1524 FastStoreLastIndex(a, receiver, a->SmiConstant(Smi::kZero)); |
| 1525 |
| 1526 // Call exec. |
| 1527 CLabel if_didnotmatch(a); |
| 1528 Node* const match_indices = RegExpPrototypeExecBodyWithoutResult( |
| 1529 a, context, receiver, string, &if_didnotmatch, true); |
| 1530 |
| 1531 // Successful match. |
| 1532 { |
| 1533 // Reset last index. |
| 1534 FastStoreLastIndex(a, receiver, previous_last_index); |
| 1535 |
| 1536 // Return the index of the match. |
| 1537 Node* const index = LoadMatchInfoField(a, match_indices, |
| 1538 RegExpMatchInfo::kFirstCaptureIndex); |
| 1539 a->Return(index); |
| 1540 } |
| 1541 |
| 1542 a->Bind(&if_didnotmatch); |
| 1543 { |
| 1544 // Reset last index and return -1. |
| 1545 FastStoreLastIndex(a, receiver, previous_last_index); |
| 1546 a->Return(a->SmiConstant(-1)); |
| 1547 } |
| 1548 } |
| 1549 |
| 1550 void RegExpPrototypeSearchBodySlow(CodeStubAssembler* a, Node* const receiver, |
| 1551 Node* const string, Node* const context) { |
| 1505 Isolate* const isolate = a->isolate(); | 1552 Isolate* const isolate = a->isolate(); |
| 1506 | 1553 |
| 1507 Node* const smi_zero = a->SmiConstant(Smi::kZero); | 1554 Node* const smi_zero = a->SmiConstant(Smi::kZero); |
| 1508 | 1555 |
| 1509 // Grab the initial value of last index. | 1556 // Grab the initial value of last index. |
| 1510 Node* const previous_last_index = | 1557 Node* const previous_last_index = SlowLoadLastIndex(a, context, receiver); |
| 1511 LoadLastIndex(a, context, receiver, is_fastpath); | |
| 1512 | 1558 |
| 1513 // Ensure last index is 0. | 1559 // Ensure last index is 0. |
| 1514 if (is_fastpath) { | 1560 { |
| 1515 FastStoreLastIndex(a, receiver, smi_zero); | |
| 1516 } else { | |
| 1517 CLabel next(a); | 1561 CLabel next(a); |
| 1518 a->GotoIf(a->SameValue(previous_last_index, smi_zero, context), &next); | 1562 a->GotoIf(a->SameValue(previous_last_index, smi_zero, context), &next); |
| 1519 | 1563 |
| 1520 SlowStoreLastIndex(a, context, receiver, smi_zero); | 1564 SlowStoreLastIndex(a, context, receiver, smi_zero); |
| 1521 a->Goto(&next); | 1565 a->Goto(&next); |
| 1522 a->Bind(&next); | 1566 a->Bind(&next); |
| 1523 } | 1567 } |
| 1524 | 1568 |
| 1525 // Call exec. | 1569 // Call exec. |
| 1526 Node* const match_indices = | 1570 Node* const exec_result = RegExpExec(a, context, receiver, string); |
| 1527 is_fastpath ? RegExpPrototypeExecBody(a, context, receiver, string, true) | |
| 1528 : RegExpExec(a, context, receiver, string); | |
| 1529 | 1571 |
| 1530 // Reset last index if necessary. | 1572 // Reset last index if necessary. |
| 1531 if (is_fastpath) { | 1573 { |
| 1532 FastStoreLastIndex(a, receiver, previous_last_index); | |
| 1533 } else { | |
| 1534 CLabel next(a); | 1574 CLabel next(a); |
| 1535 Node* const current_last_index = SlowLoadLastIndex(a, context, receiver); | 1575 Node* const current_last_index = SlowLoadLastIndex(a, context, receiver); |
| 1536 | 1576 |
| 1537 a->GotoIf(a->SameValue(current_last_index, previous_last_index, context), | 1577 a->GotoIf(a->SameValue(current_last_index, previous_last_index, context), |
| 1538 &next); | 1578 &next); |
| 1539 | 1579 |
| 1540 SlowStoreLastIndex(a, context, receiver, previous_last_index); | 1580 SlowStoreLastIndex(a, context, receiver, previous_last_index); |
| 1541 a->Goto(&next); | 1581 a->Goto(&next); |
| 1582 |
| 1542 a->Bind(&next); | 1583 a->Bind(&next); |
| 1543 } | 1584 } |
| 1544 | 1585 |
| 1545 // Return -1 if no match was found. | 1586 // Return -1 if no match was found. |
| 1546 { | 1587 { |
| 1547 CLabel next(a); | 1588 CLabel next(a); |
| 1548 a->GotoUnless(a->WordEqual(match_indices, a->NullConstant()), &next); | 1589 a->GotoUnless(a->WordEqual(exec_result, a->NullConstant()), &next); |
| 1549 a->Return(a->SmiConstant(-1)); | 1590 a->Return(a->SmiConstant(-1)); |
| 1550 a->Bind(&next); | 1591 a->Bind(&next); |
| 1551 } | 1592 } |
| 1552 | 1593 |
| 1553 // Return the index of the match. | 1594 // Return the index of the match. |
| 1554 if (is_fastpath) { | 1595 { |
| 1555 Node* const index = a->LoadObjectField( | |
| 1556 match_indices, JSRegExpResult::kIndexOffset, MachineType::AnyTagged()); | |
| 1557 a->Return(index); | |
| 1558 } else { | |
| 1559 DCHECK(!is_fastpath); | |
| 1560 | |
| 1561 CLabel fast_result(a), slow_result(a, CLabel::kDeferred); | 1596 CLabel fast_result(a), slow_result(a, CLabel::kDeferred); |
| 1562 BranchIfFastRegExpResult(a, context, a->LoadMap(match_indices), | 1597 BranchIfFastRegExpResult(a, context, a->LoadMap(exec_result), &fast_result, |
| 1563 &fast_result, &slow_result); | 1598 &slow_result); |
| 1564 | 1599 |
| 1565 a->Bind(&fast_result); | 1600 a->Bind(&fast_result); |
| 1566 { | 1601 { |
| 1567 Node* const index = | 1602 Node* const index = |
| 1568 a->LoadObjectField(match_indices, JSRegExpResult::kIndexOffset, | 1603 a->LoadObjectField(exec_result, JSRegExpResult::kIndexOffset); |
| 1569 MachineType::AnyTagged()); | |
| 1570 a->Return(index); | 1604 a->Return(index); |
| 1571 } | 1605 } |
| 1572 | 1606 |
| 1573 a->Bind(&slow_result); | 1607 a->Bind(&slow_result); |
| 1574 { | 1608 { |
| 1575 Node* const name = a->HeapConstant(isolate->factory()->index_string()); | 1609 Node* const name = a->HeapConstant(isolate->factory()->index_string()); |
| 1576 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); | 1610 Callable getproperty_callable = CodeFactory::GetProperty(a->isolate()); |
| 1577 Node* const index = | 1611 Node* const index = |
| 1578 a->CallStub(getproperty_callable, context, match_indices, name); | 1612 a->CallStub(getproperty_callable, context, exec_result, name); |
| 1579 a->Return(index); | 1613 a->Return(index); |
| 1580 } | 1614 } |
| 1581 } | 1615 } |
| 1582 } | 1616 } |
| 1583 | 1617 |
| 1584 } // namespace | 1618 } // namespace |
| 1585 | 1619 |
| 1586 // ES#sec-regexp.prototype-@@search | 1620 // ES#sec-regexp.prototype-@@search |
| 1587 // RegExp.prototype [ @@search ] ( string ) | 1621 // RegExp.prototype [ @@search ] ( string ) |
| 1588 void Builtins::Generate_RegExpPrototypeSearch(CodeAssemblerState* state) { | 1622 void Builtins::Generate_RegExpPrototypeSearch(CodeAssemblerState* state) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1601 "RegExp.prototype.@@search"); | 1635 "RegExp.prototype.@@search"); |
| 1602 Node* const receiver = maybe_receiver; | 1636 Node* const receiver = maybe_receiver; |
| 1603 | 1637 |
| 1604 // Convert {maybe_string} to a String. | 1638 // Convert {maybe_string} to a String. |
| 1605 Node* const string = a.ToString(context, maybe_string); | 1639 Node* const string = a.ToString(context, maybe_string); |
| 1606 | 1640 |
| 1607 CLabel fast_path(&a), slow_path(&a); | 1641 CLabel fast_path(&a), slow_path(&a); |
| 1608 BranchIfFastPath(&a, context, map, &fast_path, &slow_path); | 1642 BranchIfFastPath(&a, context, map, &fast_path, &slow_path); |
| 1609 | 1643 |
| 1610 a.Bind(&fast_path); | 1644 a.Bind(&fast_path); |
| 1611 Generate_RegExpPrototypeSearchBody(&a, receiver, string, context, true); | 1645 RegExpPrototypeSearchBodyFast(&a, receiver, string, context); |
| 1612 | 1646 |
| 1613 a.Bind(&slow_path); | 1647 a.Bind(&slow_path); |
| 1614 Generate_RegExpPrototypeSearchBody(&a, receiver, string, context, false); | 1648 RegExpPrototypeSearchBodySlow(&a, receiver, string, context); |
| 1615 } | 1649 } |
| 1616 | 1650 |
| 1617 namespace { | 1651 namespace { |
| 1618 | 1652 |
| 1619 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, | 1653 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, |
| 1620 // {string} is a String, and {limit} is a Smi. | 1654 // {string} is a String, and {limit} is a Smi. |
| 1621 void Generate_RegExpPrototypeSplitBody(CodeStubAssembler* a, Node* const regexp, | 1655 void Generate_RegExpPrototypeSplitBody(CodeStubAssembler* a, Node* const regexp, |
| 1622 Node* const string, Node* const limit, | 1656 Node* const string, Node* const limit, |
| 1623 Node* const context) { | 1657 Node* const context) { |
| 1624 Isolate* isolate = a->isolate(); | 1658 Isolate* isolate = a->isolate(); |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1722 a->CallStub(exec_callable, context, regexp, string, next_search_from, | 1756 a->CallStub(exec_callable, context, regexp, string, next_search_from, |
| 1723 last_match_info); | 1757 last_match_info); |
| 1724 | 1758 |
| 1725 // We're done if no match was found. | 1759 // We're done if no match was found. |
| 1726 { | 1760 { |
| 1727 CLabel next(a); | 1761 CLabel next(a); |
| 1728 a->Branch(a->WordEqual(match_indices, null), &push_suffix_and_out, &next); | 1762 a->Branch(a->WordEqual(match_indices, null), &push_suffix_and_out, &next); |
| 1729 a->Bind(&next); | 1763 a->Bind(&next); |
| 1730 } | 1764 } |
| 1731 | 1765 |
| 1732 Node* const match_from = a->LoadFixedArrayElement( | 1766 Node* const match_from = LoadMatchInfoField( |
| 1733 match_indices, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), | 1767 a, match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
| 1734 0, mode); | |
| 1735 | 1768 |
| 1736 // We're done if the match starts beyond the string. | 1769 // We're done if the match starts beyond the string. |
| 1737 { | 1770 { |
| 1738 CLabel next(a); | 1771 CLabel next(a); |
| 1739 a->Branch(a->WordEqual(match_from, string_length), &push_suffix_and_out, | 1772 a->Branch(a->WordEqual(match_from, string_length), &push_suffix_and_out, |
| 1740 &next); | 1773 &next); |
| 1741 a->Bind(&next); | 1774 a->Bind(&next); |
| 1742 } | 1775 } |
| 1743 | 1776 |
| 1744 Node* const match_to = a->LoadFixedArrayElement( | 1777 Node* const match_to = LoadMatchInfoField( |
| 1745 match_indices, | 1778 a, match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
| 1746 a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1), 0, mode); | |
| 1747 | 1779 |
| 1748 // Advance index and continue if the match is empty. | 1780 // Advance index and continue if the match is empty. |
| 1749 { | 1781 { |
| 1750 CLabel next(a); | 1782 CLabel next(a); |
| 1751 | 1783 |
| 1752 a->GotoUnless(a->SmiEqual(match_to, next_search_from), &next); | 1784 a->GotoUnless(a->SmiEqual(match_to, next_search_from), &next); |
| 1753 a->GotoUnless(a->SmiEqual(match_to, last_matched_until), &next); | 1785 a->GotoUnless(a->SmiEqual(match_to, last_matched_until), &next); |
| 1754 | 1786 |
| 1755 Node* const is_unicode = FastFlagGetter(a, regexp, JSRegExp::kUnicode); | 1787 Node* const is_unicode = FastFlagGetter(a, regexp, JSRegExp::kUnicode); |
| 1756 Node* const new_next_search_from = | 1788 Node* const new_next_search_from = |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1767 Node* const to = match_from; | 1799 Node* const to = match_from; |
| 1768 | 1800 |
| 1769 Node* const substr = a->SubString(context, string, from, to); | 1801 Node* const substr = a->SubString(context, string, from, to); |
| 1770 array.Push(substr); | 1802 array.Push(substr); |
| 1771 | 1803 |
| 1772 a->GotoIf(a->WordEqual(array.length(), int_limit), &out); | 1804 a->GotoIf(a->WordEqual(array.length(), int_limit), &out); |
| 1773 } | 1805 } |
| 1774 | 1806 |
| 1775 // Add all captures to the array. | 1807 // Add all captures to the array. |
| 1776 { | 1808 { |
| 1777 Node* const num_registers = a->LoadFixedArrayElement( | 1809 Node* const num_registers = LoadMatchInfoField( |
| 1778 match_indices, | 1810 a, match_indices, RegExpMatchInfo::kNumberOfCapturesIndex); |
| 1779 a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0, mode); | |
| 1780 Node* const int_num_registers = a->SmiUntag(num_registers); | 1811 Node* const int_num_registers = a->SmiUntag(num_registers); |
| 1781 | 1812 |
| 1782 CVariable var_reg(a, MachineType::PointerRepresentation()); | 1813 CVariable var_reg(a, MachineType::PointerRepresentation()); |
| 1783 var_reg.Bind(a->IntPtrConstant(2)); | 1814 var_reg.Bind(a->IntPtrConstant(2)); |
| 1784 | 1815 |
| 1785 CVariable* vars[] = {array.var_array(), array.var_length(), | 1816 CVariable* vars[] = {array.var_array(), array.var_length(), |
| 1786 array.var_capacity(), &var_reg}; | 1817 array.var_capacity(), &var_reg}; |
| 1787 const int vars_count = sizeof(vars) / sizeof(vars[0]); | 1818 const int vars_count = sizeof(vars) / sizeof(vars[0]); |
| 1788 CLabel nested_loop(a, vars_count, vars), nested_loop_out(a); | 1819 CLabel nested_loop(a, vars_count, vars), nested_loop_out(a); |
| 1789 a->Branch(a->IntPtrLessThan(var_reg.value(), int_num_registers), | 1820 a->Branch(a->IntPtrLessThan(var_reg.value(), int_num_registers), |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1987 a->GotoIf(a->WordEqual(res, null), &out); | 2018 a->GotoIf(a->WordEqual(res, null), &out); |
| 1988 | 2019 |
| 1989 // Reload last match info since it might have changed. | 2020 // Reload last match info since it might have changed. |
| 1990 last_match_info = a->LoadContextElement( | 2021 last_match_info = a->LoadContextElement( |
| 1991 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2022 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 1992 | 2023 |
| 1993 Node* const res_length = a->LoadJSArrayLength(res); | 2024 Node* const res_length = a->LoadJSArrayLength(res); |
| 1994 Node* const res_elems = a->LoadElements(res); | 2025 Node* const res_elems = a->LoadElements(res); |
| 1995 CSA_ASSERT(a, a->HasInstanceType(res_elems, FIXED_ARRAY_TYPE)); | 2026 CSA_ASSERT(a, a->HasInstanceType(res_elems, FIXED_ARRAY_TYPE)); |
| 1996 | 2027 |
| 1997 ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | 2028 Node* const num_capture_registers = LoadMatchInfoField( |
| 1998 Node* const num_capture_registers = a->LoadFixedArrayElement( | 2029 a, last_match_info, RegExpMatchInfo::kNumberOfCapturesIndex); |
| 1999 last_match_info, | |
| 2000 a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0, mode); | |
| 2001 | 2030 |
| 2002 CLabel if_hasexplicitcaptures(a), if_noexplicitcaptures(a), create_result(a); | 2031 CLabel if_hasexplicitcaptures(a), if_noexplicitcaptures(a), create_result(a); |
| 2003 a->Branch(a->SmiEqual(num_capture_registers, a->SmiConstant(Smi::FromInt(2))), | 2032 a->Branch(a->SmiEqual(num_capture_registers, a->SmiConstant(Smi::FromInt(2))), |
| 2004 &if_noexplicitcaptures, &if_hasexplicitcaptures); | 2033 &if_noexplicitcaptures, &if_hasexplicitcaptures); |
| 2005 | 2034 |
| 2006 a->Bind(&if_noexplicitcaptures); | 2035 a->Bind(&if_noexplicitcaptures); |
| 2007 { | 2036 { |
| 2008 // If the number of captures is two then there are no explicit captures in | 2037 // If the number of captures is two then there are no explicit captures in |
| 2009 // the regexp, just the implicit capture that captures the whole match. In | 2038 // the regexp, just the implicit capture that captures the whole match. In |
| 2010 // this case we can simplify quite a bit and end up with something faster. | 2039 // this case we can simplify quite a bit and end up with something faster. |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2204 | 2233 |
| 2205 a->Bind(&if_didnotmatch); | 2234 a->Bind(&if_didnotmatch); |
| 2206 { | 2235 { |
| 2207 FastStoreLastIndex(a, regexp, smi_zero); | 2236 FastStoreLastIndex(a, regexp, smi_zero); |
| 2208 var_result.Bind(subject_string); | 2237 var_result.Bind(subject_string); |
| 2209 a->Goto(&out); | 2238 a->Goto(&out); |
| 2210 } | 2239 } |
| 2211 | 2240 |
| 2212 a->Bind(&if_matched); | 2241 a->Bind(&if_matched); |
| 2213 { | 2242 { |
| 2214 ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS; | |
| 2215 | |
| 2216 Node* const subject_start = smi_zero; | 2243 Node* const subject_start = smi_zero; |
| 2217 Node* const match_start = a->LoadFixedArrayElement( | 2244 Node* const match_start = LoadMatchInfoField( |
| 2218 match_indices, a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), | 2245 a, match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
| 2219 0, mode); | 2246 Node* const match_end = LoadMatchInfoField( |
| 2220 Node* const match_end = a->LoadFixedArrayElement( | 2247 a, match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1); |
| 2221 match_indices, | |
| 2222 a->IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 1), 0, mode); | |
| 2223 Node* const subject_end = a->LoadStringLength(subject_string); | 2248 Node* const subject_end = a->LoadStringLength(subject_string); |
| 2224 | 2249 |
| 2225 CLabel if_replaceisempty(a), if_replaceisnotempty(a); | 2250 CLabel if_replaceisempty(a), if_replaceisnotempty(a); |
| 2226 Node* const replace_length = a->LoadStringLength(replace_string); | 2251 Node* const replace_length = a->LoadStringLength(replace_string); |
| 2227 a->Branch(a->SmiEqual(replace_length, smi_zero), &if_replaceisempty, | 2252 a->Branch(a->SmiEqual(replace_length, smi_zero), &if_replaceisempty, |
| 2228 &if_replaceisnotempty); | 2253 &if_replaceisnotempty); |
| 2229 | 2254 |
| 2230 a->Bind(&if_replaceisempty); | 2255 a->Bind(&if_replaceisempty); |
| 2231 { | 2256 { |
| 2232 // TODO(jgruber): We could skip many of the checks that using SubString | 2257 // TODO(jgruber): We could skip many of the checks that using SubString |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2387 a.Bind(&if_matched); | 2412 a.Bind(&if_matched); |
| 2388 { | 2413 { |
| 2389 Node* result = ConstructNewResultFromMatchInfo(isolate, &a, context, | 2414 Node* result = ConstructNewResultFromMatchInfo(isolate, &a, context, |
| 2390 match_indices, string); | 2415 match_indices, string); |
| 2391 a.Return(result); | 2416 a.Return(result); |
| 2392 } | 2417 } |
| 2393 } | 2418 } |
| 2394 | 2419 |
| 2395 } // namespace internal | 2420 } // namespace internal |
| 2396 } // namespace v8 | 2421 } // namespace v8 |
| OLD | NEW |