Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/regexp/regexp-utils.h"
6 :
7 : #include "src/heap/factory.h"
8 : #include "src/isolate.h"
9 : #include "src/objects-inl.h"
10 : #include "src/objects/js-regexp-inl.h"
11 : #include "src/regexp/jsregexp.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 251493 : Handle<String> RegExpUtils::GenericCaptureGetter(
17 : Isolate* isolate, Handle<RegExpMatchInfo> match_info, int capture,
18 : bool* ok) {
19 251493 : const int index = capture * 2;
20 251493 : if (index >= match_info->NumberOfCaptureRegisters()) {
21 1849 : if (ok != nullptr) *ok = false;
22 : return isolate->factory()->empty_string();
23 : }
24 :
25 : const int match_start = match_info->Capture(index);
26 : const int match_end = match_info->Capture(index + 1);
27 249644 : if (match_start == -1 || match_end == -1) {
28 207 : if (ok != nullptr) *ok = false;
29 : return isolate->factory()->empty_string();
30 : }
31 :
32 249437 : if (ok != nullptr) *ok = true;
33 : Handle<String> last_subject(match_info->LastSubject(), isolate);
34 249437 : return isolate->factory()->NewSubString(last_subject, match_start, match_end);
35 : }
36 :
37 : namespace {
38 :
39 : V8_INLINE bool HasInitialRegExpMap(Isolate* isolate, Handle<JSReceiver> recv) {
40 8254 : return recv->map() == isolate->regexp_function()->initial_map();
41 : }
42 :
43 : } // namespace
44 :
45 3651 : MaybeHandle<Object> RegExpUtils::SetLastIndex(Isolate* isolate,
46 : Handle<JSReceiver> recv,
47 : uint64_t value) {
48 : Handle<Object> value_as_object =
49 3651 : isolate->factory()->NewNumberFromInt64(value);
50 3651 : if (HasInitialRegExpMap(isolate, recv)) {
51 : JSRegExp::cast(*recv)->set_last_index(*value_as_object, SKIP_WRITE_BARRIER);
52 3237 : return recv;
53 : } else {
54 : return Object::SetProperty(
55 : isolate, recv, isolate->factory()->lastIndex_string(), value_as_object,
56 414 : StoreOrigin::kMaybeKeyed, Just(kThrowOnError));
57 : }
58 : }
59 :
60 476 : MaybeHandle<Object> RegExpUtils::GetLastIndex(Isolate* isolate,
61 : Handle<JSReceiver> recv) {
62 476 : if (HasInitialRegExpMap(isolate, recv)) {
63 458 : return handle(JSRegExp::cast(*recv)->last_index(), isolate);
64 : } else {
65 : return Object::GetProperty(isolate, recv,
66 18 : isolate->factory()->lastIndex_string());
67 : }
68 : }
69 :
70 : // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
71 : // Also takes an optional exec method in case our caller
72 : // has already fetched exec.
73 1878 : MaybeHandle<Object> RegExpUtils::RegExpExec(Isolate* isolate,
74 : Handle<JSReceiver> regexp,
75 : Handle<String> string,
76 : Handle<Object> exec) {
77 1878 : if (exec->IsUndefined(isolate)) {
78 3756 : ASSIGN_RETURN_ON_EXCEPTION(
79 : isolate, exec,
80 : Object::GetProperty(isolate, regexp, isolate->factory()->exec_string()),
81 : Object);
82 : }
83 :
84 1878 : if (exec->IsCallable()) {
85 : const int argc = 1;
86 : ScopedVector<Handle<Object>> argv(argc);
87 1842 : argv[0] = string;
88 :
89 : Handle<Object> result;
90 3684 : ASSIGN_RETURN_ON_EXCEPTION(
91 : isolate, result,
92 : Execution::Call(isolate, exec, regexp, argc, argv.start()), Object);
93 :
94 2677 : if (!result->IsJSReceiver() && !result->IsNull(isolate)) {
95 0 : THROW_NEW_ERROR(isolate,
96 : NewTypeError(MessageTemplate::kInvalidRegExpExecResult),
97 : Object);
98 : }
99 1833 : return result;
100 : }
101 :
102 36 : if (!regexp->IsJSRegExp()) {
103 0 : THROW_NEW_ERROR(isolate,
104 : NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
105 : isolate->factory()->NewStringFromAsciiChecked(
106 : "RegExp.prototype.exec"),
107 : regexp),
108 : Object);
109 : }
110 :
111 : {
112 36 : Handle<JSFunction> regexp_exec = isolate->regexp_exec_function();
113 :
114 : const int argc = 1;
115 : ScopedVector<Handle<Object>> argv(argc);
116 36 : argv[0] = string;
117 :
118 36 : return Execution::Call(isolate, regexp_exec, regexp, argc, argv.start());
119 : }
120 : }
121 :
122 405 : Maybe<bool> RegExpUtils::IsRegExp(Isolate* isolate, Handle<Object> object) {
123 405 : if (!object->IsJSReceiver()) return Just(false);
124 :
125 162 : Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
126 :
127 : Handle<Object> match;
128 324 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
129 : isolate, match,
130 : JSObject::GetProperty(isolate, receiver,
131 : isolate->factory()->match_symbol()),
132 : Nothing<bool>());
133 :
134 162 : if (!match->IsUndefined(isolate)) {
135 81 : const bool match_as_boolean = match->BooleanValue(isolate);
136 :
137 153 : if (match_as_boolean && !object->IsJSRegExp()) {
138 0 : isolate->CountUsage(v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp);
139 90 : } else if (!match_as_boolean && object->IsJSRegExp()) {
140 9 : isolate->CountUsage(v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp);
141 : }
142 :
143 : return Just(match_as_boolean);
144 : }
145 :
146 : return Just(object->IsJSRegExp());
147 : }
148 :
149 6171 : bool RegExpUtils::IsUnmodifiedRegExp(Isolate* isolate, Handle<Object> obj) {
150 : #ifdef V8_ENABLE_FORCE_SLOW_PATH
151 : if (isolate->force_slow_path()) return false;
152 : #endif
153 :
154 6171 : if (!obj->IsJSReceiver()) return false;
155 :
156 : JSReceiver recv = JSReceiver::cast(*obj);
157 :
158 : // Check the receiver's map.
159 6171 : Handle<JSFunction> regexp_function = isolate->regexp_function();
160 6171 : if (recv->map() != regexp_function->initial_map()) return false;
161 :
162 : // Check the receiver's prototype's map.
163 : Object proto = recv->map()->prototype();
164 5784 : if (!proto->IsJSReceiver()) return false;
165 :
166 5784 : Handle<Map> initial_proto_initial_map = isolate->regexp_prototype_map();
167 : Map proto_map = JSReceiver::cast(proto)->map();
168 5784 : if (proto_map != *initial_proto_initial_map) {
169 : return false;
170 : }
171 :
172 : // Check that the "exec" method is unmodified.
173 : if (FLAG_track_constant_fields) {
174 : // Check that the index refers to "exec" method (this has to be consistent
175 : // with the init order in the bootstrapper).
176 : DCHECK_EQ(*(isolate->factory()->exec_string()),
177 : proto_map->instance_descriptors()->GetKey(
178 : JSRegExp::kExecFunctionDescriptorIndex));
179 5701 : if (proto_map->instance_descriptors()
180 : ->GetDetails(JSRegExp::kExecFunctionDescriptorIndex)
181 : .constness() != PropertyConstness::kConst) {
182 : return false;
183 : }
184 : }
185 :
186 5685 : if (!isolate->IsRegExpSpeciesLookupChainIntact()) return false;
187 :
188 : // The smi check is required to omit ToLength(lastIndex) calls with possible
189 : // user-code execution on the fast path.
190 : Object last_index = JSRegExp::cast(recv)->last_index();
191 11343 : return last_index->IsSmi() && Smi::ToInt(last_index) >= 0;
192 : }
193 :
194 646 : uint64_t RegExpUtils::AdvanceStringIndex(Handle<String> string, uint64_t index,
195 : bool unicode) {
196 : DCHECK_LE(static_cast<double>(index), kMaxSafeInteger);
197 646 : const uint64_t string_length = static_cast<uint64_t>(string->length());
198 646 : if (unicode && index < string_length) {
199 18 : const uint16_t first = string->Get(static_cast<uint32_t>(index));
200 18 : if (first >= 0xD800 && first <= 0xDBFF && index + 1 < string_length) {
201 : DCHECK_LT(index, std::numeric_limits<uint64_t>::max());
202 0 : const uint16_t second = string->Get(static_cast<uint32_t>(index + 1));
203 0 : if (second >= 0xDC00 && second <= 0xDFFF) {
204 0 : return index + 2;
205 : }
206 : }
207 : }
208 :
209 646 : return index + 1;
210 : }
211 :
212 90 : MaybeHandle<Object> RegExpUtils::SetAdvancedStringIndex(
213 : Isolate* isolate, Handle<JSReceiver> regexp, Handle<String> string,
214 : bool unicode) {
215 : Handle<Object> last_index_obj;
216 180 : ASSIGN_RETURN_ON_EXCEPTION(
217 : isolate, last_index_obj,
218 : Object::GetProperty(isolate, regexp,
219 : isolate->factory()->lastIndex_string()),
220 : Object);
221 :
222 180 : ASSIGN_RETURN_ON_EXCEPTION(isolate, last_index_obj,
223 : Object::ToLength(isolate, last_index_obj), Object);
224 : const uint64_t last_index = PositiveNumberToUint64(*last_index_obj);
225 : const uint64_t new_last_index =
226 90 : AdvanceStringIndex(string, last_index, unicode);
227 :
228 90 : return SetLastIndex(isolate, regexp, new_last_index);
229 : }
230 :
231 : } // namespace internal
232 120216 : } // namespace v8
|