mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2025-04-09 10:20:59 +00:00
224 lines
7.6 KiB
C++
224 lines
7.6 KiB
C++
/*
|
|
* 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.toInteger(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
|