summaryrefslogtreecommitdiff
path: root/chromium/v8/src/objects/js-regexp.cc
blob: e800dae5c0f5140524e02e2e3aff4af8b1a051ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/objects/js-regexp.h"

#include "src/objects/js-array-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/regexp/regexp.h"

namespace v8 {
namespace internal {
MaybeHandle<JSArray> JSRegExpResult::GetAndCacheIndices(
    Isolate* isolate, Handle<JSRegExpResult> regexp_result) {
  // Check for cached indices. We do a slow lookup and set of
  // the cached_indices_or_match_info and names fields just in
  // case they have been migrated to dictionaries.
  Handle<Object> indices_or_regexp(
      GetProperty(
          isolate, regexp_result,
          isolate->factory()->regexp_result_cached_indices_or_regexp_symbol())
          .ToHandleChecked());
  if (indices_or_regexp->IsJSRegExp()) {
    // Build and cache indices for next lookup.
    // TODO(joshualitt): Instead of caching the indices, we could call
    // ReconfigureToDataProperty on 'indices' setting its value to this
    // newly created array. However, care would have to be taken to ensure
    // a new map is not created each time.

    // Grab regexp, its last_index, and the original subject string from the
    // result and the re-execute the regexp to generate a new MatchInfo.
    Handle<JSRegExp> regexp(JSRegExp::cast(*indices_or_regexp), isolate);
    Handle<Object> input_object(
        GetProperty(isolate, regexp_result,
                    isolate->factory()->regexp_result_regexp_input_symbol())
            .ToHandleChecked());
    Handle<String> subject(String::cast(*input_object), isolate);
    Handle<Object> last_index_object(
        GetProperty(
            isolate, regexp_result,
            isolate->factory()->regexp_result_regexp_last_index_symbol())
            .ToHandleChecked());

    int capture_count = regexp->CaptureCount();
    Handle<RegExpMatchInfo> match_info =
        RegExpMatchInfo::New(isolate, capture_count);

    int last_index = Smi::ToInt(*last_index_object);
    Handle<Object> result;
    ASSIGN_RETURN_ON_EXCEPTION(
        isolate, result,
        RegExp::Exec(isolate, regexp, subject, last_index, match_info),
        JSArray);
    DCHECK_EQ(*result, *match_info);

    Handle<Object> maybe_names(
        GetProperty(isolate, regexp_result,
                    isolate->factory()->regexp_result_names_symbol())
            .ToHandleChecked());
    indices_or_regexp =
        JSRegExpResultIndices::BuildIndices(isolate, match_info, maybe_names);

    // Cache the result and clear the names array, last_index and subject.
    SetProperty(
        isolate, regexp_result,
        isolate->factory()->regexp_result_cached_indices_or_regexp_symbol(),
        indices_or_regexp)
        .ToHandleChecked();
    SetProperty(isolate, regexp_result,
                isolate->factory()->regexp_result_names_symbol(),
                isolate->factory()->undefined_value())
        .ToHandleChecked();
    SetProperty(isolate, regexp_result,
                isolate->factory()->regexp_result_regexp_last_index_symbol(),
                isolate->factory()->undefined_value())
        .ToHandleChecked();
    SetProperty(isolate, regexp_result,
                isolate->factory()->regexp_result_regexp_input_symbol(),
                isolate->factory()->undefined_value())
        .ToHandleChecked();
  }
  return Handle<JSArray>::cast(indices_or_regexp);
}

Handle<JSRegExpResultIndices> JSRegExpResultIndices::BuildIndices(
    Isolate* isolate, Handle<RegExpMatchInfo> match_info,
    Handle<Object> maybe_names) {
  Handle<JSRegExpResultIndices> indices(Handle<JSRegExpResultIndices>::cast(
      isolate->factory()->NewJSObjectFromMap(
          isolate->regexp_result_indices_map())));

  // Initialize indices length to avoid having a partially initialized object
  // should GC be triggered by creating a NewFixedArray.
  indices->set_length(Smi::zero());

  // Build indices array from RegExpMatchInfo.
  int num_indices = match_info->NumberOfCaptureRegisters();
  int num_results = num_indices >> 1;
  Handle<FixedArray> indices_array =
      isolate->factory()->NewFixedArray(num_results);
  JSArray::SetContent(indices, indices_array);

  for (int i = 0; i < num_results; i++) {
    int base_offset = i * 2;
    int start_offset = match_info->Capture(base_offset);
    int end_offset = match_info->Capture(base_offset + 1);

    // Any unmatched captures are set to undefined, otherwise we set them to a
    // subarray of the indices.
    if (start_offset == -1) {
      indices_array->set(i, ReadOnlyRoots(isolate).undefined_value());
    } else {
      Handle<FixedArray> indices_sub_array(
          isolate->factory()->NewFixedArray(2));
      indices_sub_array->set(0, Smi::FromInt(start_offset));
      indices_sub_array->set(1, Smi::FromInt(end_offset));
      Handle<JSArray> indices_sub_jsarray =
          isolate->factory()->NewJSArrayWithElements(indices_sub_array,
                                                     PACKED_SMI_ELEMENTS, 2);
      indices_array->set(i, *indices_sub_jsarray);
    }
  }

  // If there are no capture groups, set the groups property to undefined.
  FieldIndex groups_index = FieldIndex::ForDescriptor(
      indices->map(), InternalIndex(kGroupsDescriptorIndex));
  if (maybe_names->IsUndefined(isolate)) {
    indices->RawFastPropertyAtPut(groups_index,
                                  ReadOnlyRoots(isolate).undefined_value());
    return indices;
  }

  // Create a groups property which returns a dictionary of named captures to
  // their corresponding capture indices.
  Handle<FixedArray> names(Handle<FixedArray>::cast(maybe_names));
  int num_names = names->length() >> 1;
  Handle<NameDictionary> group_names = NameDictionary::New(isolate, num_names);
  for (int i = 0; i < num_names; i++) {
    int base_offset = i * 2;
    int name_offset = base_offset;
    int index_offset = base_offset + 1;
    Handle<String> name(String::cast(names->get(name_offset)), isolate);
    Handle<Smi> smi_index(Smi::cast(names->get(index_offset)), isolate);
    Handle<Object> capture_indices(indices_array->get(smi_index->value()),
                                   isolate);
    if (!capture_indices->IsUndefined(isolate)) {
      capture_indices = Handle<JSArray>::cast(capture_indices);
    }
    group_names = NameDictionary::Add(
        isolate, group_names, name, capture_indices, PropertyDetails::Empty());
  }

  // Convert group_names to a JSObject and store at the groups property of the
  // result indices.
  Handle<FixedArrayBase> elements = isolate->factory()->empty_fixed_array();
  Handle<HeapObject> null =
      Handle<HeapObject>::cast(isolate->factory()->null_value());
  Handle<JSObject> js_group_names =
      isolate->factory()->NewSlowJSObjectWithPropertiesAndElements(
          null, group_names, elements);
  indices->RawFastPropertyAtPut(groups_index, *js_group_names);
  return indices;
}

uint32_t JSRegExp::BacktrackLimit() const {
  CHECK_EQ(TypeTag(), IRREGEXP);
  return static_cast<uint32_t>(Smi::ToInt(DataAt(kIrregexpBacktrackLimit)));
}

}  // namespace internal
}  // namespace v8