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
12namespace v8 {
13namespace internal {
14
15Handle<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
36namespace {
37
38V8_INLINE bool HasInitialRegExpMap(Isolate* isolate, Handle<JSReceiver> recv) {
39  return recv->map() == isolate->regexp_function()->initial_map();
40}
41
42}  // namespace
43
44MaybeHandle<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
56MaybeHandle<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.
68MaybeHandle<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
116Maybe<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
137bool 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
157int 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
172MaybeHandle<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