OLD | NEW |
1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 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/runtime/runtime-utils.h" | 5 #include "src/runtime/runtime-utils.h" |
6 | 6 |
7 #include "src/arguments.h" | 7 #include "src/arguments.h" |
8 #include "src/conversions-inl.h" | 8 #include "src/conversions-inl.h" |
9 #include "src/isolate-inl.h" | 9 #include "src/isolate-inl.h" |
10 #include "src/messages.h" | 10 #include "src/messages.h" |
(...skipping 955 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
966 virtual ~VectorBackedMatch() {} | 966 virtual ~VectorBackedMatch() {} |
967 | 967 |
968 private: | 968 private: |
969 Isolate* isolate_; | 969 Isolate* isolate_; |
970 Handle<String> subject_; | 970 Handle<String> subject_; |
971 Handle<String> match_; | 971 Handle<String> match_; |
972 const int match_position_; | 972 const int match_position_; |
973 ZoneVector<Handle<Object>>* captures_; | 973 ZoneVector<Handle<Object>>* captures_; |
974 }; | 974 }; |
975 | 975 |
976 // Only called from RegExpExecMultiple so it doesn't need to maintain | 976 // Only called from Runtime_RegExpExecMultiple so it doesn't need to maintain |
977 // separate last match info. See comment on that function. | 977 // separate last match info. See comment on that function. |
978 template <bool has_capture> | 978 template <bool has_capture> |
979 MaybeHandle<Object> SearchRegExpMultiple( | 979 static Object* SearchRegExpMultiple(Isolate* isolate, Handle<String> subject, |
980 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, | 980 Handle<JSRegExp> regexp, |
981 Handle<RegExpMatchInfo> last_match_array, | 981 Handle<RegExpMatchInfo> last_match_array, |
982 Handle<FixedArray> result_elements) { | 982 Handle<JSArray> result_array) { |
983 DCHECK(subject->IsFlat()); | 983 DCHECK(subject->IsFlat()); |
984 DCHECK_NE(has_capture, regexp->CaptureCount() == 0); | 984 DCHECK_NE(has_capture, regexp->CaptureCount() == 0); |
985 | 985 |
986 int capture_count = regexp->CaptureCount(); | 986 int capture_count = regexp->CaptureCount(); |
987 int subject_length = subject->length(); | 987 int subject_length = subject->length(); |
988 | 988 |
989 static const int kMinLengthToCache = 0x1000; | 989 static const int kMinLengthToCache = 0x1000; |
990 | 990 |
991 if (subject_length > kMinLengthToCache) { | 991 if (subject_length > kMinLengthToCache) { |
992 FixedArray* last_match_cache; | 992 FixedArray* last_match_cache; |
993 Object* cached_answer = RegExpResultsCache::Lookup( | 993 Object* cached_answer = RegExpResultsCache::Lookup( |
994 isolate->heap(), *subject, regexp->data(), &last_match_cache, | 994 isolate->heap(), *subject, regexp->data(), &last_match_cache, |
995 RegExpResultsCache::REGEXP_MULTIPLE_INDICES); | 995 RegExpResultsCache::REGEXP_MULTIPLE_INDICES); |
996 if (cached_answer->IsFixedArray()) { | 996 if (cached_answer->IsFixedArray()) { |
997 int capture_registers = (capture_count + 1) * 2; | 997 int capture_registers = (capture_count + 1) * 2; |
998 int32_t* last_match = NewArray<int32_t>(capture_registers); | 998 int32_t* last_match = NewArray<int32_t>(capture_registers); |
999 for (int i = 0; i < capture_registers; i++) { | 999 for (int i = 0; i < capture_registers; i++) { |
1000 last_match[i] = Smi::cast(last_match_cache->get(i))->value(); | 1000 last_match[i] = Smi::cast(last_match_cache->get(i))->value(); |
1001 } | 1001 } |
1002 Handle<FixedArray> cached_fixed_array(FixedArray::cast(cached_answer)); | 1002 Handle<FixedArray> cached_fixed_array = |
| 1003 Handle<FixedArray>(FixedArray::cast(cached_answer)); |
| 1004 // The cache FixedArray is a COW-array and we need to return a copy. |
| 1005 Handle<FixedArray> copied_fixed_array = |
| 1006 isolate->factory()->CopyFixedArrayWithMap( |
| 1007 cached_fixed_array, isolate->factory()->fixed_array_map()); |
| 1008 JSArray::SetContent(result_array, copied_fixed_array); |
1003 RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, | 1009 RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, |
1004 last_match); | 1010 last_match); |
1005 DeleteArray(last_match); | 1011 DeleteArray(last_match); |
1006 // The cache FixedArray is a COW-array and we need to return a copy. | 1012 return *result_array; |
1007 return isolate->factory()->CopyFixedArrayWithMap( | |
1008 cached_fixed_array, isolate->factory()->fixed_array_map()); | |
1009 } | 1013 } |
1010 } | 1014 } |
1011 | 1015 |
1012 RegExpImpl::GlobalCache global_cache(regexp, subject, isolate); | 1016 RegExpImpl::GlobalCache global_cache(regexp, subject, isolate); |
1013 if (global_cache.HasException()) return MaybeHandle<Object>(); | 1017 if (global_cache.HasException()) return isolate->heap()->exception(); |
1014 | 1018 |
1015 // Ensured in Runtime_RegExpExecMultiple. | 1019 // Ensured in Runtime_RegExpExecMultiple. |
| 1020 DCHECK(result_array->HasFastObjectElements()); |
| 1021 Handle<FixedArray> result_elements( |
| 1022 FixedArray::cast(result_array->elements())); |
1016 if (result_elements->length() < 16) { | 1023 if (result_elements->length() < 16) { |
1017 result_elements = isolate->factory()->NewFixedArrayWithHoles(16); | 1024 result_elements = isolate->factory()->NewFixedArrayWithHoles(16); |
1018 } | 1025 } |
1019 | 1026 |
1020 FixedArrayBuilder builder(result_elements); | 1027 FixedArrayBuilder builder(result_elements); |
1021 | 1028 |
1022 // Position to search from. | 1029 // Position to search from. |
1023 int match_start = -1; | 1030 int match_start = -1; |
1024 int match_end = 0; | 1031 int match_end = 0; |
1025 bool first = true; | 1032 bool first = true; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1072 } | 1079 } |
1073 elements->set(capture_count + 1, Smi::FromInt(match_start)); | 1080 elements->set(capture_count + 1, Smi::FromInt(match_start)); |
1074 elements->set(capture_count + 2, *subject); | 1081 elements->set(capture_count + 2, *subject); |
1075 builder.Add(*isolate->factory()->NewJSArrayWithElements(elements)); | 1082 builder.Add(*isolate->factory()->NewJSArrayWithElements(elements)); |
1076 } else { | 1083 } else { |
1077 builder.Add(*match); | 1084 builder.Add(*match); |
1078 } | 1085 } |
1079 } | 1086 } |
1080 } | 1087 } |
1081 | 1088 |
1082 if (global_cache.HasException()) return MaybeHandle<Object>(); | 1089 if (global_cache.HasException()) return isolate->heap()->exception(); |
1083 | 1090 |
1084 if (match_start >= 0) { | 1091 if (match_start >= 0) { |
1085 // Finished matching, with at least one match. | 1092 // Finished matching, with at least one match. |
1086 if (match_end < subject_length) { | 1093 if (match_end < subject_length) { |
1087 ReplacementStringBuilder::AddSubjectSlice(&builder, match_end, | 1094 ReplacementStringBuilder::AddSubjectSlice(&builder, match_end, |
1088 subject_length); | 1095 subject_length); |
1089 } | 1096 } |
1090 | 1097 |
1091 RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, | 1098 RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, |
1092 global_cache.LastSuccessfulMatch()); | 1099 global_cache.LastSuccessfulMatch()); |
1093 | 1100 |
1094 Handle<FixedArray> result_fixed_array = builder.array(); | |
1095 result_fixed_array->Shrink(builder.length()); | |
1096 | |
1097 if (subject_length > kMinLengthToCache) { | 1101 if (subject_length > kMinLengthToCache) { |
1098 // Store the last successful match into the array for caching. | 1102 // Store the last successful match into the array for caching. |
1099 // TODO(yangguo): do not expose last match to JS and simplify caching. | 1103 // TODO(yangguo): do not expose last match to JS and simplify caching. |
1100 int capture_registers = (capture_count + 1) * 2; | 1104 int capture_registers = (capture_count + 1) * 2; |
1101 Handle<FixedArray> last_match_cache = | 1105 Handle<FixedArray> last_match_cache = |
1102 isolate->factory()->NewFixedArray(capture_registers); | 1106 isolate->factory()->NewFixedArray(capture_registers); |
1103 int32_t* last_match = global_cache.LastSuccessfulMatch(); | 1107 int32_t* last_match = global_cache.LastSuccessfulMatch(); |
1104 for (int i = 0; i < capture_registers; i++) { | 1108 for (int i = 0; i < capture_registers; i++) { |
1105 last_match_cache->set(i, Smi::FromInt(last_match[i])); | 1109 last_match_cache->set(i, Smi::FromInt(last_match[i])); |
1106 } | 1110 } |
1107 // Cache the result and turn the FixedArray into a COW array. | 1111 Handle<FixedArray> result_fixed_array = builder.array(); |
| 1112 result_fixed_array->Shrink(builder.length()); |
| 1113 // Cache the result and copy the FixedArray into a COW array. |
| 1114 Handle<FixedArray> copied_fixed_array = |
| 1115 isolate->factory()->CopyFixedArrayWithMap( |
| 1116 result_fixed_array, isolate->factory()->fixed_array_map()); |
1108 RegExpResultsCache::Enter( | 1117 RegExpResultsCache::Enter( |
1109 isolate, subject, handle(regexp->data(), isolate), result_fixed_array, | 1118 isolate, subject, handle(regexp->data(), isolate), copied_fixed_array, |
1110 last_match_cache, RegExpResultsCache::REGEXP_MULTIPLE_INDICES); | 1119 last_match_cache, RegExpResultsCache::REGEXP_MULTIPLE_INDICES); |
1111 } | 1120 } |
1112 // The cache FixedArray is a COW-array and we need to return a copy. | 1121 return *builder.ToJSArray(result_array); |
1113 return isolate->factory()->CopyFixedArrayWithMap( | |
1114 result_fixed_array, isolate->factory()->fixed_array_map()); | |
1115 } else { | 1122 } else { |
1116 return isolate->factory()->null_value(); // No matches at all. | 1123 return isolate->heap()->null_value(); // No matches at all. |
1117 } | 1124 } |
1118 } | 1125 } |
1119 | 1126 |
1120 // This is only called for StringReplaceGlobalRegExpWithFunction. This sets | |
1121 // lastMatchInfoOverride to maintain the last match info, so we don't need to | |
1122 // set any other last match array info. | |
1123 MaybeHandle<Object> RegExpExecMultiple(Isolate* isolate, | |
1124 Handle<JSRegExp> regexp, | |
1125 Handle<String> subject, | |
1126 Handle<RegExpMatchInfo> last_match_info, | |
1127 Handle<FixedArray> result_array) { | |
1128 subject = String::Flatten(subject); | |
1129 CHECK(regexp->GetFlags() & JSRegExp::kGlobal); | |
1130 | |
1131 if (regexp->CaptureCount() == 0) { | |
1132 return SearchRegExpMultiple<false>(isolate, subject, regexp, | |
1133 last_match_info, result_array); | |
1134 } else { | |
1135 return SearchRegExpMultiple<true>(isolate, subject, regexp, last_match_info, | |
1136 result_array); | |
1137 } | |
1138 } | |
1139 | |
1140 // Helper function for replacing regular expressions with the result of a | |
1141 // function application in String.prototype.replace. | |
1142 MaybeHandle<String> StringReplaceGlobalRegExpWithFunction( | |
1143 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, | |
1144 Handle<Object> replace_obj) { | |
1145 Factory* factory = isolate->factory(); | |
1146 | |
1147 // TODO(jgruber): Convert result_array into a List<Handle<Object>> (or | |
1148 // similar) and adapt / remove FixedArrayBuilder. | |
1149 Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info(); | |
1150 Handle<FixedArray> result_array = factory->NewFixedArrayWithHoles(16); | |
1151 | |
1152 Handle<Object> res; | |
1153 ASSIGN_RETURN_ON_EXCEPTION(isolate, res, | |
1154 RegExpExecMultiple(isolate, regexp, subject, | |
1155 last_match_info, result_array), | |
1156 String); | |
1157 | |
1158 // Reload the last match info since it might have changed in the meantime. | |
1159 last_match_info = isolate->regexp_last_match_info(); | |
1160 | |
1161 if (res->IsNull(isolate)) return subject; // No matches at all. | |
1162 | |
1163 result_array = Handle<FixedArray>::cast(res); | |
1164 const int result_length = result_array->length(); | |
1165 | |
1166 const int num_captures = last_match_info->NumberOfCaptureRegisters() / 2; | |
1167 if (num_captures == 1) { | |
1168 // If the number of captures is one then there are no explicit captures in | |
1169 // the regexp, just the implicit capture that captures the whole match. In | |
1170 // this case we can simplify quite a bit and end up with something faster. | |
1171 // The builder will consist of some integers that indicate slices of the | |
1172 // input string and some replacements that were returned from the replace | |
1173 // function. | |
1174 int match_start = 0; | |
1175 for (int i = 0; i < result_length; i++) { | |
1176 Handle<Object> elem = FixedArray::get(*result_array, i, isolate); | |
1177 if (elem->IsSmi()) { | |
1178 // Integers represent slices of the original string. | |
1179 // TODO(jgruber): Maybe we don't need this weird encoding anymore (in | |
1180 // preparation to invoking StringBuilderConcat), but can just copy into | |
1181 // the result string with the IncrementalStringBuilder as we go? | |
1182 const int elem_value = Handle<Smi>::cast(elem)->value(); | |
1183 if (elem_value > 0) { | |
1184 match_start = (elem_value >> 11) + (elem_value & 0x7ff); | |
1185 } else { | |
1186 Handle<Object> next_elem = | |
1187 FixedArray::get(*result_array, ++i, isolate); | |
1188 const int next_elem_value = Handle<Smi>::cast(next_elem)->value(); | |
1189 match_start = next_elem_value - elem_value; | |
1190 } | |
1191 } else { | |
1192 DCHECK(elem->IsString()); | |
1193 Handle<String> elem_string = Handle<String>::cast(elem); | |
1194 | |
1195 // Overwrite the i'th element in the results with the string we got | |
1196 // back from the callback function. | |
1197 const int argc = 3; | |
1198 ScopedVector<Handle<Object>> argv(argc); | |
1199 | |
1200 argv[0] = elem_string; | |
1201 argv[1] = handle(Smi::FromInt(match_start), isolate); | |
1202 argv[2] = subject; | |
1203 | |
1204 Handle<Object> replacement_obj; | |
1205 ASSIGN_RETURN_ON_EXCEPTION( | |
1206 isolate, replacement_obj, | |
1207 Execution::Call(isolate, replace_obj, factory->undefined_value(), | |
1208 argc, argv.start()), | |
1209 String); | |
1210 | |
1211 Handle<String> replacement; | |
1212 ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement, | |
1213 Object::ToString(isolate, replacement_obj), | |
1214 String); | |
1215 | |
1216 result_array->set(i, *replacement); | |
1217 match_start += elem_string->length(); | |
1218 } | |
1219 } | |
1220 } else { | |
1221 DCHECK(num_captures > 1); | |
1222 for (int i = 0; i < result_length; i++) { | |
1223 Handle<Object> elem = FixedArray::get(*result_array, i, isolate); | |
1224 if (elem->IsSmi()) continue; | |
1225 | |
1226 // TODO(jgruber): We can skip this whole round-trip through a JS array | |
1227 // for result_array. | |
1228 Handle<JSArray> elem_array = Handle<JSArray>::cast(elem); | |
1229 Handle<FixedArray> elem_array_elems( | |
1230 FixedArray::cast(elem_array->elements()), isolate); | |
1231 | |
1232 const int argc = elem_array_elems->length(); | |
1233 ScopedVector<Handle<Object>> argv(argc); | |
1234 | |
1235 for (int j = 0; j < argc; j++) { | |
1236 argv[j] = FixedArray::get(*elem_array_elems, j, isolate); | |
1237 } | |
1238 | |
1239 // TODO(jgruber): This call is another pattern we could refactor. | |
1240 Handle<Object> replacement_obj; | |
1241 ASSIGN_RETURN_ON_EXCEPTION( | |
1242 isolate, replacement_obj, | |
1243 Execution::Call(isolate, replace_obj, factory->undefined_value(), | |
1244 argc, argv.start()), | |
1245 String); | |
1246 | |
1247 Handle<String> replacement; | |
1248 ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement, | |
1249 Object::ToString(isolate, replacement_obj), | |
1250 String); | |
1251 | |
1252 result_array->set(i, *replacement); | |
1253 } | |
1254 } | |
1255 | |
1256 if (result_length == 0) { | |
1257 return factory->empty_string(); | |
1258 } else if (result_length == 1) { | |
1259 Handle<Object> first = FixedArray::get(*result_array, 0, isolate); | |
1260 if (first->IsString()) return Handle<String>::cast(first); | |
1261 } | |
1262 | |
1263 bool one_byte = subject->HasOnlyOneByteChars(); | |
1264 const int length = StringBuilderConcatLength(subject->length(), *result_array, | |
1265 result_length, &one_byte); | |
1266 | |
1267 if (length == -1) { | |
1268 isolate->Throw(isolate->heap()->illegal_argument_string()); | |
1269 return MaybeHandle<String>(); | |
1270 } | |
1271 | |
1272 if (one_byte) { | |
1273 Handle<SeqOneByteString> answer; | |
1274 ASSIGN_RETURN_ON_EXCEPTION(isolate, answer, | |
1275 isolate->factory()->NewRawOneByteString(length), | |
1276 String); | |
1277 StringBuilderConcatHelper(*subject, answer->GetChars(), *result_array, | |
1278 result_length); | |
1279 return answer; | |
1280 } else { | |
1281 DCHECK(!one_byte); | |
1282 Handle<SeqTwoByteString> answer; | |
1283 ASSIGN_RETURN_ON_EXCEPTION(isolate, answer, | |
1284 isolate->factory()->NewRawTwoByteString(length), | |
1285 String); | |
1286 StringBuilderConcatHelper(*subject, answer->GetChars(), *result_array, | |
1287 result_length); | |
1288 return answer; | |
1289 } | |
1290 | |
1291 UNREACHABLE(); | |
1292 return MaybeHandle<String>(); | |
1293 } | |
1294 | |
1295 MaybeHandle<String> StringReplaceNonGlobalRegExpWithFunction( | 1127 MaybeHandle<String> StringReplaceNonGlobalRegExpWithFunction( |
1296 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, | 1128 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, |
1297 Handle<Object> replace_obj) { | 1129 Handle<Object> replace_obj) { |
1298 Factory* factory = isolate->factory(); | 1130 Factory* factory = isolate->factory(); |
1299 Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info(); | 1131 Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info(); |
1300 | 1132 |
1301 // TODO(jgruber): This is a pattern we could refactor. | 1133 // TODO(jgruber): This is a pattern we could refactor. |
1302 Handle<Object> match_indices_obj; | 1134 Handle<Object> match_indices_obj; |
1303 ASSIGN_RETURN_ON_EXCEPTION( | 1135 ASSIGN_RETURN_ON_EXCEPTION( |
1304 isolate, match_indices_obj, | 1136 isolate, match_indices_obj, |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1364 MaybeHandle<String> RegExpReplace(Isolate* isolate, Handle<JSRegExp> regexp, | 1196 MaybeHandle<String> RegExpReplace(Isolate* isolate, Handle<JSRegExp> regexp, |
1365 Handle<String> string, | 1197 Handle<String> string, |
1366 Handle<Object> replace_obj) { | 1198 Handle<Object> replace_obj) { |
1367 Factory* factory = isolate->factory(); | 1199 Factory* factory = isolate->factory(); |
1368 | 1200 |
1369 // TODO(jgruber): We need the even stricter guarantee of an unmodified | 1201 // TODO(jgruber): We need the even stricter guarantee of an unmodified |
1370 // JSRegExp map here for access to GetFlags to be legal. | 1202 // JSRegExp map here for access to GetFlags to be legal. |
1371 const int flags = regexp->GetFlags(); | 1203 const int flags = regexp->GetFlags(); |
1372 const bool global = (flags & JSRegExp::kGlobal) != 0; | 1204 const bool global = (flags & JSRegExp::kGlobal) != 0; |
1373 | 1205 |
1374 const bool functional_replace = replace_obj->IsCallable(); | 1206 // Functional fast-paths are dispatched directly by replace builtin. |
1375 if (!functional_replace) { | 1207 DCHECK(!replace_obj->IsCallable()); |
1376 Handle<String> replace; | |
1377 ASSIGN_RETURN_ON_EXCEPTION(isolate, replace, | |
1378 Object::ToString(isolate, replace_obj), String); | |
1379 replace = String::Flatten(replace); | |
1380 | 1208 |
1381 Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info(); | 1209 Handle<String> replace; |
| 1210 ASSIGN_RETURN_ON_EXCEPTION(isolate, replace, |
| 1211 Object::ToString(isolate, replace_obj), String); |
| 1212 replace = String::Flatten(replace); |
1382 | 1213 |
1383 if (!global) { | 1214 Handle<RegExpMatchInfo> last_match_info = isolate->regexp_last_match_info(); |
1384 // Non-global regexp search, string replace. | |
1385 | 1215 |
1386 Handle<Object> match_indices_obj; | 1216 if (!global) { |
1387 ASSIGN_RETURN_ON_EXCEPTION( | 1217 // Non-global regexp search, string replace. |
1388 isolate, match_indices_obj, | |
1389 RegExpImpl::Exec(regexp, string, 0, last_match_info), String); | |
1390 | 1218 |
1391 if (match_indices_obj->IsNull(isolate)) { | 1219 Handle<Object> match_indices_obj; |
1392 RETURN_ON_EXCEPTION( | 1220 ASSIGN_RETURN_ON_EXCEPTION( |
1393 isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), String); | 1221 isolate, match_indices_obj, |
1394 return string; | 1222 RegExpImpl::Exec(regexp, string, 0, last_match_info), String); |
1395 } | |
1396 | 1223 |
1397 auto match_indices = Handle<RegExpMatchInfo>::cast(match_indices_obj); | 1224 if (match_indices_obj->IsNull(isolate)) { |
1398 | |
1399 const int start_index = match_indices->Capture(0); | |
1400 const int end_index = match_indices->Capture(1); | |
1401 | |
1402 IncrementalStringBuilder builder(isolate); | |
1403 builder.AppendString(factory->NewSubString(string, 0, start_index)); | |
1404 | |
1405 if (replace->length() > 0) { | |
1406 MatchInfoBackedMatch m(isolate, string, match_indices); | |
1407 Handle<String> replacement; | |
1408 ASSIGN_RETURN_ON_EXCEPTION( | |
1409 isolate, replacement, String::GetSubstitution(isolate, &m, replace), | |
1410 String); | |
1411 builder.AppendString(replacement); | |
1412 } | |
1413 | |
1414 builder.AppendString( | |
1415 factory->NewSubString(string, end_index, string->length())); | |
1416 return builder.Finish(); | |
1417 } else { | |
1418 // Global regexp search, string replace. | |
1419 DCHECK(global); | |
1420 RETURN_ON_EXCEPTION( | 1225 RETURN_ON_EXCEPTION( |
1421 isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), String); | 1226 isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), String); |
| 1227 return string; |
| 1228 } |
1422 | 1229 |
1423 if (replace->length() == 0) { | 1230 auto match_indices = Handle<RegExpMatchInfo>::cast(match_indices_obj); |
1424 if (string->HasOnlyOneByteChars()) { | |
1425 Object* result = | |
1426 StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>( | |
1427 isolate, string, regexp, last_match_info); | |
1428 return handle(String::cast(result), isolate); | |
1429 } else { | |
1430 Object* result = | |
1431 StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>( | |
1432 isolate, string, regexp, last_match_info); | |
1433 return handle(String::cast(result), isolate); | |
1434 } | |
1435 } | |
1436 | 1231 |
1437 Object* result = StringReplaceGlobalRegExpWithString( | 1232 const int start_index = match_indices->Capture(0); |
1438 isolate, string, regexp, replace, last_match_info); | 1233 const int end_index = match_indices->Capture(1); |
1439 if (result->IsString()) { | 1234 |
| 1235 IncrementalStringBuilder builder(isolate); |
| 1236 builder.AppendString(factory->NewSubString(string, 0, start_index)); |
| 1237 |
| 1238 if (replace->length() > 0) { |
| 1239 MatchInfoBackedMatch m(isolate, string, match_indices); |
| 1240 Handle<String> replacement; |
| 1241 ASSIGN_RETURN_ON_EXCEPTION(isolate, replacement, |
| 1242 String::GetSubstitution(isolate, &m, replace), |
| 1243 String); |
| 1244 builder.AppendString(replacement); |
| 1245 } |
| 1246 |
| 1247 builder.AppendString( |
| 1248 factory->NewSubString(string, end_index, string->length())); |
| 1249 return builder.Finish(); |
| 1250 } else { |
| 1251 // Global regexp search, string replace. |
| 1252 DCHECK(global); |
| 1253 RETURN_ON_EXCEPTION(isolate, RegExpUtils::SetLastIndex(isolate, regexp, 0), |
| 1254 String); |
| 1255 |
| 1256 if (replace->length() == 0) { |
| 1257 if (string->HasOnlyOneByteChars()) { |
| 1258 Object* result = |
| 1259 StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>( |
| 1260 isolate, string, regexp, last_match_info); |
1440 return handle(String::cast(result), isolate); | 1261 return handle(String::cast(result), isolate); |
1441 } else { | 1262 } else { |
1442 return MaybeHandle<String>(); | 1263 Object* result = |
| 1264 StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>( |
| 1265 isolate, string, regexp, last_match_info); |
| 1266 return handle(String::cast(result), isolate); |
1443 } | 1267 } |
1444 } | 1268 } |
1445 } else { | 1269 |
1446 DCHECK(functional_replace); | 1270 Object* result = StringReplaceGlobalRegExpWithString( |
1447 if (global) { | 1271 isolate, string, regexp, replace, last_match_info); |
1448 // Global regexp search, function replace. | 1272 if (result->IsString()) { |
1449 return StringReplaceGlobalRegExpWithFunction(isolate, string, regexp, | 1273 return handle(String::cast(result), isolate); |
1450 replace_obj); | |
1451 } else { | 1274 } else { |
1452 // Non-global regexp search, function replace. | 1275 return MaybeHandle<String>(); |
1453 return StringReplaceNonGlobalRegExpWithFunction(isolate, string, regexp, | |
1454 replace_obj); | |
1455 } | 1276 } |
1456 } | 1277 } |
1457 | 1278 |
1458 UNREACHABLE(); | 1279 UNREACHABLE(); |
1459 return MaybeHandle<String>(); | 1280 return MaybeHandle<String>(); |
1460 } | 1281 } |
1461 | 1282 |
1462 } // namespace | 1283 } // namespace |
1463 | 1284 |
1464 RUNTIME_FUNCTION(Runtime_StringReplaceGlobalRegExpWithFunction) { | 1285 // This is only called for StringReplaceGlobalRegExpWithFunction. |
1465 HandleScope scope(isolate); | 1286 RUNTIME_FUNCTION(Runtime_RegExpExecMultiple) { |
1466 DCHECK(args.length() == 3); | 1287 HandleScope handles(isolate); |
| 1288 DCHECK(args.length() == 4); |
1467 | 1289 |
1468 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); | 1290 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0); |
1469 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1); | 1291 CONVERT_ARG_HANDLE_CHECKED(String, subject, 1); |
1470 CONVERT_ARG_HANDLE_CHECKED(JSObject, replace, 2); | 1292 CONVERT_ARG_HANDLE_CHECKED(RegExpMatchInfo, last_match_info, 2); |
| 1293 CONVERT_ARG_HANDLE_CHECKED(JSArray, result_array, 3); |
| 1294 CHECK(result_array->HasFastObjectElements()); |
1471 | 1295 |
1472 RETURN_RESULT_OR_FAILURE(isolate, StringReplaceGlobalRegExpWithFunction( | 1296 subject = String::Flatten(subject); |
1473 isolate, subject, regexp, replace)); | 1297 CHECK(regexp->GetFlags() & JSRegExp::kGlobal); |
| 1298 |
| 1299 if (regexp->CaptureCount() == 0) { |
| 1300 return SearchRegExpMultiple<false>(isolate, subject, regexp, |
| 1301 last_match_info, result_array); |
| 1302 } else { |
| 1303 return SearchRegExpMultiple<true>(isolate, subject, regexp, last_match_info, |
| 1304 result_array); |
| 1305 } |
1474 } | 1306 } |
1475 | 1307 |
1476 RUNTIME_FUNCTION(Runtime_StringReplaceNonGlobalRegExpWithFunction) { | 1308 RUNTIME_FUNCTION(Runtime_StringReplaceNonGlobalRegExpWithFunction) { |
1477 HandleScope scope(isolate); | 1309 HandleScope scope(isolate); |
1478 DCHECK(args.length() == 3); | 1310 DCHECK(args.length() == 3); |
1479 | 1311 |
1480 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); | 1312 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); |
1481 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1); | 1313 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1); |
1482 CONVERT_ARG_HANDLE_CHECKED(JSObject, replace, 2); | 1314 CONVERT_ARG_HANDLE_CHECKED(JSObject, replace, 2); |
1483 | 1315 |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1667 | 1499 |
1668 RUNTIME_FUNCTION(Runtime_IsRegExp) { | 1500 RUNTIME_FUNCTION(Runtime_IsRegExp) { |
1669 SealHandleScope shs(isolate); | 1501 SealHandleScope shs(isolate); |
1670 DCHECK(args.length() == 1); | 1502 DCHECK(args.length() == 1); |
1671 CONVERT_ARG_CHECKED(Object, obj, 0); | 1503 CONVERT_ARG_CHECKED(Object, obj, 0); |
1672 return isolate->heap()->ToBoolean(obj->IsJSRegExp()); | 1504 return isolate->heap()->ToBoolean(obj->IsJSRegExp()); |
1673 } | 1505 } |
1674 | 1506 |
1675 } // namespace internal | 1507 } // namespace internal |
1676 } // namespace v8 | 1508 } // namespace v8 |
OLD | NEW |