diff --git a/ecmascript/compiler/builtins/builtins_call_signature.h b/ecmascript/compiler/builtins/builtins_call_signature.h index 95a464feef..af7f5ec30d 100644 --- a/ecmascript/compiler/builtins/builtins_call_signature.h +++ b/ecmascript/compiler/builtins/builtins_call_signature.h @@ -64,7 +64,8 @@ namespace panda::ecmascript::kungfu { V(ACOS) \ V(ATAN) \ V(ABS) \ - V(FLOOR) + V(FLOOR) \ + V(LocaleCompare) class BuiltinsStubCSigns { public: @@ -161,6 +162,7 @@ public: {"atan", ATAN}, {"abs", ABS}, {"floor", FLOOR}, + {"localeCompare", LocaleCompare}, }; if (str2BuiltinId.count(idStr) > 0) { return str2BuiltinId.at(idStr); diff --git a/ecmascript/compiler/builtins_lowering.cpp b/ecmascript/compiler/builtins_lowering.cpp index f258082c72..7fdd479d34 100644 --- a/ecmascript/compiler/builtins_lowering.cpp +++ b/ecmascript/compiler/builtins_lowering.cpp @@ -33,6 +33,9 @@ void BuiltinLowering::LowerTypedCallBuitin(GateRef gate) case BUILTINS_STUB_ID(ATAN): LowerTypedTrigonometric(gate, id); break; + case BUILTINS_STUB_ID(LocaleCompare): + LowerTypedLocaleCompare(gate); + break; default: break; } @@ -176,6 +179,64 @@ GateRef BuiltinLowering::TypedAbs(GateRef gate) return ret; } +GateRef BuiltinLowering::LowerCallRuntime(GateRef glue, GateRef gate, int index, const std::vector &args, + bool useLabel) +{ + const std::string name = RuntimeStubCSigns::GetRTName(index); + if (useLabel) { + GateRef result = builder_.CallRuntime(glue, index, Gate::InvalidGateRef, args, gate, name.c_str()); + return result; + } else { + const CallSignature *cs = RuntimeStubCSigns::Get(RTSTUB_ID(CallRuntime)); + GateRef target = builder_.IntPtr(index); + GateRef result = builder_.Call(cs, glue, target, builder_.GetDepend(), args, gate, name.c_str()); + return result; + } +} + +void BuiltinLowering::ReplaceHirWithValue(GateRef hirGate, GateRef value, bool noThrow) +{ + if (!noThrow) { + GateRef state = builder_.GetState(); + // copy depend-wire of hirGate to value + GateRef depend = builder_.GetDepend(); + // exception value + GateRef exceptionVal = builder_.ExceptionConstant(); + // compare with trampolines result + GateRef equal = builder_.Equal(value, exceptionVal); + auto ifBranch = builder_.Branch(state, equal); + + GateRef ifTrue = builder_.IfTrue(ifBranch); + GateRef ifFalse = builder_.IfFalse(ifBranch); + GateRef eDepend = builder_.DependRelay(ifTrue, depend); + GateRef sDepend = builder_.DependRelay(ifFalse, depend); + StateDepend success(ifFalse, sDepend); + StateDepend exception(ifTrue, eDepend); + acc_.ReplaceHirWithIfBranch(hirGate, success, exception, value); + } else { + acc_.ReplaceHirDirectly(hirGate, builder_.GetStateDepend(), value); + } +} + +void BuiltinLowering::LowerTypedLocaleCompare(GateRef gate) +{ + GateRef glue = acc_.GetGlueFromArgList(); + uint32_t index = 0; + GateRef thisObj = acc_.GetValueIn(gate, index++); + GateRef a0 = acc_.GetValueIn(gate, index++); + GateRef a1 = acc_.GetValueIn(gate, index++); + GateRef a2 = acc_.GetValueIn(gate, index++); + + std::vector args; + args.reserve(index); + args.emplace_back(thisObj); + args.emplace_back(a0); + args.emplace_back(a1); + args.emplace_back(a2); + GateRef result = LowerCallRuntime(glue, gate, RTSTUB_ID(LocaleCompare), args); + ReplaceHirWithValue(gate, result); +} + GateRef BuiltinLowering::LowerCallTargetCheck(Environment *env, GateRef gate) { builder_.SetEnvironment(env); diff --git a/ecmascript/compiler/builtins_lowering.h b/ecmascript/compiler/builtins_lowering.h index d692417625..2853ed1950 100644 --- a/ecmascript/compiler/builtins_lowering.h +++ b/ecmascript/compiler/builtins_lowering.h @@ -30,12 +30,17 @@ public: GateRef LowerCallTargetCheck(Environment *env, GateRef gate); void LowerTypedSqrt(GateRef gate); GateRef CheckPara(GateRef gate, GateRef funcCheck); + void LowerTypedLocaleCompare(GateRef gate); + private: void LowerTypedTrigonometric(GateRef gate, BuiltinsStubCSigns::ID id); GateRef TypedTrigonometric(GateRef gate, BuiltinsStubCSigns::ID id); GateRef IntToTaggedIntPtr(GateRef x); void LowerTypedAbs(GateRef gate); GateRef TypedAbs(GateRef gate); + GateRef LowerCallRuntime(GateRef glue, GateRef gate, int index, const std::vector &args, + bool useLabel = false); + void ReplaceHirWithValue(GateRef hirGate, GateRef value, bool noThrow = false); Circuit *circuit_ {nullptr}; CircuitBuilder builder_; diff --git a/ecmascript/compiler/circuit_builder-inl.h b/ecmascript/compiler/circuit_builder-inl.h index 285d808a17..8888844d0e 100644 --- a/ecmascript/compiler/circuit_builder-inl.h +++ b/ecmascript/compiler/circuit_builder-inl.h @@ -1085,6 +1085,20 @@ inline GateRef CircuitBuilder::TypedCallBuiltin(GateRef hirGate, GateRef x, Buil return numberMathOp; } +inline GateRef CircuitBuilder::TypedCallThis3Builtin(GateRef hirGate, GateRef thisObj, GateRef a0, GateRef a1, + GateRef a2, BuiltinsStubCSigns::ID id) +{ + auto currentLabel = env_->GetCurrentLabel(); + auto currentControl = currentLabel->GetControl(); + auto currentDepend = currentLabel->GetDepend(); + GateRef idGate = Int8(static_cast(id)); + auto numberMathOp = TypedCallOperator(hirGate, MachineType::I64, + {currentControl, currentDepend, thisObj, a0, a1, a2, idGate}); + currentLabel->SetControl(numberMathOp); + currentLabel->SetDepend(numberMathOp); + return numberMathOp; +} + inline GateRef CircuitBuilder::GetMethodId(GateRef func) { GateRef method = GetMethodFromFunction(func); diff --git a/ecmascript/compiler/circuit_builder.h b/ecmascript/compiler/circuit_builder.h index a88c8b5dd3..e36dc694bc 100644 --- a/ecmascript/compiler/circuit_builder.h +++ b/ecmascript/compiler/circuit_builder.h @@ -261,6 +261,8 @@ public: GateRef DeoptCheck(GateRef condition, GateRef frameState, DeoptType type); GateRef TypedCallOperator(GateRef hirGate, MachineType type, const std::initializer_list& args); inline GateRef TypedCallBuiltin(GateRef hirGate, GateRef x, BuiltinsStubCSigns::ID id); + inline GateRef TypedCallThis3Builtin(GateRef hirGate, GateRef thisObj, GateRef a0, GateRef a1, GateRef a2, + BuiltinsStubCSigns::ID id); GateRef TypeConvert(MachineType type, GateType typeFrom, GateType typeTo, const std::vector& inList); GateRef AddWithOverflow(GateRef left, GateRef right); GateRef SubWithOverflow(GateRef left, GateRef right); diff --git a/ecmascript/compiler/ts_hcr_lowering.cpp b/ecmascript/compiler/ts_hcr_lowering.cpp index 47866c4a7d..e87f82eedc 100644 --- a/ecmascript/compiler/ts_hcr_lowering.cpp +++ b/ecmascript/compiler/ts_hcr_lowering.cpp @@ -880,15 +880,26 @@ void TSHCRLowering::SpeculateCallBuiltin(GateRef gate, GateRef func, GateRef a0, acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), result); } -BuiltinsStubCSigns::ID TSHCRLowering::GetBuiltinId(GateRef func) +void TSHCRLowering::SpeculateCallThis3Builtin(GateRef gate, BuiltinsStubCSigns::ID id) +{ + GateRef thisObj = acc_.GetValueIn(gate, 0); + GateRef a0 = acc_.GetValueIn(gate, 1); // 1: the first-para + GateRef a1 = acc_.GetValueIn(gate, 2); // 2: the third-para + GateRef a2 = acc_.GetValueIn(gate, 3); // 3: the fourth-para + GateRef result = builder_.TypedCallThis3Builtin(gate, thisObj, a0, a1, a2, id); + + acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result); +} + +BuiltinsStubCSigns::ID TSHCRLowering::GetBuiltinId(BuiltinTypeId id, GateRef func) { GateType funcType = acc_.GetGateType(func); - if (!tsManager_->IsBuiltinMath(funcType)) { + if (!tsManager_->IsBuiltinObject(id, funcType)) { return BuiltinsStubCSigns::ID::NONE; } std::string name = tsManager_->GetFuncName(funcType); - BuiltinsStubCSigns::ID id = BuiltinsStubCSigns::GetBuiltinId(name); - return id; + BuiltinsStubCSigns::ID stubId = BuiltinsStubCSigns::GetBuiltinId(name); + return stubId; } void TSHCRLowering::CheckCallTargetAndLowerCall(GateRef gate, GateRef func, GlobalTSTypeRef funcGt, @@ -971,7 +982,7 @@ void TSHCRLowering::LowerTypedCallArg1(GateRef gate) } GateRef a0Value = acc_.GetValueIn(gate, 0); GateType a0Type = acc_.GetGateType(a0Value); - BuiltinsStubCSigns::ID id = GetBuiltinId(func); + BuiltinsStubCSigns::ID id = GetBuiltinId(BuiltinTypeId::MATH, func); if (id != BuiltinsStubCSigns::ID::NONE && a0Type.IsNumberType()) { AddProfiling(gate); SpeculateCallBuiltin(gate, func, a0Value, id); @@ -1106,7 +1117,7 @@ void TSHCRLowering::LowerTypedCallthis1(GateRef gate) GateRef a0 = acc_.GetValueIn(gate, 1); // 1:parameter index GateType a0Type = acc_.GetGateType(a0); GateRef func = acc_.GetValueIn(gate, 2); // 2:function - BuiltinsStubCSigns::ID id = GetBuiltinId(func); + BuiltinsStubCSigns::ID id = GetBuiltinId(BuiltinTypeId::MATH, func); if (id != BuiltinsStubCSigns::ID::NONE && a0Type.IsNumberType()) { AddProfiling(gate); SpeculateCallBuiltin(gate, func, a0, id); @@ -1138,6 +1149,13 @@ void TSHCRLowering::LowerTypedCallthis3(GateRef gate) // 5: number of value inputs ASSERT(acc_.GetNumValueIn(gate) == 5); GateRef func = acc_.GetValueIn(gate, 4); // 4: func + BuiltinsStubCSigns::ID id = GetBuiltinId(BuiltinTypeId::STRING, func); + if (id == BuiltinsStubCSigns::ID::LocaleCompare) { + AddProfiling(gate); + SpeculateCallThis3Builtin(gate, id); + return; + } + if (!CanOptimizeAsFastCall(func)) { return; } diff --git a/ecmascript/compiler/ts_hcr_lowering.h b/ecmascript/compiler/ts_hcr_lowering.h index 78d13c61c3..502482d4ba 100644 --- a/ecmascript/compiler/ts_hcr_lowering.h +++ b/ecmascript/compiler/ts_hcr_lowering.h @@ -133,7 +133,8 @@ private: void SpeculateNumber(GateRef gate); void SpeculateConditionJump(GateRef gate, bool flag); void SpeculateCallBuiltin(GateRef gate, GateRef func, GateRef a0, BuiltinsStubCSigns::ID Op); - BuiltinsStubCSigns::ID GetBuiltinId(GateRef func); + void SpeculateCallThis3Builtin(GateRef gate, BuiltinsStubCSigns::ID id); + BuiltinsStubCSigns::ID GetBuiltinId(BuiltinTypeId id, GateRef func); void DeleteConstDataIfNoUser(GateRef gate); void AddProfiling(GateRef gate); diff --git a/ecmascript/global_env_constants-inl.h b/ecmascript/global_env_constants-inl.h index cc984f747d..c708004eec 100644 --- a/ecmascript/global_env_constants-inl.h +++ b/ecmascript/global_env_constants-inl.h @@ -74,6 +74,7 @@ inline uintptr_t GlobalEnvConstants::GetGlobalConstantAddr(ConstantIndex index) GLOBAL_ENV_CONSTANT_SPECIAL(DECL_GET_IMPL) // NOLINT(readability-const-return-type) GLOBAL_ENV_CONSTANT_CONSTANT(DECL_GET_IMPL) // NOLINT(readability-const-return-type) GLOBAL_ENV_CONSTANT_ACCESSOR(DECL_GET_IMPL) // NOLINT(readability-const-return-type) + GLOBAL_ENV_CACHES(DECL_GET_IMPL) // NOLINT(readability-const-return-type) #undef DECL_GET_IMPL // clang-format on } // namespace panda::ecmascript diff --git a/ecmascript/global_env_constants.cpp b/ecmascript/global_env_constants.cpp index a1087a0d17..47b2ae39d1 100644 --- a/ecmascript/global_env_constants.cpp +++ b/ecmascript/global_env_constants.cpp @@ -79,6 +79,7 @@ void GlobalEnvConstants::Init(JSThread *thread, JSHClass *hClass) { InitRootsClass(thread, hClass); InitGlobalConstant(thread); + InitGlobalCaches(); } void GlobalEnvConstants::InitRootsClass(JSThread *thread, JSHClass *hClass) @@ -609,6 +610,19 @@ void GlobalEnvConstants::InitGlobalConstant(JSThread *thread) InitClassConstructorOptimizedClass(factory); } +void GlobalEnvConstants::InitGlobalCaches() +{ + SetConstant(ConstantIndex::CACHED_JSCOLLATOR_LOCALES_INDEX, JSTaggedValue::Undefined()); +} + +void GlobalEnvConstants::SetCachedLocales(JSTaggedValue value) +{ + JSTaggedValue cached = GetCachedJSCollatorLocales(); + if (cached.IsUndefined()) { + SetConstant(ConstantIndex::CACHED_JSCOLLATOR_LOCALES_INDEX, value); + } +} + void GlobalEnvConstants::InitJSAPIContainers() { for (size_t i = GetJSAPIContainersBegin(); i <= GetJSAPIContainersEnd(); i++) { diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h index 096cd77410..22e0cf14b0 100644 --- a/ecmascript/global_env_constants.h +++ b/ecmascript/global_env_constants.h @@ -451,7 +451,9 @@ class ObjectFactory; V(JSTaggedValue, FunctionPrototypeAccessor, FUNCTION_PROTOTYPE_ACCESSOR, ecma_roots_accessor) \ V(JSTaggedValue, FunctionNameAccessor, FUNCTION_NAME_ACCESSOR, ecma_roots_accessor) \ V(JSTaggedValue, ArrayLengthAccessor, ARRAY_LENGTH_ACCESSOR, ecma_roots_accessor) -/* RealmConstant */ + +#define GLOBAL_ENV_CACHES(V) \ + V(JSTaggedValue, CachedJSCollatorLocales, CACHED_JSCOLLATOR_LOCALES_INDEX, cachedCollatorLocales) // ConstantIndex used for explicit visit each constant. enum class ConstantIndex : size_t { @@ -459,6 +461,7 @@ enum class ConstantIndex : size_t { #define INDEX_FILTER(Type, Name, Index, Desc) Index, GLOBAL_ENV_CONSTANT_CLASS(INDEX_FILTER) GLOBAL_ENV_CONSTANT_SPECIAL(INDEX_FILTER) GLOBAL_ENV_CONSTANT_CONSTANT(INDEX_FILTER) GLOBAL_ENV_CONSTANT_ACCESSOR(INDEX_FILTER) + GLOBAL_ENV_CACHES(INDEX_FILTER) #undef INDEX_FILTER CONSTATNT_COUNT, @@ -492,11 +495,14 @@ public: void InitGlobalConstantSpecial(JSThread *thread); void InitGlobalConstant(JSThread *thread); + void InitGlobalCaches(); void InitJSAPIContainers(); void InitSpecialForSnapshot(); void InitClassConstructorOptimizedClass(ObjectFactory *factory); + void SetCachedLocales(JSTaggedValue value); + void SetConstant(ConstantIndex index, JSTaggedValue value); template @@ -514,6 +520,7 @@ public: GLOBAL_ENV_CONSTANT_SPECIAL(DECL_GET) GLOBAL_ENV_CONSTANT_CONSTANT(DECL_GET) GLOBAL_ENV_CONSTANT_ACCESSOR(DECL_GET) + GLOBAL_ENV_CACHES(DECL_GET) #undef DECL_GET void VisitRangeSlot(const RootRangeVisitor &visitor) diff --git a/ecmascript/js_collator.cpp b/ecmascript/js_collator.cpp index 31526297b9..363316fa5e 100644 --- a/ecmascript/js_collator.cpp +++ b/ecmascript/js_collator.cpp @@ -37,12 +37,23 @@ const std::map JSCollator::uColAttributeVal {CaseFirstOption::UNDEFINED, UCOL_OFF} }; -JSHandle JSCollator::GetAvailableLocales(JSThread *thread) +JSHandle JSCollator::GetAvailableLocales(JSThread *thread, bool enableLocaleCache) { const char *key = nullptr; const char *path = JSCollator::uIcuDataColl.c_str(); + // key and path are const, so we can cache the result + if (enableLocaleCache) { + JSHandle cachedLocales = thread->GlobalConstants()->GetHandledCachedJSCollatorLocales(); + if (cachedLocales->IsHeapObject()) { + return JSHandle(cachedLocales); + } + } std::vector availableStringLocales = intl::LocaleHelper::GetAvailableLocales(thread, key, path); JSHandle availableLocales = JSLocale::ConstructLocaleList(thread, availableStringLocales); + if (enableLocaleCache) { + GlobalEnvConstants *constants = const_cast(thread->GlobalConstants()); + constants->SetCachedLocales(availableLocales.GetTaggedValue()); + } return availableLocales; } @@ -68,7 +79,8 @@ JSHandle JSCollator::InitializeCollator(JSThread *thread, const JSHandle &collator, const JSHandle &locales, const JSHandle &options, - bool forIcuCache) + bool forIcuCache, + bool enableLocaleCache) { EcmaVM *ecmaVm = thread->GetEcmaVM(); ObjectFactory *factory = ecmaVm->GetFactory(); @@ -143,7 +155,7 @@ JSHandle JSCollator::InitializeCollator(JSThread *thread, if (requestedLocales->GetLength() == 0) { availableLocales = factory->EmptyArray(); } else { - availableLocales = GetAvailableLocales(thread); + availableLocales = GetAvailableLocales(thread, enableLocaleCache); } ResolvedLocale r = JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys); @@ -460,4 +472,39 @@ JSTaggedValue JSCollator::CompareStrings(const icu::Collator *icuCollator, const return JSTaggedValue(result); } + +JSTaggedValue JSCollator::FastCompareStrings(JSThread *thread, const icu::Collator *icuCollator, + const JSHandle &string1, + const JSHandle &string2) +{ + if (*string1 == *string2) { + return JSTaggedValue(UCollationResult::UCOL_EQUAL); + } + + auto flatString1 = JSHandle(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), string1)); + auto flatString2 = JSHandle(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), string2)); + + UCollationResult result; + UErrorCode status = U_ZERO_ERROR; + { + DISALLOW_GARBAGE_COLLECTION; + CString str1 = ConvertToString(*flatString1, StringConvertedUsage::LOGICOPERATION); + icu::StringPiece stringPiece1(str1.c_str()); + if (!stringPiece1.empty()) { + CString str2 = ConvertToString(*flatString2, StringConvertedUsage::LOGICOPERATION); + icu::StringPiece stringPiece2(str2.c_str()); + if (!stringPiece2.empty()) { + result = icuCollator->compareUTF8(stringPiece1, stringPiece2, status); + return JSTaggedValue(result); + } + } + + icu::UnicodeString uString1 = EcmaStringToUString(flatString1); + icu::UnicodeString uString2 = EcmaStringToUString(flatString2); + + result = icuCollator->compare(uString1, uString2, status); + ASSERT(U_SUCCESS(status)); + } + return JSTaggedValue(result); +} } // namespace panda::ecmascript diff --git a/ecmascript/js_collator.h b/ecmascript/js_collator.h index f265c9fd6f..98c7882413 100644 --- a/ecmascript/js_collator.h +++ b/ecmascript/js_collator.h @@ -84,18 +84,23 @@ public: static JSHandle InitializeCollator(JSThread *thread, const JSHandle &collator, const JSHandle &locales, const JSHandle &options, - bool forIcuCache = false); + bool forIcuCache = false, + bool enableLocaleCache = false); static icu::Collator *GetCachedIcuCollator(JSThread *thread, const JSHandle &locales); // 11.3.4 Intl.Collator.prototype.resolvedOptions () static JSHandle ResolvedOptions(JSThread *thread, const JSHandle &collator); - static JSHandle GetAvailableLocales(JSThread *thread); + static JSHandle GetAvailableLocales(JSThread *thread, bool enableLocaleCache = false); static JSTaggedValue CompareStrings(const icu::Collator *icuCollator, const JSHandle &string1, const JSHandle &string2); + static JSTaggedValue FastCompareStrings(JSThread *thread, const icu::Collator *icuCollator, + const JSHandle &string1, + const JSHandle &string2); + private: static CaseFirstOption StringToCaseFirstOption(const std::string &str); diff --git a/ecmascript/js_tagged_value-inl.h b/ecmascript/js_tagged_value-inl.h index c7fbfa1601..0d5be6ffa8 100644 --- a/ecmascript/js_tagged_value-inl.h +++ b/ecmascript/js_tagged_value-inl.h @@ -276,7 +276,7 @@ inline JSHandle JSTaggedValue::RequireObjectCoercible(JSThread *t const JSHandle &tagged, const char *message) { - if (tagged->IsUndefined() || tagged->IsNull()) { + if (tagged->IsUndefinedOrNull()) { THROW_TYPE_ERROR_AND_RETURN(thread, message, JSHandle(thread, JSTaggedValue::Exception())); } return tagged; diff --git a/ecmascript/stubs/runtime_stubs.cpp b/ecmascript/stubs/runtime_stubs.cpp index 294edf53ff..6da8f56a45 100644 --- a/ecmascript/stubs/runtime_stubs.cpp +++ b/ecmascript/stubs/runtime_stubs.cpp @@ -54,6 +54,14 @@ #include "ecmascript/tagged_node.h" #include "ecmascript/ts_types/ts_manager.h" #include "libpandafile/bytecode_instruction-inl.h" +#ifdef ARK_SUPPORT_INTL +#include "ecmascript/js_collator.h" +#include "ecmascript/js_locale.h" +#else +#ifndef ARK_NOT_SUPPORT_INTL_GLOBAL +#include "ecmascript/intl/global_intl_helper.h" +#endif +#endif namespace panda::ecmascript { #if defined(__clang__) @@ -2285,7 +2293,14 @@ JSTaggedValue RuntimeStubs::CallBoundFunction(EcmaRuntimeCallInfo *info) THROW_TYPE_ERROR_AND_RETURN(thread, "class constructor cannot called without 'new'", JSTaggedValue::Exception()); } - + if (thread->IsPGOProfilerEnable()) { + ECMAObject *callTarget = reinterpret_cast(targetFunc.GetTaggedValue().GetTaggedObject()); + ASSERT(callTarget != nullptr); + Method *method = callTarget->GetCallTarget(); + if (!method->IsNativeWithCallField()) { + thread->GetEcmaVM()->GetPGOProfiler()->ProfileCall(targetFunc.GetTaggedType()); + } + } JSHandle boundArgs(thread, boundFunc->GetBoundArguments()); const int32_t boundLength = static_cast(boundArgs->GetLength()); const int32_t argsLength = static_cast(info->GetArgsNumber()) + boundLength; @@ -2338,6 +2353,65 @@ DEF_RUNTIME_STUBS(AotInlineTrace) return JSTaggedValue::Undefined().GetRawData(); } +DEF_RUNTIME_STUBS(LocaleCompare) +{ + RUNTIME_STUBS_HEADER(LocaleCompare); + + JSHandle thisTag = GetHArg(argv, argc, 0); // 0: means the zeroth parameter + JSHandle thatTag = GetHArg(argv, argc, 1); // 1: means the first parameter + JSHandle locales = GetHArg(argv, argc, 2); // 2: means the second parameter + JSHandle options = GetHArg(argv, argc, 3); // 3: means the third parameter + + JSHandle thisObj(JSTaggedValue::RequireObjectCoercible(thread, thisTag)); + [[maybe_unused]] JSHandle thisHandle = JSTaggedValue::ToString(thread, thisObj); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception().GetRawData()); + [[maybe_unused]] JSHandle thatHandle = JSTaggedValue::ToString(thread, thatTag); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception().GetRawData()); + + [[maybe_unused]] bool cacheable = options->IsUndefined() && (locales->IsUndefined() || locales->IsString()); +#ifdef ARK_SUPPORT_INTL + if (cacheable) { + auto collator = JSCollator::GetCachedIcuCollator(thread, locales); + if (collator != nullptr) { + JSTaggedValue result = JSCollator::CompareStrings(collator, thisHandle, thatHandle); + return result.GetRawData(); + } + } + EcmaVM *ecmaVm = thread->GetEcmaVM(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle ctor = ecmaVm->GetGlobalEnv()->GetCollatorFunction(); + JSHandle collator = + JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(ctor))); + JSHandle initCollator = + JSCollator::InitializeCollator(thread, collator, locales, options, cacheable, true); + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception().GetRawData()); + icu::Collator *icuCollator = nullptr; + if (cacheable) { + icuCollator = JSCollator::GetCachedIcuCollator(thread, locales); + ASSERT(icuCollator != nullptr); + } else { + icuCollator = initCollator->GetIcuCollator(); + } + JSTaggedValue result = JSCollator::FastCompareStrings(thread, icuCollator, thisHandle, thatHandle); + return result.GetRawData(); +#else +#ifdef ARK_NOT_SUPPORT_INTL_GLOBAL + ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "LocaleCompare"); +#else + intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::Collator); + auto collator = gh.GetGlobalObject(thread, + locales, options, intl::GlobalFormatterType::Collator, cacheable); + if (collator == nullptr) { + LOG_ECMA(ERROR) << "BuiltinsString::LocaleCompare:collator is nullptr"; + } + ASSERT(collator != nullptr); + auto result = collator->Compare(EcmaStringAccessor(thisHandle).ToStdString(), + EcmaStringAccessor(thatHandle).ToStdString()); + return JSTaggedValue(result).GetRawData(); +#endif +#endif +} + void RuntimeStubs::StartCallTimer(uintptr_t argGlue, JSTaggedType func, bool isAot) { auto thread = JSThread::GlueToJSThread(argGlue); diff --git a/ecmascript/stubs/runtime_stubs.h b/ecmascript/stubs/runtime_stubs.h index 3dddb9d7df..09d79277c9 100644 --- a/ecmascript/stubs/runtime_stubs.h +++ b/ecmascript/stubs/runtime_stubs.h @@ -304,7 +304,8 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co V(SlowFlattenString) \ V(NotifyConcurrentResult) \ V(OtherToNumber) \ - V(AotInlineTrace) + V(AotInlineTrace) \ + V(LocaleCompare) #define RUNTIME_STUB_LIST(V) \ RUNTIME_ASM_STUB_LIST(V) \ diff --git a/ecmascript/ts_types/lib_ark_builtins.d.ts b/ecmascript/ts_types/lib_ark_builtins.d.ts index 9f95cf7306..223063428e 100644 --- a/ecmascript/ts_types/lib_ark_builtins.d.ts +++ b/ecmascript/ts_types/lib_ark_builtins.d.ts @@ -613,7 +613,7 @@ declare class String extends Object { lastIndexOf(searchString: string, position?: number): number; - localeCompare(that: string): number; + localeCompare(that: string, locale?: string, options?: any): number; match(regexp: string | RegExp): RegExpMatchArray | null; diff --git a/ecmascript/ts_types/ts_manager.cpp b/ecmascript/ts_types/ts_manager.cpp index 3c1b42bb24..90e8026215 100644 --- a/ecmascript/ts_types/ts_manager.cpp +++ b/ecmascript/ts_types/ts_manager.cpp @@ -1330,7 +1330,7 @@ JSHandle TSManager::GenerateExportTableFromLiteral(const JSPandaFil return typeOfExportedSymbols; } -bool TSManager::IsBuiltinMath(kungfu::GateType funcType) const +bool TSManager::IsBuiltinObject(BuiltinTypeId id, kungfu::GateType funcType) const { DISALLOW_GARBAGE_COLLECTION; GlobalTSTypeRef funcGT = funcType.GetGTRef(); @@ -1339,17 +1339,17 @@ bool TSManager::IsBuiltinMath(kungfu::GateType funcType) const } if (IsBuiltinsDTSEnabled()) { - uint32_t idx = static_cast(BuiltinTypeId::MATH); + uint32_t idx = static_cast(id); const JSPandaFile *builtinPandaFile = GetBuiltinPandaFile(); - uint32_t mathOffset = GetBuiltinOffset(idx); - bool hasCreatedGT = HasCreatedGT(builtinPandaFile, mathOffset); + uint32_t builtinOffset = GetBuiltinOffset(idx); + bool hasCreatedGT = HasCreatedGT(builtinPandaFile, builtinOffset); if (hasCreatedGT) { JSHandle funcTsType = GetTSType(funcGT); ASSERT(funcTsType->IsTSFunctionType()); JSHandle functionType = JSHandle(funcTsType); auto name = functionType->GetName(); - auto gt = GetGTFromOffset(builtinPandaFile, mathOffset); + auto gt = GetGTFromOffset(builtinPandaFile, builtinOffset); auto tsType = GetTSType(gt); ASSERT(tsType->IsTSClassType()); JSHandle classType(tsType); @@ -1358,8 +1358,16 @@ bool TSManager::IsBuiltinMath(kungfu::GateType funcType) const TSObjLayoutInfo *itLayout = TSObjLayoutInfo::Cast(layout.GetTaggedObject()); int index = itLayout->GetElementIndexByKey(name); if (index != -1) { - auto mathFuncGt = GlobalTSTypeRef(itLayout->GetTypeId(index).GetInt()); - return mathFuncGt == funcGT; + auto builtinFuncGt = GlobalTSTypeRef(itLayout->GetTypeId(index).GetInt()); + return builtinFuncGt == funcGT; + } + JSHandle prototypeType(thread_, classType->GetPrototypeType()); + JSTaggedValue prototypeLayout = prototypeType->GetObjLayoutInfo(); + TSObjLayoutInfo *pPrototypeLayout = TSObjLayoutInfo::Cast(prototypeLayout.GetTaggedObject()); + index = pPrototypeLayout->GetElementIndexByKey(name); + if (index != -1) { + auto builtinFuncGt = GlobalTSTypeRef(pPrototypeLayout->GetTypeId(index).GetInt()); + return builtinFuncGt == funcGT; } } } diff --git a/ecmascript/ts_types/ts_manager.h b/ecmascript/ts_types/ts_manager.h index 0c3b28a757..e9f9d41252 100644 --- a/ecmascript/ts_types/ts_manager.h +++ b/ecmascript/ts_types/ts_manager.h @@ -553,7 +553,7 @@ public: bool PUBLIC_API IsBuiltin(kungfu::GateType funcType) const; - bool PUBLIC_API IsBuiltinMath(kungfu::GateType funcType) const; + bool PUBLIC_API IsBuiltinObject(BuiltinTypeId id, kungfu::GateType funcType) const; inline const JSPandaFile *GetBuiltinPandaFile() const {