}
-int RegExpImpl::AtomExecRaw(Handle<JSRegExp> regexp,
- Handle<String> subject,
- int index,
- int32_t* output,
- int output_size) {
- Isolate* isolate = regexp->GetIsolate();
+Handle<Object> RegExpImpl::AtomExec(Handle<JSRegExp> re,
+ Handle<String> subject,
+ int index,
+ Handle<JSArray> last_match_info) {
+ Isolate* isolate = re->GetIsolate();
ASSERT(0 <= index);
ASSERT(index <= subject->length());
if (!subject->IsFlat()) FlattenString(subject);
AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
- String* needle = String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex));
+ String* needle = String::cast(re->DataAt(JSRegExp::kAtomPatternIndex));
int needle_len = needle->length();
ASSERT(needle->IsFlat());
- ASSERT_LT(0, needle_len);
- if (index + needle_len > subject->length()) {
- return RegExpImpl::RE_FAILURE;
- }
+ if (needle_len != 0) {
+ if (index + needle_len > subject->length()) {
+ return isolate->factory()->null_value();
+ }
- for (int i = 0; i < output_size; i += 2) {
String::FlatContent needle_content = needle->GetFlatContent();
String::FlatContent subject_content = subject->GetFlatContent();
ASSERT(needle_content.IsFlat());
subject_content.ToUC16Vector(),
needle_content.ToUC16Vector(),
index)));
- if (index == -1) {
- return i / 2; // Return number of matches.
- } else {
- output[i] = index;
- output[i+1] = index + needle_len;
- index += needle_len;
- }
+ if (index == -1) return isolate->factory()->null_value();
}
- return output_size / 2;
-}
+ ASSERT(last_match_info->HasFastObjectElements());
-
-Handle<Object> RegExpImpl::AtomExec(Handle<JSRegExp> re,
- Handle<String> subject,
- int index,
- Handle<JSArray> last_match_info) {
- Isolate* isolate = re->GetIsolate();
-
- static const int kNumRegisters = 2;
- STATIC_ASSERT(kNumRegisters <= Isolate::kJSRegexpStaticOffsetsVectorSize);
- int32_t* output_registers = isolate->jsregexp_static_offsets_vector();
-
- int res = AtomExecRaw(re, subject, index, output_registers, kNumRegisters);
-
- if (res == RegExpImpl::RE_FAILURE) return isolate->factory()->null_value();
-
- ASSERT_EQ(res, RegExpImpl::RE_SUCCESS);
- NoHandleAllocation no_handles;
- FixedArray* array = FixedArray::cast(last_match_info->elements());
- SetAtomLastCapture(array, *subject, output_registers[0], output_registers[1]);
+ {
+ NoHandleAllocation no_handles;
+ FixedArray* array = FixedArray::cast(last_match_info->elements());
+ SetAtomLastCapture(array, *subject, index, index + needle_len);
+ }
return last_match_info;
}
#ifdef V8_INTERPRETED_REGEXP
// Byte-code regexp needs space allocated for all its registers.
- // The result captures are copied to the start of the registers array
- // if the match succeeds. This way those registers are not clobbered
- // when we set the last match info from last successful match.
- return IrregexpNumberOfRegisters(FixedArray::cast(regexp->data())) +
- (IrregexpNumberOfCaptures(FixedArray::cast(regexp->data())) + 1) * 2;
+ return IrregexpNumberOfRegisters(FixedArray::cast(regexp->data()));
#else // V8_INTERPRETED_REGEXP
// Native regexp only needs room to output captures. Registers are handled
// internally.
}
-int RegExpImpl::IrregexpExecRaw(Handle<JSRegExp> regexp,
- Handle<String> subject,
- int index,
- int32_t* output,
- int output_size) {
+int RegExpImpl::GlobalOffsetsVectorSize(Handle<JSRegExp> regexp,
+ int registers_per_match,
+ int* max_matches) {
+#ifdef V8_INTERPRETED_REGEXP
+ // Global loop in interpreted regexp is not implemented. Therefore we choose
+ // the size of the offsets vector so that it can only store one match.
+ *max_matches = 1;
+ return registers_per_match;
+#else // V8_INTERPRETED_REGEXP
+ int size = Max(registers_per_match, OffsetsVector::kStaticOffsetsVectorSize);
+ *max_matches = size / registers_per_match;
+ return size;
+#endif // V8_INTERPRETED_REGEXP
+}
+
+
+int RegExpImpl::IrregexpExecRaw(
+ Handle<JSRegExp> regexp,
+ Handle<String> subject,
+ int index,
+ Vector<int> output) {
Isolate* isolate = regexp->GetIsolate();
Handle<FixedArray> irregexp(FixedArray::cast(regexp->data()), isolate);
bool is_ascii = subject->IsAsciiRepresentationUnderneath();
#ifndef V8_INTERPRETED_REGEXP
- ASSERT(output_size >= (IrregexpNumberOfCaptures(*irregexp) + 1) * 2);
+ ASSERT(output.length() >= (IrregexpNumberOfCaptures(*irregexp) + 1) * 2);
do {
EnsureCompiledIrregexp(regexp, subject, is_ascii);
Handle<Code> code(IrregexpNativeCode(*irregexp, is_ascii), isolate);
- // The stack is used to allocate registers for the compiled regexp code.
- // This means that in case of failure, the output registers array is left
- // untouched and contains the capture results from the previous successful
- // match. We can use that to set the last match info lazily.
NativeRegExpMacroAssembler::Result res =
NativeRegExpMacroAssembler::Match(code,
subject,
- output,
- output_size,
+ output.start(),
+ output.length(),
index,
isolate);
if (res != NativeRegExpMacroAssembler::RETRY) {
return RE_EXCEPTION;
#else // V8_INTERPRETED_REGEXP
- ASSERT(output_size >= IrregexpNumberOfRegisters(*irregexp));
+ ASSERT(output.length() >= IrregexpNumberOfRegisters(*irregexp));
// We must have done EnsureCompiledIrregexp, so we can get the number of
// registers.
+ int* register_vector = output.start();
int number_of_capture_registers =
(IrregexpNumberOfCaptures(*irregexp) + 1) * 2;
- int32_t* raw_output = &output[number_of_capture_registers];
- // We do not touch the actual capture result registers until we know there
- // has been a match so that we can use those capture results to set the
- // last match info.
for (int i = number_of_capture_registers - 1; i >= 0; i--) {
- raw_output[i] = -1;
+ register_vector[i] = -1;
}
Handle<ByteArray> byte_codes(IrregexpByteCode(*irregexp, is_ascii), isolate);
IrregexpResult result = IrregexpInterpreter::Match(isolate,
byte_codes,
subject,
- raw_output,
+ register_vector,
index);
- if (result == RE_SUCCESS) {
- // Copy capture results to the start of the registers array.
- memcpy(output, raw_output, number_of_capture_registers * sizeof(int32_t));
- }
if (result == RE_EXCEPTION) {
ASSERT(!isolate->has_pending_exception());
isolate->StackOverflow();
}
-Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> regexp,
+Handle<Object> RegExpImpl::IrregexpExec(Handle<JSRegExp> jsregexp,
Handle<String> subject,
int previous_index,
Handle<JSArray> last_match_info) {
- Isolate* isolate = regexp->GetIsolate();
- ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
+ Isolate* isolate = jsregexp->GetIsolate();
+ ASSERT_EQ(jsregexp->TypeTag(), JSRegExp::IRREGEXP);
// Prepare space for the return values.
-#if defined(V8_INTERPRETED_REGEXP) && defined(DEBUG)
+#ifdef V8_INTERPRETED_REGEXP
+#ifdef DEBUG
if (FLAG_trace_regexp_bytecodes) {
- String* pattern = regexp->Pattern();
+ String* pattern = jsregexp->Pattern();
PrintF("\n\nRegexp match: /%s/\n\n", *(pattern->ToCString()));
PrintF("\n\nSubject string: '%s'\n\n", *(subject->ToCString()));
}
#endif
- int required_registers = RegExpImpl::IrregexpPrepare(regexp, subject);
+#endif
+ int required_registers = RegExpImpl::IrregexpPrepare(jsregexp, subject);
if (required_registers < 0) {
// Compiling failed with an exception.
ASSERT(isolate->has_pending_exception());
return Handle<Object>::null();
}
- int32_t* output_registers = NULL;
- if (required_registers > Isolate::kJSRegexpStaticOffsetsVectorSize) {
- output_registers = NewArray<int32_t>(required_registers);
- }
- SmartArrayPointer<int32_t> auto_release(output_registers);
- if (output_registers == NULL) {
- output_registers = isolate->jsregexp_static_offsets_vector();
- }
+ OffsetsVector registers(required_registers, isolate);
- int res = RegExpImpl::IrregexpExecRaw(
- regexp, subject, previous_index, output_registers, required_registers);
+ int res = RegExpImpl::IrregexpExecRaw(jsregexp, subject, previous_index,
+ Vector<int>(registers.vector(),
+ registers.length()));
if (res == RE_SUCCESS) {
- int capture_count =
- IrregexpNumberOfCaptures(FixedArray::cast(regexp->data()));
- return SetLastMatchInfo(
- last_match_info, subject, capture_count, output_registers);
+ int capture_register_count =
+ (IrregexpNumberOfCaptures(FixedArray::cast(jsregexp->data())) + 1) * 2;
+ last_match_info->EnsureSize(capture_register_count + kLastMatchOverhead);
+ AssertNoAllocation no_gc;
+ int* register_vector = registers.vector();
+ FixedArray* array = FixedArray::cast(last_match_info->elements());
+ for (int i = 0; i < capture_register_count; i += 2) {
+ SetCapture(array, i, register_vector[i]);
+ SetCapture(array, i + 1, register_vector[i + 1]);
+ }
+ SetLastCaptureCount(array, capture_register_count);
+ SetLastSubject(array, *subject);
+ SetLastInput(array, *subject);
+ return last_match_info;
}
if (res == RE_EXCEPTION) {
ASSERT(isolate->has_pending_exception());
}
-Handle<JSArray> RegExpImpl::SetLastMatchInfo(Handle<JSArray> last_match_info,
- Handle<String> subject,
- int capture_count,
- int32_t* match) {
- int capture_register_count = (capture_count + 1) * 2;
- last_match_info->EnsureSize(capture_register_count + kLastMatchOverhead);
- AssertNoAllocation no_gc;
- FixedArray* array = FixedArray::cast(last_match_info->elements());
- if (match != NULL) {
- for (int i = 0; i < capture_register_count; i += 2) {
- SetCapture(array, i, match[i]);
- SetCapture(array, i + 1, match[i + 1]);
- }
- }
- SetLastCaptureCount(array, capture_register_count);
- SetLastSubject(array, *subject);
- SetLastInput(array, *subject);
- return last_match_info;
-}
-
-
-RegExpImpl::GlobalCache::GlobalCache(Handle<JSRegExp> regexp,
- Handle<String> subject,
- bool is_global,
- Isolate* isolate) {
-#ifdef V8_INTERPRETED_REGEXP
- bool interpreted = true;
-#else
- bool interpreted = false;
-#endif // V8_INTERPRETED_REGEXP
-
- regexp_ = regexp;
- subject_ = subject;
-
- if (regexp_->TypeTag() == JSRegExp::ATOM) {
- static const int kAtomRegistersPerMatch = 2;
- registers_per_match_ = kAtomRegistersPerMatch;
- // There is no distinction between interpreted and native for atom regexps.
- interpreted = false;
- } else {
- registers_per_match_ = RegExpImpl::IrregexpPrepare(regexp_, subject_);
- if (registers_per_match_ < 0) {
- num_matches_ = -1; // Signal exception.
- return;
- }
- }
-
- if (is_global && !interpreted) {
- register_array_size_ =
- Max(registers_per_match_, Isolate::kJSRegexpStaticOffsetsVectorSize);
- max_matches_ = register_array_size_ / registers_per_match_;
- } else {
- // Global loop in interpreted regexp is not implemented. We choose
- // the size of the offsets vector so that it can only store one match.
- register_array_size_ = registers_per_match_;
- max_matches_ = 1;
- }
-
- if (register_array_size_ > Isolate::kJSRegexpStaticOffsetsVectorSize) {
- register_array_ = NewArray<int32_t>(register_array_size_);
- } else {
- register_array_ = isolate->jsregexp_static_offsets_vector();
- }
-
- // Set state so that fetching the results the first time triggers a call
- // to the compiled regexp.
- current_match_index_ = max_matches_ - 1;
- num_matches_ = max_matches_;
- ASSERT(registers_per_match_ >= 2); // Each match has at least one capture.
- ASSERT_GE(register_array_size_, registers_per_match_);
- int32_t* last_match =
- ®ister_array_[current_match_index_ * registers_per_match_];
- last_match[0] = -1;
- last_match[1] = 0;
-}
-
-
-RegExpImpl::GlobalCache::~GlobalCache() {
- // Deallocate the register array if we allocated it in the constructor
- // (as opposed to using the existing jsregexp_static_offsets_vector).
- if (register_array_size_ > Isolate::kJSRegexpStaticOffsetsVectorSize) {
- DeleteArray(register_array_);
- }
-}
-
-
-int32_t* RegExpImpl::GlobalCache::FetchNext() {
- current_match_index_++;
- if (current_match_index_ >= num_matches_) {
- // Current batch of results exhausted.
- // Fail if last batch was not even fully filled.
- if (num_matches_ < max_matches_) {
- num_matches_ = 0; // Signal failed match.
- return NULL;
- }
-
- int32_t* last_match =
- ®ister_array_[(current_match_index_ - 1) * registers_per_match_];
- int last_end_index = last_match[1];
-
- if (regexp_->TypeTag() == JSRegExp::ATOM) {
- num_matches_ = RegExpImpl::AtomExecRaw(regexp_,
- subject_,
- last_end_index,
- register_array_,
- register_array_size_);
- } else {
- int last_start_index = last_match[0];
- if (last_start_index == last_end_index) last_end_index++;
- if (last_end_index > subject_->length()) {
- num_matches_ = 0; // Signal failed match.
- return NULL;
- }
- num_matches_ = RegExpImpl::IrregexpExecRaw(regexp_,
- subject_,
- last_end_index,
- register_array_,
- register_array_size_);
- }
-
- if (num_matches_ <= 0) return NULL;
- current_match_index_ = 0;
- return register_array_;
- } else {
- return ®ister_array_[current_match_index_ * registers_per_match_];
- }
-}
-
-
-int32_t* RegExpImpl::GlobalCache::LastSuccessfulMatch() {
- int index = current_match_index_ * registers_per_match_;
- if (num_matches_ == 0) {
- // After a failed match we shift back by one result.
- index -= registers_per_match_;
- }
- return ®ister_array_[index];
-}
-
-
// -------------------------------------------------------------------
// Implementation of the Irregexp regular expression engine.
//
class CompiledReplacement {
public:
explicit CompiledReplacement(Zone* zone)
- : parts_(1, zone), replacement_substrings_(0, zone), zone_(zone) {}
+ : parts_(1, zone), replacement_substrings_(0, zone),
+ simple_hint_(false),
+ zone_(zone) {}
- // Return whether the replacement is simple.
- bool Compile(Handle<String> replacement,
+ void Compile(Handle<String> replacement,
int capture_count,
int subject_length);
- // Use Apply only if Compile returned false.
void Apply(ReplacementStringBuilder* builder,
int match_from,
int match_to,
- int32_t* match);
+ Handle<JSArray> last_match_info);
// Number of distinct parts of the replacement pattern.
int parts() {
return parts_.length();
}
+ bool simple_hint() {
+ return simple_hint_;
+ }
+
Zone* zone() const { return zone_; }
private:
};
template<typename Char>
- bool ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
- Vector<Char> characters,
- int capture_count,
- int subject_length,
- Zone* zone) {
+ static bool ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
+ Vector<Char> characters,
+ int capture_count,
+ int subject_length,
+ Zone* zone) {
int length = characters.length();
int last = 0;
for (int i = 0; i < length; i++) {
}
if (length > last) {
if (last == 0) {
- // Replacement is simple. Do not use Apply to do the replacement.
+ parts->Add(ReplacementPart::ReplacementString(), zone);
return true;
} else {
parts->Add(ReplacementPart::ReplacementSubString(last, length), zone);
ZoneList<ReplacementPart> parts_;
ZoneList<Handle<String> > replacement_substrings_;
+ bool simple_hint_;
Zone* zone_;
};
-bool CompiledReplacement::Compile(Handle<String> replacement,
+void CompiledReplacement::Compile(Handle<String> replacement,
int capture_count,
int subject_length) {
{
AssertNoAllocation no_alloc;
String::FlatContent content = replacement->GetFlatContent();
ASSERT(content.IsFlat());
- bool simple = false;
if (content.IsAscii()) {
- simple = ParseReplacementPattern(&parts_,
- content.ToAsciiVector(),
- capture_count,
- subject_length,
- zone());
+ simple_hint_ = ParseReplacementPattern(&parts_,
+ content.ToAsciiVector(),
+ capture_count,
+ subject_length,
+ zone());
} else {
ASSERT(content.IsTwoByte());
- simple = ParseReplacementPattern(&parts_,
- content.ToUC16Vector(),
- capture_count,
- subject_length,
- zone());
+ simple_hint_ = ParseReplacementPattern(&parts_,
+ content.ToUC16Vector(),
+ capture_count,
+ subject_length,
+ zone());
}
- if (simple) return true;
}
-
Isolate* isolate = replacement->GetIsolate();
// Find substrings of replacement string and create them as String objects.
int substring_index = 0;
substring_index++;
}
}
- return false;
}
void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
int match_from,
int match_to,
- int32_t* match) {
- ASSERT_LT(0, parts_.length());
+ Handle<JSArray> last_match_info) {
for (int i = 0, n = parts_.length(); i < n; i++) {
ReplacementPart part = parts_[i];
switch (part.tag) {
}
case SUBJECT_CAPTURE: {
int capture = part.data;
- int from = match[capture * 2];
- int to = match[capture * 2 + 1];
+ FixedArray* match_info = FixedArray::cast(last_match_info->elements());
+ int from = RegExpImpl::GetCapture(match_info, capture * 2);
+ int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
if (from >= 0 && to > from) {
builder->AddSubjectSlice(from, to);
}
}
+// Two smis before and after the match, for very long strings.
+const int kMaxBuilderEntriesPerRegExpMatch = 5;
+
+
+static void SetLastMatchInfoNoCaptures(Handle<String> subject,
+ Handle<JSArray> last_match_info,
+ int match_start,
+ int match_end) {
+ // Fill last_match_info with a single capture.
+ last_match_info->EnsureSize(2 + RegExpImpl::kLastMatchOverhead);
+ AssertNoAllocation no_gc;
+ FixedArray* elements = FixedArray::cast(last_match_info->elements());
+ RegExpImpl::SetLastCaptureCount(elements, 2);
+ RegExpImpl::SetLastInput(elements, *subject);
+ RegExpImpl::SetLastSubject(elements, *subject);
+ RegExpImpl::SetCapture(elements, 0, match_start);
+ RegExpImpl::SetCapture(elements, 1, match_end);
+}
+
+
+template <typename SubjectChar, typename PatternChar>
+static bool SearchStringMultiple(Isolate* isolate,
+ Vector<const SubjectChar> subject,
+ Vector<const PatternChar> pattern,
+ String* pattern_string,
+ FixedArrayBuilder* builder,
+ int* match_pos) {
+ int pos = *match_pos;
+ int subject_length = subject.length();
+ int pattern_length = pattern.length();
+ int max_search_start = subject_length - pattern_length;
+ StringSearch<PatternChar, SubjectChar> search(isolate, pattern);
+ while (pos <= max_search_start) {
+ if (!builder->HasCapacity(kMaxBuilderEntriesPerRegExpMatch)) {
+ *match_pos = pos;
+ return false;
+ }
+ // Position of end of previous match.
+ int match_end = pos + pattern_length;
+ int new_pos = search.Search(subject, match_end);
+ if (new_pos >= 0) {
+ // A match.
+ if (new_pos > match_end) {
+ ReplacementStringBuilder::AddSubjectSlice(builder,
+ match_end,
+ new_pos);
+ }
+ pos = new_pos;
+ builder->Add(pattern_string);
+ } else {
+ break;
+ }
+ }
+
+ if (pos < max_search_start) {
+ ReplacementStringBuilder::AddSubjectSlice(builder,
+ pos + pattern_length,
+ subject_length);
+ }
+ *match_pos = pos;
+ return true;
+}
+
+
+
+
template<typename ResultSeqString>
MUST_USE_RESULT static MaybeObject* StringReplaceAtomRegExpWithString(
Isolate* isolate,
Handle<String> subject,
Handle<JSRegExp> pattern_regexp,
Handle<String> replacement,
- Handle<JSArray> last_match_info) {
+ Handle<JSArray> last_match_info,
+ Zone* zone) {
ASSERT(subject->IsFlat());
ASSERT(replacement->IsFlat());
- Zone* zone = isolate->runtime_zone();
- ZoneScope zone_space(zone, DELETE_ON_EXIT);
- ZoneList<int> indices(8, zone);
+ ZoneScope zone_space(isolate->runtime_zone(), DELETE_ON_EXIT);
+ ZoneList<int> indices(8, isolate->runtime_zone());
ASSERT_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag());
String* pattern =
String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex));
int pattern_len = pattern->length();
int replacement_len = replacement->length();
- FindStringIndicesDispatch(
- isolate, *subject, pattern, &indices, 0xffffffff, zone);
+ FindStringIndicesDispatch(isolate, *subject, pattern, &indices, 0xffffffff,
+ zone);
int matches = indices.length();
if (matches == 0) return *subject;
subject_len);
}
- int32_t match_indices[] = { indices.at(matches - 1),
- indices.at(matches - 1) + pattern_len };
- RegExpImpl::SetLastMatchInfo(last_match_info, subject, 0, match_indices);
+ SetLastMatchInfoNoCaptures(subject,
+ last_match_info,
+ indices.at(matches - 1),
+ indices.at(matches - 1) + pattern_len);
return *result;
}
MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithString(
Isolate* isolate,
- Handle<String> subject,
- Handle<JSRegExp> regexp,
- Handle<String> replacement,
- Handle<JSArray> last_match_info) {
+ String* subject,
+ JSRegExp* regexp,
+ String* replacement,
+ JSArray* last_match_info,
+ Zone* zone) {
ASSERT(subject->IsFlat());
ASSERT(replacement->IsFlat());
- bool is_global = regexp->GetFlags().is_global();
- int capture_count = regexp->CaptureCount();
- int subject_length = subject->length();
+ HandleScope handles(isolate);
+
+ int length = subject->length();
+ Handle<String> subject_handle(subject);
+ Handle<JSRegExp> regexp_handle(regexp);
+ Handle<String> replacement_handle(replacement);
+ Handle<JSArray> last_match_info_handle(last_match_info);
+ Handle<Object> match = RegExpImpl::Exec(regexp_handle,
+ subject_handle,
+ 0,
+ last_match_info_handle);
+ if (match.is_null()) {
+ return Failure::Exception();
+ }
+ if (match->IsNull()) {
+ return *subject_handle;
+ }
+
+ int capture_count = regexp_handle->CaptureCount();
// CompiledReplacement uses zone allocation.
- Zone* zone = isolate->runtime_zone();
ZoneScope zonescope(zone, DELETE_ON_EXIT);
CompiledReplacement compiled_replacement(zone);
- bool simple_replace = compiled_replacement.Compile(replacement,
- capture_count,
- subject_length);
+
+ compiled_replacement.Compile(replacement_handle,
+ capture_count,
+ length);
+
+ bool is_global = regexp_handle->GetFlags().is_global();
// Shortcut for simple non-regexp global replacements
if (is_global &&
- regexp->TypeTag() == JSRegExp::ATOM &&
- simple_replace) {
- if (subject->HasOnlyAsciiChars()) {
+ regexp_handle->TypeTag() == JSRegExp::ATOM &&
+ compiled_replacement.simple_hint()) {
+ if (subject_handle->HasOnlyAsciiChars() &&
+ replacement_handle->HasOnlyAsciiChars()) {
return StringReplaceAtomRegExpWithString<SeqAsciiString>(
- isolate, subject, regexp, replacement, last_match_info);
- } else {
+ isolate,
+ subject_handle,
+ regexp_handle,
+ replacement_handle,
+ last_match_info_handle,
+ zone);
+ } else {
return StringReplaceAtomRegExpWithString<SeqTwoByteString>(
- isolate, subject, regexp, replacement, last_match_info);
+ isolate,
+ subject_handle,
+ regexp_handle,
+ replacement_handle,
+ last_match_info_handle,
+ zone);
}
}
- RegExpImpl::GlobalCache global_cache(regexp, subject, is_global, isolate);
- if (global_cache.HasException()) return Failure::Exception();
-
- int32_t* current_match = global_cache.FetchNext();
- if (current_match == NULL) {
- if (global_cache.HasException()) return Failure::Exception();
- return *subject;
- }
-
// Guessing the number of parts that the final result string is built
// from. Global regexps can match any number of times, so we guess
// conservatively.
int expected_parts =
(compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
ReplacementStringBuilder builder(isolate->heap(),
- subject,
+ subject_handle,
expected_parts);
+ // Index of end of last match.
+ int prev = 0;
+
+
// Number of parts added by compiled replacement plus preceeding
// string and possibly suffix after last match. It is possible for
// all components to use two elements when encoded as two smis.
const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
-
- int prev = 0;
-
+ bool matched = true;
do {
+ ASSERT(last_match_info_handle->HasFastObjectElements());
+ // Increase the capacity of the builder before entering local handle-scope,
+ // so its internal buffer can safely allocate a new handle if it grows.
builder.EnsureCapacity(parts_added_per_loop);
- int start = current_match[0];
- int end = current_match[1];
+ HandleScope loop_scope(isolate);
+ int start, end;
+ {
+ AssertNoAllocation match_info_array_is_not_in_a_handle;
+ FixedArray* match_info_array =
+ FixedArray::cast(last_match_info_handle->elements());
+
+ ASSERT_EQ(capture_count * 2 + 2,
+ RegExpImpl::GetLastCaptureCount(match_info_array));
+ start = RegExpImpl::GetCapture(match_info_array, 0);
+ end = RegExpImpl::GetCapture(match_info_array, 1);
+ }
if (prev < start) {
builder.AddSubjectSlice(prev, start);
}
+ compiled_replacement.Apply(&builder,
+ start,
+ end,
+ last_match_info_handle);
- if (simple_replace) {
- builder.AddString(replacement);
- } else {
- compiled_replacement.Apply(&builder,
- start,
- end,
- current_match);
- }
prev = end;
// Only continue checking for global regexps.
if (!is_global) break;
- current_match = global_cache.FetchNext();
- } while (current_match != NULL);
+ // Continue from where the match ended, unless it was an empty match.
+ int next = end;
+ if (start == end) {
+ next = end + 1;
+ if (next > length) break;
+ }
- if (global_cache.HasException()) return Failure::Exception();
+ match = RegExpImpl::Exec(regexp_handle,
+ subject_handle,
+ next,
+ last_match_info_handle);
+ if (match.is_null()) {
+ return Failure::Exception();
+ }
+ matched = !match->IsNull();
+ } while (matched);
- if (prev < subject_length) {
- builder.EnsureCapacity(2);
- builder.AddSubjectSlice(prev, subject_length);
+ if (prev < length) {
+ builder.AddSubjectSlice(prev, length);
}
- RegExpImpl::SetLastMatchInfo(last_match_info,
- subject,
- capture_count,
- global_cache.LastSuccessfulMatch());
-
return *(builder.ToString());
}
template <typename ResultSeqString>
MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithEmptyString(
Isolate* isolate,
- Handle<String> subject,
- Handle<JSRegExp> regexp,
- Handle<JSArray> last_match_info) {
+ String* subject,
+ JSRegExp* regexp,
+ JSArray* last_match_info,
+ Zone* zone) {
ASSERT(subject->IsFlat());
- bool is_global = regexp->GetFlags().is_global();
+ HandleScope handles(isolate);
+
+ Handle<String> subject_handle(subject);
+ Handle<JSRegExp> regexp_handle(regexp);
+ Handle<JSArray> last_match_info_handle(last_match_info);
// Shortcut for simple non-regexp global replacements
- if (is_global &&
- regexp->TypeTag() == JSRegExp::ATOM) {
- Handle<String> empty_string(HEAP->empty_string());
- if (subject->HasOnlyAsciiChars()) {
+ if (regexp_handle->GetFlags().is_global() &&
+ regexp_handle->TypeTag() == JSRegExp::ATOM) {
+ Handle<String> empty_string_handle(HEAP->empty_string());
+ if (subject_handle->HasOnlyAsciiChars()) {
return StringReplaceAtomRegExpWithString<SeqAsciiString>(
isolate,
- subject,
- regexp,
- empty_string,
- last_match_info);
+ subject_handle,
+ regexp_handle,
+ empty_string_handle,
+ last_match_info_handle,
+ zone);
} else {
return StringReplaceAtomRegExpWithString<SeqTwoByteString>(
isolate,
- subject,
- regexp,
- empty_string,
- last_match_info);
+ subject_handle,
+ regexp_handle,
+ empty_string_handle,
+ last_match_info_handle,
+ zone);
}
}
- RegExpImpl::GlobalCache global_cache(regexp, subject, is_global, isolate);
- if (global_cache.HasException()) return Failure::Exception();
+ Handle<Object> match = RegExpImpl::Exec(regexp_handle,
+ subject_handle,
+ 0,
+ last_match_info_handle);
+ if (match.is_null()) return Failure::Exception();
+ if (match->IsNull()) return *subject_handle;
+
+ ASSERT(last_match_info_handle->HasFastObjectElements());
- int32_t* current_match = global_cache.FetchNext();
- if (current_match == NULL) {
- if (global_cache.HasException()) return Failure::Exception();
- return *subject;
+ int start, end;
+ {
+ AssertNoAllocation match_info_array_is_not_in_a_handle;
+ FixedArray* match_info_array =
+ FixedArray::cast(last_match_info_handle->elements());
+
+ start = RegExpImpl::GetCapture(match_info_array, 0);
+ end = RegExpImpl::GetCapture(match_info_array, 1);
}
- int start = current_match[0];
- int end = current_match[1];
- int capture_count = regexp->CaptureCount();
- int subject_length = subject->length();
+ bool global = regexp_handle->GetFlags().is_global();
- int new_length = subject_length - (end - start);
- if (new_length == 0) return isolate->heap()->empty_string();
+ if (start == end && !global) return *subject_handle;
+ int length = subject_handle->length();
+ int new_length = length - (end - start);
+ if (new_length == 0) {
+ return isolate->heap()->empty_string();
+ }
Handle<ResultSeqString> answer;
if (ResultSeqString::kHasAsciiEncoding) {
answer = Handle<ResultSeqString>::cast(
isolate->factory()->NewRawTwoByteString(new_length));
}
- if (!is_global) {
- RegExpImpl::SetLastMatchInfo(
- last_match_info, subject, capture_count, current_match);
- if (start == end) {
- return *subject;
- } else {
- if (start > 0) {
- String::WriteToFlat(*subject, answer->GetChars(), 0, start);
- }
- if (end < subject_length) {
- String::WriteToFlat(
- *subject, answer->GetChars() + start, end, subject_length);
- }
- return *answer;
+ // If the regexp isn't global, only match once.
+ if (!global) {
+ if (start > 0) {
+ String::WriteToFlat(*subject_handle,
+ answer->GetChars(),
+ 0,
+ start);
}
+ if (end < length) {
+ String::WriteToFlat(*subject_handle,
+ answer->GetChars() + start,
+ end,
+ length);
+ }
+ return *answer;
}
- int prev = 0;
+ int prev = 0; // Index of end of last match.
+ int next = 0; // Start of next search (prev unless last match was empty).
int position = 0;
do {
- start = current_match[0];
- end = current_match[1];
if (prev < start) {
// Add substring subject[prev;start] to answer string.
- String::WriteToFlat(
- *subject, answer->GetChars() + position, prev, start);
+ String::WriteToFlat(*subject_handle,
+ answer->GetChars() + position,
+ prev,
+ start);
position += start - prev;
}
prev = end;
+ next = end;
+ // Continue from where the match ended, unless it was an empty match.
+ if (start == end) {
+ next++;
+ if (next > length) break;
+ }
+ match = RegExpImpl::Exec(regexp_handle,
+ subject_handle,
+ next,
+ last_match_info_handle);
+ if (match.is_null()) return Failure::Exception();
+ if (match->IsNull()) break;
+
+ ASSERT(last_match_info_handle->HasFastObjectElements());
+ HandleScope loop_scope(isolate);
+ {
+ AssertNoAllocation match_info_array_is_not_in_a_handle;
+ FixedArray* match_info_array =
+ FixedArray::cast(last_match_info_handle->elements());
+ start = RegExpImpl::GetCapture(match_info_array, 0);
+ end = RegExpImpl::GetCapture(match_info_array, 1);
+ }
+ } while (true);
- current_match = global_cache.FetchNext();
- } while (current_match != NULL);
-
- if (global_cache.HasException()) return Failure::Exception();
-
- RegExpImpl::SetLastMatchInfo(last_match_info,
- subject,
- capture_count,
- global_cache.LastSuccessfulMatch());
-
- if (prev < subject_length) {
+ if (prev < length) {
// Add substring subject[prev;length] to answer string.
- String::WriteToFlat(
- *subject, answer->GetChars() + position, prev, subject_length);
- position += subject_length - prev;
+ String::WriteToFlat(*subject_handle,
+ answer->GetChars() + position,
+ prev,
+ length);
+ position += length - prev;
}
- if (position == 0) return isolate->heap()->empty_string();
+ if (position == 0) {
+ return isolate->heap()->empty_string();
+ }
// Shorten string and fill
int string_size = ResultSeqString::SizeFor(position);
RUNTIME_FUNCTION(MaybeObject*, Runtime_StringReplaceRegExpWithString) {
ASSERT(args.length() == 4);
- HandleScope scope(isolate);
-
- CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
- CONVERT_ARG_HANDLE_CHECKED(String, replacement, 2);
- CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1);
- CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3);
+ CONVERT_ARG_CHECKED(String, subject, 0);
+ if (!subject->IsFlat()) {
+ Object* flat_subject;
+ { MaybeObject* maybe_flat_subject = subject->TryFlatten();
+ if (!maybe_flat_subject->ToObject(&flat_subject)) {
+ return maybe_flat_subject;
+ }
+ }
+ subject = String::cast(flat_subject);
+ }
- if (!subject->IsFlat()) subject = FlattenGetString(subject);
+ CONVERT_ARG_CHECKED(String, replacement, 2);
+ if (!replacement->IsFlat()) {
+ Object* flat_replacement;
+ { MaybeObject* maybe_flat_replacement = replacement->TryFlatten();
+ if (!maybe_flat_replacement->ToObject(&flat_replacement)) {
+ return maybe_flat_replacement;
+ }
+ }
+ replacement = String::cast(flat_replacement);
+ }
- if (!replacement->IsFlat()) replacement = FlattenGetString(replacement);
+ CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
+ CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ASSERT(last_match_info->HasFastObjectElements());
+ Zone* zone = isolate->runtime_zone();
if (replacement->length() == 0) {
if (subject->HasOnlyAsciiChars()) {
return StringReplaceRegExpWithEmptyString<SeqAsciiString>(
- isolate, subject, regexp, last_match_info);
+ isolate, subject, regexp, last_match_info, zone);
} else {
return StringReplaceRegExpWithEmptyString<SeqTwoByteString>(
- isolate, subject, regexp, last_match_info);
+ isolate, subject, regexp, last_match_info, zone);
}
}
- return StringReplaceRegExpWithString(
- isolate, subject, regexp, replacement, last_match_info);
+ return StringReplaceRegExpWithString(isolate,
+ subject,
+ regexp,
+ replacement,
+ last_match_info,
+ zone);
}
CONVERT_ARG_HANDLE_CHECKED(JSArray, regexp_info, 2);
HandleScope handles;
- RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
- if (global_cache.HasException()) return Failure::Exception();
-
- int capture_count = regexp->CaptureCount();
+ Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
- Zone* zone = isolate->runtime_zone();
- ZoneScope zone_space(zone, DELETE_ON_EXIT);
- ZoneList<int> offsets(8, zone);
-
- while (true) {
- int32_t* match = global_cache.FetchNext();
- if (match == NULL) break;
- offsets.Add(match[0], zone); // start
- offsets.Add(match[1], zone); // end
+ if (match.is_null()) {
+ return Failure::Exception();
}
-
- if (global_cache.HasException()) return Failure::Exception();
-
- if (offsets.length() == 0) {
- // Not a single match.
+ if (match->IsNull()) {
return isolate->heap()->null_value();
}
+ int length = subject->length();
- RegExpImpl::SetLastMatchInfo(regexp_info,
- subject,
- capture_count,
- global_cache.LastSuccessfulMatch());
-
+ Zone* zone = isolate->runtime_zone();
+ ZoneScope zone_space(zone, DELETE_ON_EXIT);
+ ZoneList<int> offsets(8, zone);
+ int start;
+ int end;
+ do {
+ {
+ AssertNoAllocation no_alloc;
+ FixedArray* elements = FixedArray::cast(regexp_info->elements());
+ start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
+ end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
+ }
+ offsets.Add(start, zone);
+ offsets.Add(end, zone);
+ if (start == end) if (++end > length) break;
+ match = RegExpImpl::Exec(regexp, subject, end, regexp_info);
+ if (match.is_null()) {
+ return Failure::Exception();
+ }
+ } while (!match->IsNull());
int matches = offsets.length() / 2;
Handle<FixedArray> elements = isolate->factory()->NewFixedArray(matches);
- Handle<String> substring =
- isolate->factory()->NewSubString(subject, offsets.at(0), offsets.at(1));
+ Handle<String> substring = isolate->factory()->
+ NewSubString(subject, offsets.at(0), offsets.at(1));
elements->set(0, *substring);
- for (int i = 1; i < matches; i++) {
- HandleScope temp_scope(isolate);
+ for (int i = 1; i < matches ; i++) {
int from = offsets.at(i * 2);
int to = offsets.at(i * 2 + 1);
- Handle<String> substring =
- isolate->factory()->NewProperSubString(subject, from, to);
+ Handle<String> substring = isolate->factory()->
+ NewProperSubString(subject, from, to);
elements->set(i, *substring);
}
Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(elements);
}
-// Only called from Runtime_RegExpExecMultiple so it doesn't need to maintain
-// separate last match info. See comment on that function.
-template<bool has_capture>
-static int SearchRegExpMultiple(
+static bool SearchStringMultiple(Isolate* isolate,
+ Handle<String> subject,
+ Handle<String> pattern,
+ Handle<JSArray> last_match_info,
+ FixedArrayBuilder* builder) {
+ ASSERT(subject->IsFlat());
+ ASSERT(pattern->IsFlat());
+
+ // Treating as if a previous match was before first character.
+ int match_pos = -pattern->length();
+
+ for (;;) { // Break when search complete.
+ builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
+ AssertNoAllocation no_gc;
+ String::FlatContent subject_content = subject->GetFlatContent();
+ String::FlatContent pattern_content = pattern->GetFlatContent();
+ if (subject_content.IsAscii()) {
+ Vector<const char> subject_vector = subject_content.ToAsciiVector();
+ if (pattern_content.IsAscii()) {
+ if (SearchStringMultiple(isolate,
+ subject_vector,
+ pattern_content.ToAsciiVector(),
+ *pattern,
+ builder,
+ &match_pos)) break;
+ } else {
+ if (SearchStringMultiple(isolate,
+ subject_vector,
+ pattern_content.ToUC16Vector(),
+ *pattern,
+ builder,
+ &match_pos)) break;
+ }
+ } else {
+ Vector<const uc16> subject_vector = subject_content.ToUC16Vector();
+ if (pattern_content.IsAscii()) {
+ if (SearchStringMultiple(isolate,
+ subject_vector,
+ pattern_content.ToAsciiVector(),
+ *pattern,
+ builder,
+ &match_pos)) break;
+ } else {
+ if (SearchStringMultiple(isolate,
+ subject_vector,
+ pattern_content.ToUC16Vector(),
+ *pattern,
+ builder,
+ &match_pos)) break;
+ }
+ }
+ }
+
+ if (match_pos >= 0) {
+ SetLastMatchInfoNoCaptures(subject,
+ last_match_info,
+ match_pos,
+ match_pos + pattern->length());
+ return true;
+ }
+ return false; // No matches at all.
+}
+
+
+static int SearchRegExpNoCaptureMultiple(
Isolate* isolate,
Handle<String> subject,
Handle<JSRegExp> regexp,
Handle<JSArray> last_match_array,
FixedArrayBuilder* builder) {
ASSERT(subject->IsFlat());
- ASSERT_NE(has_capture, regexp->CaptureCount() == 0);
-
- RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate);
- if (global_cache.HasException()) return RegExpImpl::RE_EXCEPTION;
-
- int capture_count = regexp->CaptureCount();
- int subject_length = subject->length();
-
- // Position to search from.
+ ASSERT(regexp->CaptureCount() == 0);
int match_start = -1;
int match_end = 0;
+ int pos = 0;
+ int registers_per_match = RegExpImpl::IrregexpPrepare(regexp, subject);
+ if (registers_per_match < 0) return RegExpImpl::RE_EXCEPTION;
+
+ int max_matches;
+ int num_registers = RegExpImpl::GlobalOffsetsVectorSize(regexp,
+ registers_per_match,
+ &max_matches);
+ OffsetsVector registers(num_registers, isolate);
+ Vector<int32_t> register_vector(registers.vector(), registers.length());
+ int subject_length = subject->length();
bool first = true;
-
- // Two smis before and after the match, for very long strings.
- static const int kMaxBuilderEntriesPerRegExpMatch = 5;
-
- while (true) {
- int32_t* current_match = global_cache.FetchNext();
- if (current_match == NULL) break;
- match_start = current_match[0];
- builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
- if (match_end < match_start) {
- ReplacementStringBuilder::AddSubjectSlice(builder,
- match_end,
- match_start);
- }
- match_end = current_match[1];
- {
- // Avoid accumulating new handles inside loop.
- HandleScope temp_scope(isolate);
- Handle<String> match;
- if (!first) {
- match = isolate->factory()->NewProperSubString(subject,
- match_start,
- match_end);
- } else {
- match = isolate->factory()->NewSubString(subject,
- match_start,
- match_end);
- first = false;
+ for (;;) { // Break on failure, return on exception.
+ int num_matches = RegExpImpl::IrregexpExecRaw(regexp,
+ subject,
+ pos,
+ register_vector);
+ if (num_matches > 0) {
+ for (int match_index = 0; match_index < num_matches; match_index++) {
+ int32_t* current_match = ®ister_vector[match_index * 2];
+ match_start = current_match[0];
+ builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
+ if (match_end < match_start) {
+ ReplacementStringBuilder::AddSubjectSlice(builder,
+ match_end,
+ match_start);
+ }
+ match_end = current_match[1];
+ HandleScope loop_scope(isolate);
+ if (!first) {
+ builder->Add(*isolate->factory()->NewProperSubString(subject,
+ match_start,
+ match_end));
+ } else {
+ builder->Add(*isolate->factory()->NewSubString(subject,
+ match_start,
+ match_end));
+ first = false;
+ }
}
- if (has_capture) {
- // Arguments array to replace function is match, captures, index and
- // subject, i.e., 3 + capture count in total.
- Handle<FixedArray> elements =
- isolate->factory()->NewFixedArray(3 + capture_count);
-
- elements->set(0, *match);
- for (int i = 1; i <= capture_count; i++) {
- int start = current_match[i * 2];
- if (start >= 0) {
- int end = current_match[i * 2 + 1];
- ASSERT(start <= end);
- Handle<String> substring =
- isolate->factory()->NewSubString(subject, start, end);
- elements->set(i, *substring);
- } else {
- ASSERT(current_match[i * 2 + 1] < 0);
- elements->set(i, isolate->heap()->undefined_value());
- }
- }
- elements->set(capture_count + 1, Smi::FromInt(match_start));
- elements->set(capture_count + 2, *subject);
- builder->Add(*isolate->factory()->NewJSArrayWithElements(elements));
+ // If we did not get the maximum number of matches, we can stop here
+ // since there are no matches left.
+ if (num_matches < max_matches) break;
+
+ if (match_start != match_end) {
+ pos = match_end;
} else {
- builder->Add(*match);
+ pos = match_end + 1;
+ if (pos > subject_length) break;
}
+ } else if (num_matches == 0) {
+ break;
+ } else {
+ ASSERT_EQ(num_matches, RegExpImpl::RE_EXCEPTION);
+ return RegExpImpl::RE_EXCEPTION;
}
}
- if (global_cache.HasException()) return RegExpImpl::RE_EXCEPTION;
-
if (match_start >= 0) {
- // Finished matching, with at least one match.
if (match_end < subject_length) {
ReplacementStringBuilder::AddSubjectSlice(builder,
match_end,
subject_length);
}
-
- RegExpImpl::SetLastMatchInfo(
- last_match_array, subject, capture_count, NULL);
-
+ SetLastMatchInfoNoCaptures(subject,
+ last_match_array,
+ match_start,
+ match_end);
return RegExpImpl::RE_SUCCESS;
} else {
return RegExpImpl::RE_FAILURE; // No matches at all.
}
+// Only called from Runtime_RegExpExecMultiple so it doesn't need to maintain
+// separate last match info. See comment on that function.
+static int SearchRegExpMultiple(
+ Isolate* isolate,
+ Handle<String> subject,
+ Handle<JSRegExp> regexp,
+ Handle<JSArray> last_match_array,
+ FixedArrayBuilder* builder,
+ Zone* zone) {
+
+ ASSERT(subject->IsFlat());
+ int registers_per_match = RegExpImpl::IrregexpPrepare(regexp, subject);
+ if (registers_per_match < 0) return RegExpImpl::RE_EXCEPTION;
+
+ int max_matches;
+ int num_registers = RegExpImpl::GlobalOffsetsVectorSize(regexp,
+ registers_per_match,
+ &max_matches);
+ OffsetsVector registers(num_registers, isolate);
+ Vector<int32_t> register_vector(registers.vector(), registers.length());
+
+ int num_matches = RegExpImpl::IrregexpExecRaw(regexp,
+ subject,
+ 0,
+ register_vector);
+
+ int capture_count = regexp->CaptureCount();
+ int subject_length = subject->length();
+
+ // Position to search from.
+ int pos = 0;
+ // End of previous match. Differs from pos if match was empty.
+ int match_end = 0;
+ bool first = true;
+
+ if (num_matches > 0) {
+ do {
+ int match_start = 0;
+ for (int match_index = 0; match_index < num_matches; match_index++) {
+ int32_t* current_match =
+ ®ister_vector[match_index * registers_per_match];
+ match_start = current_match[0];
+ builder->EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch);
+ if (match_end < match_start) {
+ ReplacementStringBuilder::AddSubjectSlice(builder,
+ match_end,
+ match_start);
+ }
+ match_end = current_match[1];
+
+ {
+ // Avoid accumulating new handles inside loop.
+ HandleScope temp_scope(isolate);
+ // Arguments array to replace function is match, captures, index and
+ // subject, i.e., 3 + capture count in total.
+ Handle<FixedArray> elements =
+ isolate->factory()->NewFixedArray(3 + capture_count);
+ Handle<String> match;
+ if (!first) {
+ match = isolate->factory()->NewProperSubString(subject,
+ match_start,
+ match_end);
+ } else {
+ match = isolate->factory()->NewSubString(subject,
+ match_start,
+ match_end);
+ }
+ elements->set(0, *match);
+ for (int i = 1; i <= capture_count; i++) {
+ int start = current_match[i * 2];
+ if (start >= 0) {
+ int end = current_match[i * 2 + 1];
+ ASSERT(start <= end);
+ Handle<String> substring;
+ if (!first) {
+ substring =
+ isolate->factory()->NewProperSubString(subject, start, end);
+ } else {
+ substring =
+ isolate->factory()->NewSubString(subject, start, end);
+ }
+ elements->set(i, *substring);
+ } else {
+ ASSERT(current_match[i * 2 + 1] < 0);
+ elements->set(i, isolate->heap()->undefined_value());
+ }
+ }
+ elements->set(capture_count + 1, Smi::FromInt(match_start));
+ elements->set(capture_count + 2, *subject);
+ builder->Add(*isolate->factory()->NewJSArrayWithElements(elements));
+ }
+ first = false;
+ }
+
+ // If we did not get the maximum number of matches, we can stop here
+ // since there are no matches left.
+ if (num_matches < max_matches) break;
+
+ if (match_end > match_start) {
+ pos = match_end;
+ } else {
+ pos = match_end + 1;
+ if (pos > subject_length) {
+ break;
+ }
+ }
+
+ num_matches = RegExpImpl::IrregexpExecRaw(regexp,
+ subject,
+ pos,
+ register_vector);
+ } while (num_matches > 0);
+
+ if (num_matches != RegExpImpl::RE_EXCEPTION) {
+ // Finished matching, with at least one match.
+ if (match_end < subject_length) {
+ ReplacementStringBuilder::AddSubjectSlice(builder,
+ match_end,
+ subject_length);
+ }
+
+ int last_match_capture_count = (capture_count + 1) * 2;
+ int last_match_array_size =
+ last_match_capture_count + RegExpImpl::kLastMatchOverhead;
+ last_match_array->EnsureSize(last_match_array_size);
+ AssertNoAllocation no_gc;
+ FixedArray* elements = FixedArray::cast(last_match_array->elements());
+ // We have to set this even though the rest of the last match array is
+ // ignored.
+ RegExpImpl::SetLastCaptureCount(elements, last_match_capture_count);
+ // These are also read without consulting the override.
+ RegExpImpl::SetLastSubject(elements, *subject);
+ RegExpImpl::SetLastInput(elements, *subject);
+ return RegExpImpl::RE_SUCCESS;
+ }
+ }
+ // No matches at all, return failure or exception result directly.
+ return num_matches;
+}
+
+
// This is only called for StringReplaceGlobalRegExpWithFunction. This sets
// lastMatchInfoOverride to maintain the last match info, so we don't need to
// set any other last match array info.
}
FixedArrayBuilder builder(result_elements);
+ if (regexp->TypeTag() == JSRegExp::ATOM) {
+ Handle<String> pattern(
+ String::cast(regexp->DataAt(JSRegExp::kAtomPatternIndex)));
+ ASSERT(pattern->IsFlat());
+ if (SearchStringMultiple(isolate, subject, pattern,
+ last_match_info, &builder)) {
+ return *builder.ToJSArray(result_array);
+ }
+ return isolate->heap()->null_value();
+ }
+
+ ASSERT_EQ(regexp->TypeTag(), JSRegExp::IRREGEXP);
+
int result;
if (regexp->CaptureCount() == 0) {
- result = SearchRegExpMultiple<false>(
- isolate, subject, regexp, last_match_info, &builder);
+ result = SearchRegExpNoCaptureMultiple(isolate,
+ subject,
+ regexp,
+ last_match_info,
+ &builder);
} else {
- result = SearchRegExpMultiple<true>(
- isolate, subject, regexp, last_match_info, &builder);
+ result = SearchRegExpMultiple(isolate,
+ subject,
+ regexp,
+ last_match_info,
+ &builder,
+ isolate->runtime_zone());
}
-
if (result == RegExpImpl::RE_SUCCESS) return *builder.ToJSArray(result_array);
if (result == RegExpImpl::RE_FAILURE) return isolate->heap()->null_value();
ASSERT_EQ(result, RegExpImpl::RE_EXCEPTION);