| // Copyright 2016 the V8 project authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "src/regexp/regexp-utils.h" | 
 |  | 
 | #include "src/execution/isolate.h" | 
 | #include "src/execution/protectors-inl.h" | 
 | #include "src/heap/factory.h" | 
 | #include "src/objects/js-regexp-inl.h" | 
 | #include "src/objects/objects-inl.h" | 
 | #include "src/regexp/regexp.h" | 
 |  | 
 | namespace v8 { | 
 | namespace internal { | 
 |  | 
 | Handle<String> RegExpUtils::GenericCaptureGetter( | 
 |     Isolate* isolate, Handle<RegExpMatchInfo> match_info, int capture, | 
 |     bool* ok) { | 
 |   const int index = capture * 2; | 
 |   if (index >= match_info->NumberOfCaptureRegisters()) { | 
 |     if (ok != nullptr) *ok = false; | 
 |     return isolate->factory()->empty_string(); | 
 |   } | 
 |  | 
 |   const int match_start = match_info->Capture(index); | 
 |   const int match_end = match_info->Capture(index + 1); | 
 |   if (match_start == -1 || match_end == -1) { | 
 |     if (ok != nullptr) *ok = false; | 
 |     return isolate->factory()->empty_string(); | 
 |   } | 
 |  | 
 |   if (ok != nullptr) *ok = true; | 
 |   Handle<String> last_subject(match_info->LastSubject(), isolate); | 
 |   return isolate->factory()->NewSubString(last_subject, match_start, match_end); | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | V8_INLINE bool HasInitialRegExpMap(Isolate* isolate, JSReceiver recv) { | 
 |   return recv.map() == isolate->regexp_function()->initial_map(); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | MaybeHandle<Object> RegExpUtils::SetLastIndex(Isolate* isolate, | 
 |                                               Handle<JSReceiver> recv, | 
 |                                               uint64_t value) { | 
 |   Handle<Object> value_as_object = | 
 |       isolate->factory()->NewNumberFromInt64(value); | 
 |   if (HasInitialRegExpMap(isolate, *recv)) { | 
 |     JSRegExp::cast(*recv).set_last_index(*value_as_object, SKIP_WRITE_BARRIER); | 
 |     return recv; | 
 |   } else { | 
 |     return Object::SetProperty( | 
 |         isolate, recv, isolate->factory()->lastIndex_string(), value_as_object, | 
 |         StoreOrigin::kMaybeKeyed, Just(kThrowOnError)); | 
 |   } | 
 | } | 
 |  | 
 | MaybeHandle<Object> RegExpUtils::GetLastIndex(Isolate* isolate, | 
 |                                               Handle<JSReceiver> recv) { | 
 |   if (HasInitialRegExpMap(isolate, *recv)) { | 
 |     return handle(JSRegExp::cast(*recv).last_index(), isolate); | 
 |   } else { | 
 |     return Object::GetProperty(isolate, recv, | 
 |                                isolate->factory()->lastIndex_string()); | 
 |   } | 
 | } | 
 |  | 
 | // ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S ) | 
 | // Also takes an optional exec method in case our caller | 
 | // has already fetched exec. | 
 | MaybeHandle<Object> RegExpUtils::RegExpExec(Isolate* isolate, | 
 |                                             Handle<JSReceiver> regexp, | 
 |                                             Handle<String> string, | 
 |                                             Handle<Object> exec) { | 
 |   if (exec->IsUndefined(isolate)) { | 
 |     ASSIGN_RETURN_ON_EXCEPTION( | 
 |         isolate, exec, | 
 |         Object::GetProperty(isolate, regexp, isolate->factory()->exec_string()), | 
 |         Object); | 
 |   } | 
 |  | 
 |   if (exec->IsCallable()) { | 
 |     const int argc = 1; | 
 |     ScopedVector<Handle<Object>> argv(argc); | 
 |     argv[0] = string; | 
 |  | 
 |     Handle<Object> result; | 
 |     ASSIGN_RETURN_ON_EXCEPTION( | 
 |         isolate, result, | 
 |         Execution::Call(isolate, exec, regexp, argc, argv.begin()), Object); | 
 |  | 
 |     if (!result->IsJSReceiver() && !result->IsNull(isolate)) { | 
 |       THROW_NEW_ERROR(isolate, | 
 |                       NewTypeError(MessageTemplate::kInvalidRegExpExecResult), | 
 |                       Object); | 
 |     } | 
 |     return result; | 
 |   } | 
 |  | 
 |   if (!regexp->IsJSRegExp()) { | 
 |     THROW_NEW_ERROR(isolate, | 
 |                     NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, | 
 |                                  isolate->factory()->NewStringFromAsciiChecked( | 
 |                                      "RegExp.prototype.exec"), | 
 |                                  regexp), | 
 |                     Object); | 
 |   } | 
 |  | 
 |   { | 
 |     Handle<JSFunction> regexp_exec = isolate->regexp_exec_function(); | 
 |  | 
 |     const int argc = 1; | 
 |     ScopedVector<Handle<Object>> argv(argc); | 
 |     argv[0] = string; | 
 |  | 
 |     return Execution::Call(isolate, regexp_exec, regexp, argc, argv.begin()); | 
 |   } | 
 | } | 
 |  | 
 | Maybe<bool> RegExpUtils::IsRegExp(Isolate* isolate, Handle<Object> object) { | 
 |   if (!object->IsJSReceiver()) return Just(false); | 
 |  | 
 |   Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object); | 
 |  | 
 |   Handle<Object> match; | 
 |   ASSIGN_RETURN_ON_EXCEPTION_VALUE( | 
 |       isolate, match, | 
 |       JSObject::GetProperty(isolate, receiver, | 
 |                             isolate->factory()->match_symbol()), | 
 |       Nothing<bool>()); | 
 |  | 
 |   if (!match->IsUndefined(isolate)) { | 
 |     const bool match_as_boolean = match->BooleanValue(isolate); | 
 |  | 
 |     if (match_as_boolean && !object->IsJSRegExp()) { | 
 |       isolate->CountUsage(v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp); | 
 |     } else if (!match_as_boolean && object->IsJSRegExp()) { | 
 |       isolate->CountUsage(v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp); | 
 |     } | 
 |  | 
 |     return Just(match_as_boolean); | 
 |   } | 
 |  | 
 |   return Just(object->IsJSRegExp()); | 
 | } | 
 |  | 
 | bool RegExpUtils::IsUnmodifiedRegExp(Isolate* isolate, Handle<Object> obj) { | 
 | #ifdef V8_ENABLE_FORCE_SLOW_PATH | 
 |   if (isolate->force_slow_path()) return false; | 
 | #endif | 
 |  | 
 |   if (!obj->IsJSReceiver()) return false; | 
 |  | 
 |   JSReceiver recv = JSReceiver::cast(*obj); | 
 |  | 
 |   if (!HasInitialRegExpMap(isolate, recv)) return false; | 
 |  | 
 |   // Check the receiver's prototype's map. | 
 |   Object proto = recv.map().prototype(); | 
 |   if (!proto.IsJSReceiver()) return false; | 
 |  | 
 |   Handle<Map> initial_proto_initial_map = isolate->regexp_prototype_map(); | 
 |   Map proto_map = JSReceiver::cast(proto).map(); | 
 |   if (proto_map != *initial_proto_initial_map) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Check that the "exec" method is unmodified. | 
 |   // Check that the index refers to "exec" method (this has to be consistent | 
 |   // with the init order in the bootstrapper). | 
 |   InternalIndex kExecIndex(JSRegExp::kExecFunctionDescriptorIndex); | 
 |   DCHECK_EQ(*(isolate->factory()->exec_string()), | 
 |             proto_map.instance_descriptors(kRelaxedLoad).GetKey(kExecIndex)); | 
 |   if (proto_map.instance_descriptors(kRelaxedLoad) | 
 |           .GetDetails(kExecIndex) | 
 |           .constness() != PropertyConstness::kConst) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Note: Unlike the more involved check in CSA (see BranchIfFastRegExp), this | 
 |   // does not go on to check the actual value of the exec property. This would | 
 |   // not be valid since this method is called from places that access the flags | 
 |   // property. Similar spots in CSA would use BranchIfFastRegExp_Strict in this | 
 |   // case. | 
 |  | 
 |   if (!Protectors::IsRegExpSpeciesLookupChainIntact(isolate)) return false; | 
 |  | 
 |   // The smi check is required to omit ToLength(lastIndex) calls with possible | 
 |   // user-code execution on the fast path. | 
 |   Object last_index = JSRegExp::cast(recv).last_index(); | 
 |   return last_index.IsSmi() && Smi::ToInt(last_index) >= 0; | 
 | } | 
 |  | 
 | uint64_t RegExpUtils::AdvanceStringIndex(Handle<String> string, uint64_t index, | 
 |                                          bool unicode) { | 
 |   DCHECK_LE(static_cast<double>(index), kMaxSafeInteger); | 
 |   const uint64_t string_length = static_cast<uint64_t>(string->length()); | 
 |   if (unicode && index < string_length) { | 
 |     const uint16_t first = string->Get(static_cast<uint32_t>(index)); | 
 |     if (first >= 0xD800 && first <= 0xDBFF && index + 1 < string_length) { | 
 |       DCHECK_LT(index, std::numeric_limits<uint64_t>::max()); | 
 |       const uint16_t second = string->Get(static_cast<uint32_t>(index + 1)); | 
 |       if (second >= 0xDC00 && second <= 0xDFFF) { | 
 |         return index + 2; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   return index + 1; | 
 | } | 
 |  | 
 | MaybeHandle<Object> RegExpUtils::SetAdvancedStringIndex( | 
 |     Isolate* isolate, Handle<JSReceiver> regexp, Handle<String> string, | 
 |     bool unicode) { | 
 |   Handle<Object> last_index_obj; | 
 |   ASSIGN_RETURN_ON_EXCEPTION( | 
 |       isolate, last_index_obj, | 
 |       Object::GetProperty(isolate, regexp, | 
 |                           isolate->factory()->lastIndex_string()), | 
 |       Object); | 
 |  | 
 |   ASSIGN_RETURN_ON_EXCEPTION(isolate, last_index_obj, | 
 |                              Object::ToLength(isolate, last_index_obj), Object); | 
 |   const uint64_t last_index = PositiveNumberToUint64(*last_index_obj); | 
 |   const uint64_t new_last_index = | 
 |       AdvanceStringIndex(string, last_index, unicode); | 
 |  | 
 |   return SetLastIndex(isolate, regexp, new_last_index); | 
 | } | 
 |  | 
 | }  // namespace internal | 
 | }  // namespace v8 |