|  | /* | 
|  | *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org) | 
|  | *  Copyright (C) 2003-2018 Apple Inc. All Rights Reserved. | 
|  | * | 
|  | *  This library is free software; you can redistribute it and/or | 
|  | *  modify it under the terms of the GNU Lesser General Public | 
|  | *  License as published by the Free Software Foundation; either | 
|  | *  version 2 of the License, or (at your option) any later version. | 
|  | * | 
|  | *  This library is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | *  Lesser General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU Lesser General Public | 
|  | *  License along with this library; if not, write to the Free Software | 
|  | *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | 
|  | * | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include "ButterflyInlines.h" | 
|  | #include "Error.h" | 
|  | #include "ExceptionHelpers.h" | 
|  | #include "JSArray.h" | 
|  | #include "JSGlobalObject.h" | 
|  | #include "JSString.h" | 
|  | #include "JSCInlines.h" | 
|  | #include "RegExpGlobalDataInlines.h" | 
|  | #include "RegExpMatchesArray.h" | 
|  | #include "RegExpObject.h" | 
|  |  | 
|  | namespace JSC { | 
|  |  | 
|  | ALWAYS_INLINE unsigned getRegExpObjectLastIndexAsUnsigned( | 
|  | JSGlobalObject* globalObject, RegExpObject* regExpObject, const String& input) | 
|  | { | 
|  | VM& vm = globalObject->vm(); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  | JSValue jsLastIndex = regExpObject->getLastIndex(); | 
|  | unsigned lastIndex; | 
|  | if (LIKELY(jsLastIndex.isUInt32())) { | 
|  | lastIndex = jsLastIndex.asUInt32(); | 
|  | if (lastIndex > input.length()) | 
|  | return UINT_MAX; | 
|  | } else { | 
|  | double doubleLastIndex = jsLastIndex.toIntegerOrInfinity(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, UINT_MAX); | 
|  | if (doubleLastIndex > input.length()) | 
|  | return UINT_MAX; | 
|  | lastIndex = (doubleLastIndex < 0) ? 0 : static_cast<unsigned>(doubleLastIndex); | 
|  | } | 
|  | return lastIndex; | 
|  | } | 
|  |  | 
|  | inline JSValue RegExpObject::execInline(JSGlobalObject* globalObject, JSString* string) | 
|  | { | 
|  | VM& vm = globalObject->vm(); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | RegExp* regExp = this->regExp(); | 
|  | String input = string->value(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  |  | 
|  | bool globalOrSticky = regExp->globalOrSticky(); | 
|  | unsigned lastIndex = getRegExpObjectLastIndexAsUnsigned(globalObject, this, input); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | if (lastIndex == UINT_MAX && globalOrSticky) { | 
|  | scope.release(); | 
|  | setLastIndex(globalObject, 0); | 
|  | return jsNull(); | 
|  | } | 
|  |  | 
|  | if (!globalOrSticky) | 
|  | lastIndex = 0; | 
|  |  | 
|  | MatchResult result; | 
|  | JSArray* array = | 
|  | createRegExpMatchesArray(vm, globalObject, string, input, regExp, lastIndex, result); | 
|  | if (!array) { | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | scope.release(); | 
|  | if (globalOrSticky) | 
|  | setLastIndex(globalObject, 0); | 
|  | return jsNull(); | 
|  | } | 
|  |  | 
|  | if (globalOrSticky) | 
|  | setLastIndex(globalObject, result.end); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | globalObject->regExpGlobalData().recordMatch(vm, globalObject, regExp, string, result); | 
|  | return array; | 
|  | } | 
|  |  | 
|  | // Shared implementation used by test and exec. | 
|  | inline MatchResult RegExpObject::matchInline( | 
|  | JSGlobalObject* globalObject, JSString* string) | 
|  | { | 
|  | VM& vm = globalObject->vm(); | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | RegExp* regExp = this->regExp(); | 
|  | String input = string->value(globalObject); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  |  | 
|  | if (!regExp->global() && !regExp->sticky()) { | 
|  | scope.release(); | 
|  | return globalObject->regExpGlobalData().performMatch(globalObject, regExp, string, input, 0); | 
|  | } | 
|  |  | 
|  | unsigned lastIndex = getRegExpObjectLastIndexAsUnsigned(globalObject, this, input); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | if (lastIndex == UINT_MAX) { | 
|  | scope.release(); | 
|  | setLastIndex(globalObject, 0); | 
|  | return MatchResult::failed(); | 
|  | } | 
|  |  | 
|  | MatchResult result = globalObject->regExpGlobalData().performMatch(globalObject, regExp, string, input, lastIndex); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | scope.release(); | 
|  | setLastIndex(globalObject, result.end); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | inline unsigned advanceStringUnicode(String s, unsigned length, unsigned currentIndex) | 
|  | { | 
|  | if (currentIndex + 1 >= length) | 
|  | return currentIndex + 1; | 
|  |  | 
|  | UChar first = s[currentIndex]; | 
|  | if (first < 0xD800 || first > 0xDBFF) | 
|  | return currentIndex + 1; | 
|  |  | 
|  | UChar second = s[currentIndex + 1]; | 
|  | if (second < 0xDC00 || second > 0xDFFF) | 
|  | return currentIndex + 1; | 
|  |  | 
|  | return currentIndex + 2; | 
|  | } | 
|  |  | 
|  | template<typename FixEndFunc> | 
|  | JSValue collectMatches(VM& vm, JSGlobalObject* globalObject, JSString* string, const String& s, RegExp* regExp, const FixEndFunc& fixEnd) | 
|  | { | 
|  | auto scope = DECLARE_THROW_SCOPE(vm); | 
|  |  | 
|  | MatchResult result = globalObject->regExpGlobalData().performMatch(globalObject, regExp, string, s, 0); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | if (!result) | 
|  | return jsNull(); | 
|  |  | 
|  | static unsigned maxSizeForDirectPath = 100000; | 
|  |  | 
|  | JSArray* array = constructEmptyArray(globalObject, nullptr); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  |  | 
|  | bool hasException = false; | 
|  | unsigned arrayIndex = 0; | 
|  | auto iterate = [&] () { | 
|  | size_t end = result.end; | 
|  | size_t length = end - result.start; | 
|  | array->putDirectIndex(globalObject, arrayIndex++, jsSubstringOfResolved(vm, string, result.start, length)); | 
|  | if (UNLIKELY(scope.exception())) { | 
|  | hasException = true; | 
|  | return; | 
|  | } | 
|  | if (!length) | 
|  | end = fixEnd(end); | 
|  | result = globalObject->regExpGlobalData().performMatch(globalObject, regExp, string, s, end); | 
|  | if (UNLIKELY(scope.exception())) { | 
|  | hasException = true; | 
|  | return; | 
|  | } | 
|  | }; | 
|  |  | 
|  | do { | 
|  | if (array->length() >= maxSizeForDirectPath) { | 
|  | // First do a throw-away match to see how many matches we'll get. | 
|  | unsigned matchCount = 0; | 
|  | MatchResult savedResult = result; | 
|  | do { | 
|  | if (array->length() + matchCount > MAX_STORAGE_VECTOR_LENGTH) { | 
|  | throwOutOfMemoryError(globalObject, scope); | 
|  | return jsUndefined(); | 
|  | } | 
|  |  | 
|  | size_t end = result.end; | 
|  | matchCount++; | 
|  | if (result.empty()) | 
|  | end = fixEnd(end); | 
|  |  | 
|  | // Using RegExpGlobalData::performMatch() instead of calling RegExp::match() | 
|  | // directly is a surprising but profitable choice: it means that when we do OOM, we | 
|  | // will leave the cached result in the state it ought to have had just before the | 
|  | // OOM! On the other hand, if this loop concludes that the result is small enough, | 
|  | // then the iterate() loop below will overwrite the cached result anyway. | 
|  | result = globalObject->regExpGlobalData().performMatch(globalObject, regExp, string, s, end); | 
|  | RETURN_IF_EXCEPTION(scope, { }); | 
|  | } while (result); | 
|  |  | 
|  | // OK, we have a sensible number of matches. Now we can create them for reals. | 
|  | result = savedResult; | 
|  | do { | 
|  | iterate(); | 
|  | EXCEPTION_ASSERT(!!scope.exception() == hasException); | 
|  | if (UNLIKELY(hasException)) | 
|  | return { }; | 
|  | } while (result); | 
|  |  | 
|  | return array; | 
|  | } | 
|  |  | 
|  | iterate(); | 
|  | EXCEPTION_ASSERT(!!scope.exception() == hasException); | 
|  | if (UNLIKELY(hasException)) | 
|  | return { }; | 
|  | } while (result); | 
|  |  | 
|  | return array; | 
|  | } | 
|  |  | 
|  | } // namespace JSC |