Index: src/builtins/builtins-regexp.cc |
diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc |
index 7ddfc58c9ead540ee92421ad774a7e851c382571..6420297da84a2ddd6ae9642b69e9b795ac50847f 100644 |
--- a/src/builtins/builtins-regexp.cc |
+++ b/src/builtins/builtins-regexp.cc |
@@ -1235,5 +1235,385 @@ BUILTIN(RegExpPrototypeSearch) { |
isolate, Object::GetProperty(result, isolate->factory()->index_string())); |
} |
+namespace { |
+ |
+MUST_USE_RESULT MaybeHandle<Object> ToUint32(Isolate* isolate, |
+ Handle<Object> object, |
+ uint32_t* out) { |
+ if (object->IsUndefined(isolate)) { |
+ *out = kMaxUInt32; |
+ return object; |
+ } |
+ |
+ Handle<Object> number; |
+ ASSIGN_RETURN_ON_EXCEPTION(isolate, number, Object::ToNumber(object), Object); |
+ *out = NumberToUint32(*number); |
+ return object; |
+} |
+ |
+bool AtSurrogatePair(Isolate* isolate, Handle<String> string, int index) { |
+ if (index + 1 >= string->length()) return false; |
+ const uint16_t first = string->Get(index); |
+ if (first < 0xD800 || first > 0xDBFF) return false; |
+ const uint16_t second = string->Get(index + 1); |
+ return (second >= 0xDC00 && second <= 0xDFFF); |
+} |
+ |
+Handle<JSArray> NewJSArrayWithElements(Isolate* isolate, |
+ Handle<FixedArray> elems, |
+ int num_elems) { |
+ elems->Shrink(num_elems); |
+ return isolate->factory()->NewJSArrayWithElements(elems); |
+} |
+ |
+MaybeHandle<JSArray> RegExpSplit(Isolate* isolate, Handle<JSRegExp> regexp, |
+ Handle<String> string, |
+ Handle<Object> limit_obj) { |
+ Factory* factory = isolate->factory(); |
+ |
+ uint32_t limit; |
+ RETURN_ON_EXCEPTION(isolate, ToUint32(isolate, limit_obj, &limit), JSArray); |
+ |
+ const int length = string->length(); |
+ |
+ if (limit == 0) return factory->NewJSArray(0); |
+ |
+ Handle<JSObject> last_match_info = isolate->regexp_last_match_info(); |
+ |
+ if (length == 0) { |
+ Handle<Object> match_indices; |
+ ASSIGN_RETURN_ON_EXCEPTION( |
+ isolate, match_indices, |
+ RegExpImpl::Exec(regexp, string, 0, last_match_info), JSArray); |
+ |
+ if (!match_indices->IsNull(isolate)) return factory->NewJSArray(0); |
+ |
+ Handle<FixedArray> elems = factory->NewUninitializedFixedArray(1); |
+ elems->set(0, *string); |
+ return factory->NewJSArrayWithElements(elems); |
+ } |
+ |
+ int current_index = 0; |
+ int start_index = 0; |
+ int start_match = 0; |
+ |
+ static const int kInitialArraySize = 8; |
+ Handle<FixedArray> elems = factory->NewFixedArrayWithHoles(kInitialArraySize); |
+ int num_elems = 0; |
+ |
+ while (true) { |
+ if (start_index == length) { |
+ Handle<String> substr = |
+ factory->NewSubString(string, current_index, length); |
+ elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
+ break; |
+ } |
+ |
+ Handle<Object> match_indices_obj; |
+ ASSIGN_RETURN_ON_EXCEPTION( |
+ isolate, match_indices_obj, |
+ RegExpImpl::Exec(regexp, string, start_index, last_match_info), |
+ JSArray); |
+ |
+ if (match_indices_obj->IsNull(isolate)) { |
+ Handle<String> substr = |
+ factory->NewSubString(string, current_index, length); |
+ elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
+ break; |
+ } |
+ |
+ auto match_indices = Handle<JSReceiver>::cast(match_indices_obj); |
+ |
+ Handle<Object> start_match_obj = |
+ JSReceiver::GetElement(isolate, match_indices, |
+ RegExpImpl::kFirstCapture) |
+ .ToHandleChecked(); |
+ start_match = Handle<Smi>::cast(start_match_obj)->value(); |
+ |
+ if (start_match == length) { |
+ Handle<String> substr = |
+ factory->NewSubString(string, current_index, length); |
+ elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
+ break; |
+ } |
+ |
+ Handle<Object> end_index_obj = |
+ JSReceiver::GetElement(isolate, match_indices, |
+ RegExpImpl::kFirstCapture + 1) |
+ .ToHandleChecked(); |
+ const int end_index = Handle<Smi>::cast(end_index_obj)->value(); |
+ |
+ if (start_index == end_index && end_index == current_index) { |
+ const bool unicode = (regexp->GetFlags() & JSRegExp::kUnicode) != 0; |
+ if (unicode && AtSurrogatePair(isolate, string, start_index)) { |
+ start_index += 2; |
+ } else { |
+ start_index += 1; |
+ } |
+ continue; |
+ } |
+ |
+ { |
+ Handle<String> substr = |
+ factory->NewSubString(string, current_index, start_match); |
+ elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
+ } |
+ |
+ if (num_elems == limit) break; |
+ |
+ // TODO(jgruber): Refactor GetLastMatchInfo methods to take an input |
+ // argument. |
+ Handle<Object> num_captures_obj = |
+ JSReceiver::GetElement(isolate, match_indices, |
+ RegExpImpl::kLastCaptureCount) |
+ .ToHandleChecked(); |
+ const int match_indices_len = Handle<Smi>::cast(num_captures_obj)->value() + |
+ RegExpImpl::kFirstCapture; |
+ |
+ for (int i = RegExpImpl::kFirstCapture + 2; i < match_indices_len;) { |
+ Handle<Object> start_obj = |
+ JSReceiver::GetElement(isolate, match_indices, i++).ToHandleChecked(); |
+ const int start = Handle<Smi>::cast(start_obj)->value(); |
+ |
+ Handle<Object> end_obj = |
+ JSReceiver::GetElement(isolate, match_indices, i++).ToHandleChecked(); |
+ const int end = Handle<Smi>::cast(end_obj)->value(); |
+ |
+ if (end != -1) { |
+ Handle<String> substr = factory->NewSubString(string, start, end); |
+ elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
+ } else { |
+ elems = FixedArray::SetAndGrow(elems, num_elems++, |
+ factory->undefined_value()); |
+ } |
+ |
+ if (num_elems == limit) { |
+ return NewJSArrayWithElements(isolate, elems, num_elems); |
+ } |
+ } |
+ |
+ start_index = current_index = end_index; |
+ } |
+ |
+ return NewJSArrayWithElements(isolate, elems, num_elems); |
+} |
+ |
+// ES##sec-speciesconstructor |
+// SpeciesConstructor ( O, defaultConstructor ) |
+MaybeHandle<Object> SpeciesConstructor(Isolate* isolate, |
+ Handle<JSReceiver> recv, |
+ Handle<JSFunction> default_ctor) { |
+ Handle<Object> ctor_obj; |
+ ASSIGN_RETURN_ON_EXCEPTION( |
+ isolate, ctor_obj, |
+ JSObject::GetProperty(recv, isolate->factory()->constructor_string()), |
+ Object); |
+ |
+ if (ctor_obj->IsUndefined(isolate)) return default_ctor; |
+ |
+ if (!ctor_obj->IsJSReceiver()) { |
+ THROW_NEW_ERROR(isolate, |
+ NewTypeError(MessageTemplate::kConstructorNotReceiver), |
+ Object); |
+ } |
+ |
+ Handle<JSReceiver> ctor = Handle<JSReceiver>::cast(ctor_obj); |
+ |
+ Handle<Object> species; |
+ ASSIGN_RETURN_ON_EXCEPTION( |
+ isolate, species, |
+ JSObject::GetProperty(ctor, isolate->factory()->species_symbol()), |
+ Object); |
+ |
+ if (species->IsNull(isolate) || species->IsUndefined(isolate)) { |
+ return default_ctor; |
+ } |
+ |
+ if (species->IsConstructor()) return species; |
+ |
+ THROW_NEW_ERROR( |
+ isolate, NewTypeError(MessageTemplate::kSpeciesNotConstructor), Object); |
+} |
+ |
+bool IsBuiltinExec(Handle<Object> exec) { |
+ if (!exec->IsJSFunction()) return false; |
+ |
+ Code* code = Handle<JSFunction>::cast(exec)->code(); |
+ if (code == nullptr) return false; |
+ |
+ return (code->builtin_index() == Builtins::kRegExpPrototypeExec); |
+} |
+ |
+} // namespace |
+ |
+// ES#sec-regexp.prototype-@@split |
+// RegExp.prototype [ @@split ] ( string, limit ) |
+BUILTIN(RegExpPrototypeSplit) { |
+ HandleScope scope(isolate); |
+ CHECK_RECEIVER(JSReceiver, recv, "RegExp.prototype.@@split"); |
+ |
+ Factory* factory = isolate->factory(); |
+ |
+ Handle<Object> string_obj = args.atOrUndefined(isolate, 1); |
+ Handle<Object> limit_obj = args.atOrUndefined(isolate, 2); |
+ |
+ Handle<String> string; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string, |
+ Object::ToString(isolate, string_obj)); |
+ |
+ Handle<JSFunction> regexp_fun = isolate->regexp_function(); |
+ Handle<Object> ctor; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, ctor, SpeciesConstructor(isolate, recv, regexp_fun)); |
+ |
+ if (recv->IsJSRegExp() && *ctor == *regexp_fun) { |
+ Handle<Object> exec; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, exec, JSObject::GetProperty( |
+ recv, factory->NewStringFromAsciiChecked("exec"))); |
+ if (IsBuiltinExec(exec)) { |
+ RETURN_RESULT_OR_FAILURE( |
+ isolate, RegExpSplit(isolate, Handle<JSRegExp>::cast(recv), string, |
+ limit_obj)); |
+ } |
+ } |
+ |
+ Handle<Object> flags_obj; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, flags_obj, JSObject::GetProperty(recv, factory->flags_string())); |
+ |
+ Handle<String> flags; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, flags, |
+ Object::ToString(isolate, flags_obj)); |
+ |
+ Handle<String> u_str = factory->LookupSingleCharacterStringFromCode('u'); |
+ const bool unicode = (String::IndexOf(isolate, flags, u_str, 0) >= 0); |
+ |
+ Handle<String> y_str = factory->LookupSingleCharacterStringFromCode('y'); |
+ const bool sticky = (String::IndexOf(isolate, flags, y_str, 0) >= 0); |
+ |
+ Handle<String> new_flags = flags; |
+ if (!sticky) { |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, new_flags, |
+ factory->NewConsString(flags, y_str)); |
+ } |
+ |
+ Handle<JSReceiver> splitter; |
+ { |
+ const int argc = 2; |
+ |
+ ScopedVector<Handle<Object>> argv(argc); |
+ argv[0] = recv; |
+ argv[1] = new_flags; |
+ |
+ Handle<JSFunction> ctor_fun = Handle<JSFunction>::cast(ctor); |
+ Handle<Object> splitter_obj; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, splitter_obj, Execution::New(ctor_fun, argc, argv.start())); |
+ |
+ splitter = Handle<JSReceiver>::cast(splitter_obj); |
+ } |
+ |
+ uint32_t limit; |
+ RETURN_FAILURE_ON_EXCEPTION(isolate, ToUint32(isolate, limit_obj, &limit)); |
+ |
+ const int length = string->length(); |
+ |
+ if (limit == 0) return *factory->NewJSArray(0); |
+ |
+ if (length == 0) { |
+ Handle<Object> result; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, result, |
+ RegExpExec(isolate, splitter, string, factory->undefined_value())); |
+ |
+ if (!result->IsNull(isolate)) return *factory->NewJSArray(0); |
+ |
+ Handle<FixedArray> elems = factory->NewUninitializedFixedArray(1); |
+ elems->set(0, *string); |
+ return *factory->NewJSArrayWithElements(elems); |
+ } |
+ |
+ // TODO(jgruber): Wrap this in a helper class. |
+ static const int kInitialArraySize = 8; |
+ Handle<FixedArray> elems = factory->NewFixedArrayWithHoles(kInitialArraySize); |
+ int num_elems = 0; |
+ |
+ int string_index = 0; |
+ int prev_string_index = 0; |
+ while (string_index < length) { |
+ RETURN_FAILURE_ON_EXCEPTION(isolate, |
+ SetLastIndex(isolate, splitter, string_index)); |
+ |
+ Handle<Object> result; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, result, |
+ RegExpExec(isolate, splitter, string, factory->undefined_value())); |
+ |
+ if (result->IsNull(isolate)) { |
+ string_index += |
+ AdvanceStringIndex(isolate, string, string_index, unicode); |
+ continue; |
+ } |
+ |
+ // TODO(jgruber): Extract toLength of some property into function. |
+ Handle<Object> last_index_obj; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, last_index_obj, |
+ GetLastIndex(isolate, splitter)); |
+ |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, last_index_obj, Object::ToLength(isolate, last_index_obj)); |
+ const int last_index = Handle<Smi>::cast(last_index_obj)->value(); |
+ |
+ const int end = std::min(last_index, length); |
+ if (end == prev_string_index) { |
+ string_index += |
+ AdvanceStringIndex(isolate, string, string_index, unicode); |
+ continue; |
+ } |
+ |
+ { |
+ Handle<String> substr = |
+ factory->NewSubString(string, prev_string_index, string_index); |
+ elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
+ if (num_elems == limit) { |
+ return *NewJSArrayWithElements(isolate, elems, num_elems); |
+ } |
+ } |
+ |
+ prev_string_index = end; |
+ |
+ Handle<Object> num_captures_obj; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, num_captures_obj, |
+ Object::GetProperty(result, isolate->factory()->length_string())); |
+ |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, num_captures_obj, Object::ToLength(isolate, num_captures_obj)); |
+ const int num_captures = |
+ std::max(Handle<Smi>::cast(num_captures_obj)->value(), 0); |
+ |
+ for (int i = 1; i < num_captures; i++) { |
+ Handle<Object> capture; |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, capture, Object::GetElement(isolate, result, i)); |
+ elems = FixedArray::SetAndGrow(elems, num_elems++, capture); |
+ if (num_elems == limit) { |
+ return *NewJSArrayWithElements(isolate, elems, num_elems); |
+ } |
+ } |
+ |
+ string_index = prev_string_index; |
+ } |
+ |
+ { |
+ Handle<String> substr = |
+ factory->NewSubString(string, prev_string_index, length); |
+ elems = FixedArray::SetAndGrow(elems, num_elems++, substr); |
+ } |
+ |
+ return *NewJSArrayWithElements(isolate, elems, num_elems); |
+} |
+ |
} // namespace internal |
} // namespace v8 |