Home | History | Annotate | Download | only in regexp
      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 Handle<String> RegExpUtils::GenericCaptureGetter(
     17     Isolate* isolate, Handle<RegExpMatchInfo> match_info, int capture,
     18     bool* ok) {
     19   const int index = capture * 2;
     20   if (index >= match_info->NumberOfCaptureRegisters()) {
     21     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   if (match_start == -1 || match_end == -1) {
     28     if (ok != nullptr) *ok = false;
     29     return isolate->factory()->empty_string();
     30   }
     31 
     32   if (ok != nullptr) *ok = true;
     33   Handle<String> last_subject(match_info->LastSubject(), isolate);
     34   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   return recv->map() == isolate->regexp_function()->initial_map();
     41 }
     42 
     43 }  // namespace
     44 
     45 MaybeHandle<Object> RegExpUtils::SetLastIndex(Isolate* isolate,
     46                                               Handle<JSReceiver> recv,
     47                                               uint64_t value) {
     48   Handle<Object> value_as_object =
     49       isolate->factory()->NewNumberFromInt64(value);
     50   if (HasInitialRegExpMap(isolate, recv)) {
     51     JSRegExp::cast(*recv)->set_last_index(*value_as_object, SKIP_WRITE_BARRIER);
     52     return recv;
     53   } else {
     54     return Object::SetProperty(isolate, recv,
     55                                isolate->factory()->lastIndex_string(),
     56                                value_as_object, LanguageMode::kStrict);
     57   }
     58 }
     59 
     60 MaybeHandle<Object> RegExpUtils::GetLastIndex(Isolate* isolate,
     61                                               Handle<JSReceiver> recv) {
     62   if (HasInitialRegExpMap(isolate, recv)) {
     63     return handle(JSRegExp::cast(*recv)->last_index(), isolate);
     64   } else {
     65     return Object::GetProperty(isolate, recv,
     66                                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 MaybeHandle<Object> RegExpUtils::RegExpExec(Isolate* isolate,
     74                                             Handle<JSReceiver> regexp,
     75                                             Handle<String> string,
     76                                             Handle<Object> exec) {
     77   if (exec->IsUndefined(isolate)) {
     78     ASSIGN_RETURN_ON_EXCEPTION(
     79         isolate, exec,
     80         Object::GetProperty(isolate, regexp, isolate->factory()->exec_string()),
     81         Object);
     82   }
     83 
     84   if (exec->IsCallable()) {
     85     const int argc = 1;
     86     ScopedVector<Handle<Object>> argv(argc);
     87     argv[0] = string;
     88 
     89     Handle<Object> result;
     90     ASSIGN_RETURN_ON_EXCEPTION(
     91         isolate, result,
     92         Execution::Call(isolate, exec, regexp, argc, argv.start()), Object);
     93 
     94     if (!result->IsJSReceiver() && !result->IsNull(isolate)) {
     95       THROW_NEW_ERROR(isolate,
     96                       NewTypeError(MessageTemplate::kInvalidRegExpExecResult),
     97                       Object);
     98     }
     99     return result;
    100   }
    101 
    102   if (!regexp->IsJSRegExp()) {
    103     THROW_NEW_ERROR(isolate,
    104                     NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
    105                                  isolate->factory()->NewStringFromAsciiChecked(
    106                                      "RegExp.prototype.exec"),
    107                                  regexp),
    108                     Object);
    109   }
    110 
    111   {
    112     Handle<JSFunction> regexp_exec = isolate->regexp_exec_function();
    113 
    114     const int argc = 1;
    115     ScopedVector<Handle<Object>> argv(argc);
    116     argv[0] = string;
    117 
    118     return Execution::Call(isolate, regexp_exec, regexp, argc, argv.start());
    119   }
    120 }
    121 
    122 Maybe<bool> RegExpUtils::IsRegExp(Isolate* isolate, Handle<Object> object) {
    123   if (!object->IsJSReceiver()) return Just(false);
    124 
    125   Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
    126 
    127   Handle<Object> match;
    128   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    129       isolate, match,
    130       JSObject::GetProperty(isolate, receiver,
    131                             isolate->factory()->match_symbol()),
    132       Nothing<bool>());
    133 
    134   if (!match->IsUndefined(isolate)) return Just(match->BooleanValue(isolate));
    135   return Just(object->IsJSRegExp());
    136 }
    137 
    138 bool RegExpUtils::IsUnmodifiedRegExp(Isolate* isolate, Handle<Object> obj) {
    139   // TODO(ishell): Update this check once map changes for constant field
    140   // tracking are landing.
    141 
    142 #ifdef V8_ENABLE_FORCE_SLOW_PATH
    143   if (isolate->force_slow_path()) return false;
    144 #endif
    145 
    146   if (!obj->IsJSReceiver()) return false;
    147 
    148   JSReceiver* recv = JSReceiver::cast(*obj);
    149 
    150   // Check the receiver's map.
    151   Handle<JSFunction> regexp_function = isolate->regexp_function();
    152   if (recv->map() != regexp_function->initial_map()) return false;
    153 
    154   // Check the receiver's prototype's map.
    155   Object* proto = recv->map()->prototype();
    156   if (!proto->IsJSReceiver()) return false;
    157 
    158   Handle<Map> initial_proto_initial_map = isolate->regexp_prototype_map();
    159   if (JSReceiver::cast(proto)->map() != *initial_proto_initial_map) {
    160     return false;
    161   }
    162 
    163   // The smi check is required to omit ToLength(lastIndex) calls with possible
    164   // user-code execution on the fast path.
    165   Object* last_index = JSRegExp::cast(recv)->last_index();
    166   return last_index->IsSmi() && Smi::ToInt(last_index) >= 0;
    167 }
    168 
    169 uint64_t RegExpUtils::AdvanceStringIndex(Handle<String> string, uint64_t index,
    170                                          bool unicode) {
    171   DCHECK_LE(static_cast<double>(index), kMaxSafeInteger);
    172   const uint64_t string_length = static_cast<uint64_t>(string->length());
    173   if (unicode && index < string_length) {
    174     const uint16_t first = string->Get(static_cast<uint32_t>(index));
    175     if (first >= 0xD800 && first <= 0xDBFF && index + 1 < string_length) {
    176       DCHECK_LT(index, std::numeric_limits<uint64_t>::max());
    177       const uint16_t second = string->Get(static_cast<uint32_t>(index + 1));
    178       if (second >= 0xDC00 && second <= 0xDFFF) {
    179         return index + 2;
    180       }
    181     }
    182   }
    183 
    184   return index + 1;
    185 }
    186 
    187 MaybeHandle<Object> RegExpUtils::SetAdvancedStringIndex(
    188     Isolate* isolate, Handle<JSReceiver> regexp, Handle<String> string,
    189     bool unicode) {
    190   Handle<Object> last_index_obj;
    191   ASSIGN_RETURN_ON_EXCEPTION(
    192       isolate, last_index_obj,
    193       Object::GetProperty(isolate, regexp,
    194                           isolate->factory()->lastIndex_string()),
    195       Object);
    196 
    197   ASSIGN_RETURN_ON_EXCEPTION(isolate, last_index_obj,
    198                              Object::ToLength(isolate, last_index_obj), Object);
    199   const uint64_t last_index = PositiveNumberToUint64(*last_index_obj);
    200   const uint64_t new_last_index =
    201       AdvanceStringIndex(string, last_index, unicode);
    202 
    203   return SetLastIndex(isolate, regexp, new_last_index);
    204 }
    205 
    206 }  // namespace internal
    207 }  // namespace v8
    208