• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,JSReceiver * recv)38 V8_INLINE bool HasInitialRegExpMap(Isolate* isolate, 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   Handle<Object> match;
122   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
123       isolate, match,
124       JSObject::GetProperty(receiver, isolate->factory()->match_symbol()),
125       Nothing<bool>());
126 
127   if (!match->IsUndefined(isolate)) return Just(match->BooleanValue());
128   return Just(object->IsJSRegExp());
129 }
130 
IsUnmodifiedRegExp(Isolate * isolate,Handle<Object> obj)131 bool RegExpUtils::IsUnmodifiedRegExp(Isolate* isolate, Handle<Object> obj) {
132   // TODO(ishell): Update this check once map changes for constant field
133   // tracking are landing.
134 
135   if (!obj->IsJSReceiver()) return false;
136 
137   JSReceiver* recv = JSReceiver::cast(*obj);
138 
139   if (!HasInitialRegExpMap(isolate, recv)) return false;
140 
141   // Check the receiver's prototype's map.
142   Object* proto = recv->map()->prototype();
143   if (!proto->IsJSReceiver()) return false;
144 
145   Handle<Map> initial_proto_initial_map = isolate->regexp_prototype_map();
146   if (JSReceiver::cast(proto)->map() != *initial_proto_initial_map) {
147     return false;
148   }
149 
150   // The smi check is required to omit ToLength(lastIndex) calls with possible
151   // user-code execution on the fast path.
152   Object* last_index = JSRegExp::cast(recv)->LastIndex();
153   return last_index->IsSmi() && Smi::cast(last_index)->value() >= 0;
154 }
155 
AdvanceStringIndex(Isolate * isolate,Handle<String> string,int index,bool unicode)156 int RegExpUtils::AdvanceStringIndex(Isolate* isolate, Handle<String> string,
157                                     int index, bool unicode) {
158   if (unicode && index < string->length()) {
159     const uint16_t first = string->Get(index);
160     if (first >= 0xD800 && first <= 0xDBFF && string->length() > index + 1) {
161       const uint16_t second = string->Get(index + 1);
162       if (second >= 0xDC00 && second <= 0xDFFF) {
163         return index + 2;
164       }
165     }
166   }
167 
168   return index + 1;
169 }
170 
SetAdvancedStringIndex(Isolate * isolate,Handle<JSReceiver> regexp,Handle<String> string,bool unicode)171 MaybeHandle<Object> RegExpUtils::SetAdvancedStringIndex(
172     Isolate* isolate, Handle<JSReceiver> regexp, Handle<String> string,
173     bool unicode) {
174   Handle<Object> last_index_obj;
175   ASSIGN_RETURN_ON_EXCEPTION(
176       isolate, last_index_obj,
177       Object::GetProperty(regexp, isolate->factory()->lastIndex_string()),
178       Object);
179 
180   ASSIGN_RETURN_ON_EXCEPTION(isolate, last_index_obj,
181                              Object::ToLength(isolate, last_index_obj), Object);
182   const int last_index = PositiveNumberToUint32(*last_index_obj);
183   const int new_last_index =
184       AdvanceStringIndex(isolate, string, last_index, unicode);
185 
186   return SetLastIndex(isolate, regexp, new_last_index);
187 }
188 
189 }  // namespace internal
190 }  // namespace v8
191