string.localecompare

Signed-off-by: gwl <guowanlong@huawei.com>
Change-Id: I7c61fab62ee92c32be66a52b4f46d0f8659153ee
This commit is contained in:
gwl 2023-11-02 17:08:12 +08:00
parent d29a1082fb
commit 455fd02b7e
18 changed files with 296 additions and 28 deletions

View File

@ -568,6 +568,30 @@ JSTaggedValue BuiltinsString::LocaleCompare(EcmaRuntimeCallInfo *argv)
#endif
}
JSTaggedValue BuiltinsString::LocaleCompareGC(JSThread *thread, JSHandle<JSTaggedValue> locales,
JSHandle<EcmaString> thisHandle, JSHandle<EcmaString> thatHandle,
JSHandle<JSTaggedValue> options, bool cacheable)
{
EcmaVM *ecmaVm = thread->GetEcmaVM();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSTaggedValue> ctor = ecmaVm->GetGlobalEnv()->GetCollatorFunction();
JSHandle<JSCollator> collator =
JSHandle<JSCollator>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(ctor)));
JSHandle<JSCollator> initCollator =
JSCollator::InitializeCollator(thread, collator, locales, options, cacheable);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
icu::Collator *icuCollator = nullptr;
if (cacheable) {
icuCollator = JSCollator::GetCachedIcuCollator(thread, locales);
ASSERT(icuCollator != nullptr);
} else {
icuCollator = initCollator->GetIcuCollator();
}
JSTaggedValue result = JSCollator::CompareStrings(icuCollator, thisHandle, thatHandle);
return result;
}
// 21.1.3.11
JSTaggedValue BuiltinsString::Match(EcmaRuntimeCallInfo *argv)
{

View File

@ -159,6 +159,9 @@ public:
static JSTaggedValue LastIndexOf(EcmaRuntimeCallInfo *argv);
// 21.1.3.10
static JSTaggedValue LocaleCompare(EcmaRuntimeCallInfo *argv);
static JSTaggedValue LocaleCompareGC(JSThread *thread, JSHandle<JSTaggedValue> locales,
JSHandle<EcmaString> thisHandle, JSHandle<EcmaString> thatHandle,
JSHandle<JSTaggedValue> options, bool cacheable);
// 21.1.3.11
static JSTaggedValue Match(EcmaRuntimeCallInfo *argv);

View File

@ -29,7 +29,8 @@ namespace panda::ecmascript::kungfu {
// AOT_BUILTINS_STUB_LIST is used in AOT only.
#define BUILTINS_STUB_LIST(V) \
BUILTINS_METHOD_STUB_LIST(V) \
BUILTINS_CONSTRUCTOR_STUB_LIST(V)
BUILTINS_CONSTRUCTOR_STUB_LIST(V) \
AOT_AND_BUILTINS_STUB_LIST(V)
#define BUILTINS_METHOD_STUB_LIST(V) \
V(StringCharCodeAt) \
@ -89,6 +90,9 @@ namespace panda::ecmascript::kungfu {
V(DateConstructor) \
V(ArrayConstructor)
#define AOT_AND_BUILTINS_STUB_LIST(V) \
V(LocaleCompare)
#define AOT_BUILTINS_STUB_LIST(V) \
V(SQRT) /* list start and math list start */ \
V(COS) \
@ -97,7 +101,6 @@ namespace panda::ecmascript::kungfu {
V(ATAN) \
V(ABS) \
V(FLOOR) /* math list end */ \
V(LocaleCompare) \
V(SORT) \
V(STRINGIFY) \
V(MAP_PROTO_ITERATOR) \
@ -163,8 +166,9 @@ public:
static bool IsTypedBuiltin(ID builtinId)
{
return (BuiltinsStubCSigns::ID::TYPED_BUILTINS_FIRST <= builtinId) &&
(builtinId <= BuiltinsStubCSigns::ID::TYPED_BUILTINS_LAST);
return (BuiltinsStubCSigns::ID::LocaleCompare == builtinId) ||
((BuiltinsStubCSigns::ID::TYPED_BUILTINS_FIRST <= builtinId) &&
(builtinId <= BuiltinsStubCSigns::ID::TYPED_BUILTINS_LAST));
}
static bool IsTypedBuiltinMath(ID builtinId)

View File

@ -1840,6 +1840,58 @@ GateRef BuiltinsStringStubBuilder::StringConcat(GateRef glue, GateRef leftString
return ret;
}
void BuiltinsStringStubBuilder::LocaleCompare([[maybe_unused]] GateRef glue, GateRef thisValue, GateRef numArgs,
[[maybe_unused]] Variable *res, [[maybe_unused]] Label *exit,
Label *slowPath)
{
auto env = GetEnvironment();
Label thisIsHeapObj(env);
Branch(TaggedIsHeapObject(thisValue), &thisIsHeapObj, slowPath);
Bind(&thisIsHeapObj);
{
Label thisValueIsString(env);
Label fristArgIsString(env);
Label arg0IsHeapObj(env);
Branch(IsString(thisValue), &thisValueIsString, slowPath);
Bind(&thisValueIsString);
GateRef arg0 = GetCallArg0(numArgs);
Branch(TaggedIsHeapObject(arg0), &arg0IsHeapObj, slowPath);
Bind(&arg0IsHeapObj);
Branch(IsString(arg0), &fristArgIsString, slowPath);
Bind(&fristArgIsString);
#ifdef ARK_SUPPORT_INTL
GateRef locales = GetCallArg1(numArgs);
GateRef options = GetCallArg2(numArgs);
GateRef localesIsUndef = TaggedIsUndefined(locales);
GateRef optionsIsUndef = TaggedIsUndefined(options);
GateRef cacheable = BoolAnd(BoolOr(localesIsUndef, TaggedObjectIsString(locales)), optionsIsUndef);
Label optionsIsString(env);
Label cacheAble(env);
Label uncacheable(env);
Branch(cacheable, &cacheAble, &uncacheable);
Bind(&cacheAble);
{
Label defvalue(env);
GateRef resValue = CallNGCRuntime(glue, RTSTUB_ID(LocaleCompareNoGc), {glue, locales, thisValue, arg0});
Branch(TaggedIsUndefined(resValue), slowPath, &defvalue);
Bind(&defvalue);
*res = resValue;
Jump(exit);
}
Bind(&uncacheable);
{
res->WriteVariable(CallRuntime(glue, RTSTUB_ID(LocaleCompareWithGc), {locales, thisValue, arg0, options}));
Jump(exit);
}
#else
Jump(slowPath);
#endif
}
}
GateRef BuiltinsStringStubBuilder::EcmaStringTrim(GateRef glue, GateRef srcString, GateRef trimMode)
{
auto env = GetEnvironment();

View File

@ -38,6 +38,7 @@ public:
void Replace(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *res, Label *exit, Label *slowPath);
void Trim(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *res, Label *exit, Label *slowPath);
void Slice(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *res, Label *exit, Label *slowPath);
void LocaleCompare(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *res, Label *exit, Label *slowPath);
GateRef ConvertAndClampRelativeIndex(GateRef index, GateRef length);
GateRef StringAt(const StringInfoGateRef &stringInfoGate, GateRef index);

View File

@ -177,6 +177,24 @@ DECLARE_BUILTINS(String##method)
V(Trim, JS_ANY, Undefined()) \
V(Slice, JS_ANY, Undefined())
DECLARE_BUILTINS(LocaleCompare)
{
auto env = GetEnvironment();
DEFVARIABLE(res, VariableType::JS_ANY(), Undefined());
Label exit(env);
Label slowPath(env);
BuiltinsStringStubBuilder stringStubBuilder(this);
stringStubBuilder.LocaleCompare(glue, thisValue, numArgs, &res, &exit, &slowPath);
Bind(&slowPath);
{
auto name = BuiltinsStubCSigns::GetName(BUILTINS_STUB_ID(LocaleCompare));
res = CallSlowPath(nativeCode, glue, thisValue, numArgs, func, newTarget, name.c_str());
Jump(&exit);
}
Bind(&exit);
Return(*res);
}
BUILTINS_WITH_STRING_STUB_BUILDER(DECLARE_BUILTINS_WITH_STRING_STUB_BUILDER)
#undef DECLARE_BUILTINS_WITH_STRING_STUB_BUILDER

View File

@ -1209,6 +1209,23 @@ DEF_CALL_SIGNATURE(BigIntEquals)
callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC);
}
DEF_CALL_SIGNATURE(LocaleCompareNoGc)
{
// 4 : 4 input parameters
CallSignature localeCompareNoGc("LocaleCompareNoGc", 0, 4,
ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY());
*callSign = localeCompareNoGc;
std::array<VariableType, 4> params = { // 4 : 4 input parameters
VariableType::NATIVE_POINTER(),
VariableType::JS_POINTER(),
VariableType::JS_POINTER(),
VariableType::JS_POINTER(),
};
callSign->SetParameters(params.data());
callSign->SetGCLeafFunction(true);
callSign->SetTargetKind(CallSignature::TargetKind::RUNTIME_STUB_NO_GC);
}
DEF_CALL_SIGNATURE(BigIntSameValueZero)
{
// 1 : 1 input parameters

View File

@ -471,6 +471,7 @@ private:
V(CreateJSMapIterator) \
V(JSHClassFindProtoTransitions) \
V(NumberHelperStringToDouble) \
V(LocaleCompareNoGc) \
V(StringGetStart) \
V(StringGetEnd)

View File

@ -1147,6 +1147,25 @@ inline GateRef StubBuilder::IsString(GateRef obj)
return res;
}
inline GateRef StubBuilder::TaggedObjectIsString(GateRef obj)
{
auto env = GetEnvironment();
Label entryPass(env);
env->SubCfgEntry(&entryPass);
DEFVARIABLE(result, VariableType::BOOL(), False());
Label heapObj(env);
Label exit(env);
GateRef isHeapObject = TaggedIsHeapObject(obj);
Branch(isHeapObject, &heapObj, &exit);
Bind(&heapObj);
result = env_->GetBuilder()->TaggedObjectIsString(obj);
Jump(&exit);
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
inline GateRef StubBuilder::IsLineString(GateRef obj)
{
GateRef objectType = GetObjectType(LoadHClass(obj));

View File

@ -310,6 +310,7 @@ public:
GateRef IsEcmaObject(GateRef obj);
GateRef IsSymbol(GateRef obj);
GateRef IsString(GateRef obj);
GateRef TaggedObjectIsString(GateRef obj);
GateRef IsLineString(GateRef obj);
GateRef IsSlicedString(GateRef obj);
GateRef IsConstantString(GateRef obj);

View File

@ -75,7 +75,8 @@ enum class IcuFormatterType {
SIMPLE_DATE_FORMAT_DATE,
SIMPLE_DATE_FORMAT_TIME,
NUMBER_FORMATTER,
COLLATOR
COLLATOR,
ICU_FORMATTER_TYPE_COUNT
};
using HostPromiseRejectionTracker = void (*)(const EcmaVM* vm,
@ -273,32 +274,27 @@ public:
IcuDeleteEntry deleteEntry = nullptr)
{
EcmaContext::IcuFormatter icuFormatter = IcuFormatter(locale, icuObj, deleteEntry);
icuObjCache_.insert_or_assign(type, std::move(icuFormatter));
icuObjCache_[static_cast<int>(type)] = icuFormatter;
}
void *GetIcuFormatterFromCache(IcuFormatterType type, std::string locale)
ARK_INLINE void *GetIcuFormatterFromCache(IcuFormatterType type, std::string &locale)
{
auto iter = icuObjCache_.find(type);
if (iter != icuObjCache_.end()) {
EcmaContext::IcuFormatter icuFormatter = iter->second;
if (icuFormatter.locale == locale) {
return icuFormatter.icuObj;
}
auto &icuFormatter = icuObjCache_[static_cast<int>(type)];
if (icuFormatter.locale == locale) {
return icuFormatter.icuObj;
}
return nullptr;
}
void ClearIcuCache()
{
auto iter = icuObjCache_.begin();
while (iter != icuObjCache_.end()) {
EcmaContext::IcuFormatter icuFormatter = iter->second;
for (uint32_t i = 0; i < static_cast<uint32_t>(IcuFormatterType::ICU_FORMATTER_TYPE_COUNT); i++) {
auto &icuFormatter = icuObjCache_[i];
IcuDeleteEntry deleteEntry = icuFormatter.deleteEntry;
if (deleteEntry != nullptr) {
deleteEntry(icuFormatter.icuObj, vm_);
}
iter->second = EcmaContext::IcuFormatter{};
iter++;
icuFormatter = EcmaContext::IcuFormatter{};
}
}
@ -545,8 +541,7 @@ private:
IcuFormatter(const std::string &locale, void *icuObj, IcuDeleteEntry deleteEntry = nullptr)
: locale(locale), icuObj(icuObj), deleteEntry(deleteEntry) {}
};
std::unordered_map<IcuFormatterType, IcuFormatter> icuObjCache_;
IcuFormatter icuObjCache_[static_cast<uint32_t>(IcuFormatterType::ICU_FORMATTER_TYPE_COUNT)];
// Handlescope
static const uint32_t NODE_BLOCK_SIZE_LOG2 = 10;
static const uint32_t NODE_BLOCK_SIZE = 1U << NODE_BLOCK_SIZE_LOG2;

View File

@ -20,6 +20,7 @@
#include "ecmascript/mem/c_string.h"
#include "ecmascript/mem/barriers-inl.h"
#include "ecmascript/object_factory-inl.h"
#include "ecmascript/ecma_string-inl.h"
#include "unicode/udata.h"
@ -315,9 +316,9 @@ JSHandle<JSCollator> JSCollator::InitializeCollator(JSThread *thread,
return collator;
}
icu::Collator *JSCollator::GetCachedIcuCollator(JSThread *thread, const JSHandle<JSTaggedValue> &locales)
icu::Collator *JSCollator::GetCachedIcuCollator(JSThread *thread, const JSTaggedValue &locales)
{
std::string cacheEntry = locales->IsUndefined() ? "" : EcmaStringAccessor(locales.GetTaggedValue()).ToStdString();
std::string cacheEntry = locales.IsUndefined() ? "" : EcmaStringAccessor(locales).ToStdString();
void *cachedCollator =
thread->GetCurrentEcmaContext()->GetIcuFormatterFromCache(IcuFormatterType::COLLATOR, cacheEntry);
if (cachedCollator != nullptr) {
@ -326,6 +327,11 @@ icu::Collator *JSCollator::GetCachedIcuCollator(JSThread *thread, const JSHandle
return nullptr;
}
icu::Collator *JSCollator::GetCachedIcuCollator(JSThread *thread, const JSHandle<JSTaggedValue> &locales)
{
return GetCachedIcuCollator(thread, locales.GetTaggedValue());
}
UColAttributeValue JSCollator::OptionToUColAttribute(CaseFirstOption caseFirstOption)
{
auto iter = uColAttributeValueMap.find(caseFirstOption);
@ -455,22 +461,49 @@ JSHandle<JSObject> JSCollator::ResolvedOptions(JSThread *thread, const JSHandle<
return options;
}
icu::UnicodeString EcmaStringToUString(const JSHandle<EcmaString> &string)
ARK_INLINE icu::UnicodeString EcmaStringToUString(EcmaString *string)
{
std::string stdString(ConvertToString(*string, StringConvertedUsage::LOGICOPERATION));
icu::StringPiece sp(stdString);
CVector<uint8_t> buf;
Span<const uint8_t> span = EcmaStringAccessor(string).ToUtf8Span(buf);
icu::StringPiece sp(reinterpret_cast<const char*>(span.begin()), span.size());
icu::UnicodeString uString = icu::UnicodeString::fromUTF8(sp);
return uString;
}
icu::UnicodeString EcmaStringToUString(const JSHandle<EcmaString> &string)
{
return EcmaStringToUString(string.GetObject<EcmaString>());
}
JSTaggedValue JSCollator::CompareStrings(const icu::Collator *icuCollator, const JSHandle<EcmaString> &string1,
const JSHandle<EcmaString> &string2)
{
return CompareStrings(icuCollator, string1.GetObject<EcmaString>(), string2.GetObject<EcmaString>());
}
JSTaggedValue JSCollator::CompareStrings(const icu::Collator *icuCollator, EcmaString *string1, EcmaString *string2)
{
UCollationResult result;
UErrorCode status = U_ZERO_ERROR;
if (string1 == string2) {
return JSTaggedValue(UCollationResult::UCOL_EQUAL);
}
{
EcmaStringAccessor string1Acc(string1);
EcmaStringAccessor string2Acc(string2);
if (string1Acc.IsUtf8() && string1Acc.IsLineOrConstantString() &&
string2Acc.IsUtf8() && string2Acc.IsLineOrConstantString()) {
icu::StringPiece stringPiece1(reinterpret_cast<const char*>(string1Acc.GetDataUtf8()),
string1Acc.GetLength());
icu::StringPiece stringPiece2(reinterpret_cast<const char*>(string2Acc.GetDataUtf8()),
string2Acc.GetLength());
result = icuCollator->compareUTF8(stringPiece1, stringPiece2, status);
return JSTaggedValue(result);
}
}
icu::UnicodeString uString1 = EcmaStringToUString(string1);
icu::UnicodeString uString2 = EcmaStringToUString(string2);
UCollationResult result;
UErrorCode status = U_ZERO_ERROR;
result = icuCollator->compare(uString1, uString2, status);
ASSERT(U_SUCCESS(status));

View File

@ -88,6 +88,7 @@ public:
bool enableLocaleCache = false);
static icu::Collator *GetCachedIcuCollator(JSThread *thread, const JSHandle<JSTaggedValue> &locales);
static icu::Collator *GetCachedIcuCollator(JSThread *thread, const JSTaggedValue &locales);
// 11.3.4 Intl.Collator.prototype.resolvedOptions ()
static JSHandle<JSObject> ResolvedOptions(JSThread *thread, const JSHandle<JSCollator> &collator);
@ -97,6 +98,8 @@ public:
static JSTaggedValue CompareStrings(const icu::Collator *icuCollator, const JSHandle<EcmaString> &string1,
const JSHandle<EcmaString> &string2);
static JSTaggedValue CompareStrings(const icu::Collator *icuCollator, EcmaString *string1, EcmaString *string2);
static JSTaggedValue FastCompareStrings(JSThread *thread, const icu::Collator *icuCollator,
const JSHandle<EcmaString> &string1,
const JSHandle<EcmaString> &string2);

View File

@ -2784,6 +2784,31 @@ DEF_RUNTIME_STUBS(ObjectSlowAssign)
return builtins::BuiltinsObject::AssignTaggedValue(thread, source, toAssign).GetRawData();
}
DEF_RUNTIME_STUBS(LocaleCompareWithGc)
{
RUNTIME_STUBS_HEADER(LocaleCompareWithGc);
JSHandle<JSTaggedValue> locales = GetHArg<JSTaggedValue>(argv, argc, 0); // 0: means the zeroth parameter
JSHandle<EcmaString> thisHandle = GetHArg<EcmaString>(argv, argc, 1); // 1: means the first parameter
JSHandle<EcmaString> thatHandle = GetHArg<EcmaString>(argv, argc, 2); // 2: means the second parameter
JSHandle<JSTaggedValue> options = GetHArg<JSTaggedValue>(argv, argc, 3); // 3: means the third parameter
bool cacheable = options->IsUndefined() && (locales->IsUndefined() || locales->IsString());
return builtins::BuiltinsString::LocaleCompareGC(thread, locales, thisHandle, thatHandle,
options, cacheable).GetRawData();
}
JSTaggedValue RuntimeStubs::LocaleCompareNoGc(uintptr_t argGlue, JSTaggedType locales, EcmaString *thisHandle,
EcmaString *thatHandle)
{
DISALLOW_GARBAGE_COLLECTION;
auto thread = JSThread::GlueToJSThread(argGlue);
auto collator = JSCollator::GetCachedIcuCollator(thread, JSTaggedValue(locales));
JSTaggedValue result = JSTaggedValue::Undefined();
if (collator != nullptr) {
result = JSCollator::CompareStrings(collator, thisHandle, thatHandle);
}
return result;
}
void RuntimeStubs::Initialize(JSThread *thread)
{
#define DEF_RUNTIME_STUB(name) kungfu::RuntimeStubCSigns::ID_##name

View File

@ -130,6 +130,7 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co
V(ComputeHashcode) \
V(JSHClassFindProtoTransitions) \
V(NumberHelperStringToDouble) \
V(LocaleCompareNoGc) \
V(StringGetStart) \
V(StringGetEnd)
@ -336,7 +337,8 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co
V(LinkedHashMapComputeCapacity) \
V(LinkedHashSetComputeCapacity) \
V(JSObjectGrowElementsCapacity) \
V(HClassCloneWithAddProto)
V(HClassCloneWithAddProto) \
V(LocaleCompareWithGc)
#define RUNTIME_STUB_LIST(V) \
RUNTIME_ASM_STUB_LIST(V) \
@ -413,6 +415,8 @@ public:
static bool BigIntSameValueZero(JSTaggedType key, JSTaggedType other);
static JSTaggedValue JSHClassFindProtoTransitions(JSHClass *cls, JSTaggedValue key, JSTaggedValue proto);
static JSTaggedValue NumberHelperStringToDouble(EcmaString *str);
static JSTaggedValue LocaleCompareNoGc(uintptr_t argGlue, JSTaggedType locales, EcmaString *thisHandle,
EcmaString *thatHandle);
static double TimeClip(double time);
static double SetDateValues(double year, double month, double day);
static void StartCallTimer(uintptr_t argGlue, JSTaggedType func, bool isAot);

View File

@ -0,0 +1,18 @@
# Copyright (c) 2023 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_moduletest_action("stringlocalecompare") {
deps = []
}

View File

@ -0,0 +1,16 @@
# Copyright (c) 2023 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-1
1
0

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @tc.name:stringSlice
* @tc.desc:test String.slice
* @tc.type: FUNC
* @tc.require: issueI5NO8G
*/
let str1 = "a";
let str2 = "c";
print(str1.localeCompare(str2));
let str3 = "check";
let str4 = "against";
print(str3.localeCompare(str4));
let str5 = "abc";
let str6 = "abc";
print(str5.localecCompare(str6));