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