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/factory.h"
8 #include "src/isolate.h"
9 #include "src/objects-inl.h"
10 #include "src/regexp/jsregexp.h"
11
12 namespace v8 {
13 namespace internal {
14
GenericCaptureGetter(Isolate * isolate,Handle<RegExpMatchInfo> match_info,int capture,bool * ok)15 Handle<String> RegExpUtils::GenericCaptureGetter(
16 Isolate* isolate, Handle<RegExpMatchInfo> match_info, int capture,
17 bool* ok) {
18 const int index = capture * 2;
19 if (index >= match_info->NumberOfCaptureRegisters()) {
20 if (ok != nullptr) *ok = false;
21 return isolate->factory()->empty_string();
22 }
23
24 const int match_start = match_info->Capture(index);
25 const int match_end = match_info->Capture(index + 1);
26 if (match_start == -1 || match_end == -1) {
27 if (ok != nullptr) *ok = false;
28 return isolate->factory()->empty_string();
29 }
30
31 if (ok != nullptr) *ok = true;
32 Handle<String> last_subject(match_info->LastSubject());
33 return isolate->factory()->NewSubString(last_subject, match_start, match_end);
34 }
35
36 namespace {
37
HasInitialRegExpMap(Isolate * isolate,Handle<JSReceiver> recv)38 V8_INLINE bool HasInitialRegExpMap(Isolate* isolate, Handle<JSReceiver> recv) {
39 return recv->map() == isolate->regexp_function()->initial_map();
40 }
41
42 } // namespace
43
SetLastIndex(Isolate * isolate,Handle<JSReceiver> recv,int value)44 MaybeHandle<Object> RegExpUtils::SetLastIndex(Isolate* isolate,
45 Handle<JSReceiver> recv,
46 int value) {
47 if (HasInitialRegExpMap(isolate, recv)) {
48 JSRegExp::cast(*recv)->SetLastIndex(value);
49 return recv;
50 } else {
51 return Object::SetProperty(recv, isolate->factory()->lastIndex_string(),
52 handle(Smi::FromInt(value), isolate), STRICT);
53 }
54 }
55
GetLastIndex(Isolate * isolate,Handle<JSReceiver> recv)56 MaybeHandle<Object> RegExpUtils::GetLastIndex(Isolate* isolate,
57 Handle<JSReceiver> recv) {
58 if (HasInitialRegExpMap(isolate, recv)) {
59 return handle(JSRegExp::cast(*recv)->LastIndex(), isolate);
60 } else {
61 return Object::GetProperty(recv, isolate->factory()->lastIndex_string());
62 }
63 }
64
65 // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
66 // Also takes an optional exec method in case our caller
67 // has already fetched exec.
RegExpExec(Isolate * isolate,Handle<JSReceiver> regexp,Handle<String> string,Handle<Object> exec)68 MaybeHandle<Object> RegExpUtils::RegExpExec(Isolate* isolate,
69 Handle<JSReceiver> regexp,
70 Handle<String> string,
71 Handle<Object> exec) {
72 if (exec->IsUndefined(isolate)) {
73 ASSIGN_RETURN_ON_EXCEPTION(
74 isolate, exec,
75 Object::GetProperty(regexp, isolate->factory()->exec_string()), Object);
76 }
77
78 if (exec->IsCallable()) {
79 const int argc = 1;
80 ScopedVector<Handle<Object>> argv(argc);
81 argv[0] = string;
82
83 Handle<Object> result;
84 ASSIGN_RETURN_ON_EXCEPTION(
85 isolate, result,
86 Execution::Call(isolate, exec, regexp, argc, argv.start()), Object);
87
88 if (!result->IsJSReceiver() && !result->IsNull(isolate)) {
89 THROW_NEW_ERROR(isolate,
90 NewTypeError(MessageTemplate::kInvalidRegExpExecResult),
91 Object);
92 }
93 return result;
94 }
95
96 if (!regexp->IsJSRegExp()) {
97 THROW_NEW_ERROR(isolate,
98 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
99 isolate->factory()->NewStringFromAsciiChecked(
100 "RegExp.prototype.exec"),
101 regexp),
102 Object);
103 }
104
105 {
106 Handle<JSFunction> regexp_exec = isolate->regexp_exec_function();
107
108 const int argc = 1;
109 ScopedVector<Handle<Object>> argv(argc);
110 argv[0] = string;
111
112 return Execution::Call(isolate, regexp_exec, regexp, argc, argv.start());
113 }
114 }
115
IsRegExp(Isolate * isolate,Handle<Object> object)116 Maybe<bool> RegExpUtils::IsRegExp(Isolate* isolate, Handle<Object> object) {
117 if (!object->IsJSReceiver()) return Just(false);
118
119 Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
120
121 if (isolate->regexp_function()->initial_map() == receiver->map()) {
122 // Fast-path for unmodified JSRegExp instances.
123 // TODO(ishell): Adapt for new fast-path logic.
124 return Just(true);
125 }
126
127 Handle<Object> match;
128 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
129 isolate, match,
130 JSObject::GetProperty(receiver, isolate->factory()->match_symbol()),
131 Nothing<bool>());
132
133 if (!match->IsUndefined(isolate)) return Just(match->BooleanValue());
134 return Just(object->IsJSRegExp());
135 }
136
IsUnmodifiedRegExp(Isolate * isolate,Handle<Object> obj)137 bool RegExpUtils::IsUnmodifiedRegExp(Isolate* isolate, Handle<Object> obj) {
138 // TODO(ishell): Update this check once map changes for constant field
139 // tracking are landing.
140
141 if (!obj->IsJSReceiver()) return false;
142
143 JSReceiver* recv = JSReceiver::cast(*obj);
144
145 // Check the receiver's map.
146 Handle<JSFunction> regexp_function = isolate->regexp_function();
147 if (recv->map() != regexp_function->initial_map()) return false;
148
149 // Check the receiver's prototype's map.
150 Object* proto = recv->map()->prototype();
151 if (!proto->IsJSReceiver()) return false;
152
153 Handle<Map> initial_proto_initial_map = isolate->regexp_prototype_map();
154 return (JSReceiver::cast(proto)->map() == *initial_proto_initial_map);
155 }
156
AdvanceStringIndex(Isolate * isolate,Handle<String> string,int index,bool unicode)157 int RegExpUtils::AdvanceStringIndex(Isolate* isolate, Handle<String> string,
158 int index, bool unicode) {
159 if (unicode && index < string->length()) {
160 const uint16_t first = string->Get(index);
161 if (first >= 0xD800 && first <= 0xDBFF && string->length() > index + 1) {
162 const uint16_t second = string->Get(index + 1);
163 if (second >= 0xDC00 && second <= 0xDFFF) {
164 return index + 2;
165 }
166 }
167 }
168
169 return index + 1;
170 }
171
SetAdvancedStringIndex(Isolate * isolate,Handle<JSReceiver> regexp,Handle<String> string,bool unicode)172 MaybeHandle<Object> RegExpUtils::SetAdvancedStringIndex(
173 Isolate* isolate, Handle<JSReceiver> regexp, Handle<String> string,
174 bool unicode) {
175 Handle<Object> last_index_obj;
176 ASSIGN_RETURN_ON_EXCEPTION(
177 isolate, last_index_obj,
178 Object::GetProperty(regexp, isolate->factory()->lastIndex_string()),
179 Object);
180
181 ASSIGN_RETURN_ON_EXCEPTION(isolate, last_index_obj,
182 Object::ToLength(isolate, last_index_obj), Object);
183
184 const int last_index = Handle<Smi>::cast(last_index_obj)->value();
185 const int new_last_index =
186 AdvanceStringIndex(isolate, string, last_index, unicode);
187
188 return SetLastIndex(isolate, regexp, new_last_index);
189 }
190
191 } // namespace internal
192 } // namespace v8
193