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/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 
     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 
     38 V8_INLINE bool HasInitialRegExpMap(Isolate* isolate, Handle<JSReceiver> recv) {
     39   return recv->map() == isolate->regexp_function()->initial_map();
     40 }
     41 
     42 }  // namespace
     43 
     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 
     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.
     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 
    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 
    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   // Check the receiver's map.
    140   Handle<JSFunction> regexp_function = isolate->regexp_function();
    141   if (recv->map() != regexp_function->initial_map()) return false;
    142 
    143   // Check the receiver's prototype's map.
    144   Object* proto = recv->map()->prototype();
    145   if (!proto->IsJSReceiver()) return false;
    146 
    147   Handle<Map> initial_proto_initial_map = isolate->regexp_prototype_map();
    148   if (JSReceiver::cast(proto)->map() != *initial_proto_initial_map) {
    149     return false;
    150   }
    151 
    152   // The smi check is required to omit ToLength(lastIndex) calls with possible
    153   // user-code execution on the fast path.
    154   Object* last_index = JSRegExp::cast(recv)->LastIndex();
    155   return last_index->IsSmi() && Smi::cast(last_index)->value() >= 0;
    156 }
    157 
    158 int RegExpUtils::AdvanceStringIndex(Isolate* isolate, Handle<String> string,
    159                                     int index, bool unicode) {
    160   if (unicode && index < string->length()) {
    161     const uint16_t first = string->Get(index);
    162     if (first >= 0xD800 && first <= 0xDBFF && string->length() > index + 1) {
    163       const uint16_t second = string->Get(index + 1);
    164       if (second >= 0xDC00 && second <= 0xDFFF) {
    165         return index + 2;
    166       }
    167     }
    168   }
    169 
    170   return index + 1;
    171 }
    172 
    173 MaybeHandle<Object> RegExpUtils::SetAdvancedStringIndex(
    174     Isolate* isolate, Handle<JSReceiver> regexp, Handle<String> string,
    175     bool unicode) {
    176   Handle<Object> last_index_obj;
    177   ASSIGN_RETURN_ON_EXCEPTION(
    178       isolate, last_index_obj,
    179       Object::GetProperty(regexp, isolate->factory()->lastIndex_string()),
    180       Object);
    181 
    182   ASSIGN_RETURN_ON_EXCEPTION(isolate, last_index_obj,
    183                              Object::ToLength(isolate, last_index_obj), Object);
    184   const int last_index = PositiveNumberToUint32(*last_index_obj);
    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