From cea7ac93e1841ef39309076a4eb078ff4481d207 Mon Sep 17 00:00:00 2001 From: zhouguangyuan Date: Mon, 9 Sep 2024 22:54:00 +0800 Subject: [PATCH] IR for Intl.Collator.prototype.resolvedOptions Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/IAQ2T7 Signed-off-by: ZhouGuangyuan Change-Id: I01bfcc0e7af40714514ab22f643bae71f98b8947 --- ecmascript/builtins/builtins.cpp | 3 +- ecmascript/compiler/BUILD.gn | 1 + .../builtins/builtins_call_signature.h | 6 +- .../builtins_collator_stub_builder.cpp | 238 ++++++++++++++++++ .../builtins/builtins_collator_stub_builder.h | 48 ++++ .../compiler/builtins/builtins_stubs.cpp | 1 + ecmascript/compiler/stub_builder-inl.h | 6 + ecmascript/compiler/stub_builder.h | 1 + ecmascript/stubs/runtime_stub_list.h | 3 +- ecmascript/stubs/runtime_stubs.cpp | 16 ++ test/moduletest/intl/expect_output.txt | 1 + test/moduletest/intl/intl.js | 9 + 12 files changed, 330 insertions(+), 3 deletions(-) create mode 100644 ecmascript/compiler/builtins/builtins_collator_stub_builder.cpp create mode 100644 ecmascript/compiler/builtins/builtins_collator_stub_builder.h diff --git a/ecmascript/builtins/builtins.cpp b/ecmascript/builtins/builtins.cpp index ee0b8148ef..1b90a8e51a 100644 --- a/ecmascript/builtins/builtins.cpp +++ b/ecmascript/builtins/builtins.cpp @@ -3465,7 +3465,8 @@ void Builtins::InitializeCollator(const JSHandle &env) SetAccessor(collatorPrototype, thread_->GlobalConstants()->GetHandledCompareString(), compareGetter, compareSetter); // 11.3.4 Intl.Collator.prototype.resolvedOptions () - SetFunction(env, collatorPrototype, "resolvedOptions", Collator::ResolvedOptions, FunctionLength::ZERO); + SetFunction(env, collatorPrototype, "resolvedOptions", Collator::ResolvedOptions, FunctionLength::ZERO, + kungfu::BuiltinsStubCSigns::CollatorResolvedOptions); } void Builtins::InitializePluralRules(const JSHandle &env) diff --git a/ecmascript/compiler/BUILD.gn b/ecmascript/compiler/BUILD.gn index 6253f5eff8..35d69574e5 100644 --- a/ecmascript/compiler/BUILD.gn +++ b/ecmascript/compiler/BUILD.gn @@ -110,6 +110,7 @@ libark_jsoptimizer_sources = [ "bc_call_signature.cpp", "builtins/builtins_array_stub_builder.cpp", "builtins/builtins_call_signature.cpp", + "builtins/builtins_collator_stub_builder.cpp", "builtins/builtins_collection_stub_builder.cpp", "builtins/builtins_dataview_stub_builder.cpp", "builtins/builtins_function_stub_builder.cpp", diff --git a/ecmascript/compiler/builtins/builtins_call_signature.h b/ecmascript/compiler/builtins/builtins_call_signature.h index 027679c4c8..1293834c10 100644 --- a/ecmascript/compiler/builtins/builtins_call_signature.h +++ b/ecmascript/compiler/builtins/builtins_call_signature.h @@ -46,7 +46,8 @@ namespace panda::ecmascript::kungfu { BUILTINS_WITH_NUMBER_STUB_BUILDER(T) \ BUILTINS_WITH_TYPEDARRAY_STUB_BUILDER(V) \ BUILTINS_WITH_DATAVIEW_STUB_BUILDER(K) \ - BUILTINS_WITH_REFLECT_STUB_BUILDER(T) + BUILTINS_WITH_REFLECT_STUB_BUILDER(T) \ + BUILTINS_WITH_COLLATOR_STUB_BUILDER(V) #define BUILTINS_WITH_STRING_STUB_BUILDER(V) \ V(CharAt, String, Hole()) \ @@ -156,6 +157,9 @@ namespace panda::ecmascript::kungfu { #define BUILTINS_WITH_REFLECT_STUB_BUILDER(V) \ V(Get, Reflect, Undefined()) +#define BUILTINS_WITH_COLLATOR_STUB_BUILDER(V) \ + V(ResolvedOptions, Collator, Undefined()) + #define BUILTINS_WITH_TYPEDARRAY_STUB_BUILDER(V) \ V(Reverse, TypedArray, Undefined()) \ V(LastIndexOf, TypedArray, Undefined()) \ diff --git a/ecmascript/compiler/builtins/builtins_collator_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_collator_stub_builder.cpp new file mode 100644 index 0000000000..403e16abda --- /dev/null +++ b/ecmascript/compiler/builtins/builtins_collator_stub_builder.cpp @@ -0,0 +1,238 @@ +/* +* Copyright (c) 2024 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. + */ + +#include "ecmascript/compiler/builtins/builtins_collator_stub_builder.h" + +#include "ecmascript/compiler/new_object_stub_builder.h" +#include "ecmascript/js_collator.h" + +namespace panda::ecmascript::kungfu { +void BuiltinsCollatorStubBuilder::ResolvedOptions(GateRef glue, GateRef thisValue, [[maybe_unused]] GateRef numArgs, + Variable* res, Label* exit, + Label* slowPath) +{ + auto env = GetEnvironment(); + Label entryPass(env); + env->SubCfgEntry(&entryPass); + + Label isHeapObject(env); + Label isJsCollator(env); + + BRANCH_LIKELY(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); + Bind(&isHeapObject); + BRANCH_LIKELY(IsJSCollator(thisValue), &isJsCollator, slowPath); + + Bind(&isJsCollator); + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); + GateRef funCtor = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, GlobalEnv::OBJECT_FUNCTION_INDEX); + + NewObjectStubBuilder newObjectStubBuilder(this); + GateRef initialOptions = newObjectStubBuilder.NewJSObjectByConstructor(glue, funCtor, funCtor); + DEFVARIABLE(options, VariableType::JS_ANY(), initialOptions); + + // [[Locale]] + GateRef localeKey = GetGlobalConstantValue(VariableType::JS_ANY(), glue, ConstantIndex::LOCALE_STRING_INDEX); + GateRef locale = Load(VariableType::JS_ANY(), thisValue, IntPtr(JSCollator::LOCALE_OFFSET)); + CreateDataPropertyOrThrow(glue, *options, localeKey, locale); + ReturnExceptionIfAbruptCompletion(glue); + + // [[Usage]] + GateRef usageKey = GetGlobalConstantValue(VariableType::JS_ANY(), glue, ConstantIndex::USAGE_INDEX); + GateRef usage = GetBitField(thisValue); + usage = UsageOptionsToEcmaString(glue, usage); + CreateDataProperty(glue, *options, usageKey, usage); + + // [[Sensitivity]] + GateRef sensitivityKey = GetGlobalConstantValue(VariableType::JS_ANY(), glue, ConstantIndex::SENSITIVITY_INDEX); + GateRef sensitivity = GetBitField(thisValue); + sensitivity = SensitivityOptionsToEcmaString(glue, sensitivity); + CreateDataProperty(glue, *options, sensitivityKey, sensitivity); + + // [[IgnorePunctuation]] + GateRef ignorePunctuationKey = GetGlobalConstantValue(VariableType::JS_ANY(), glue, + ConstantIndex::IGNORE_PUNCTUATION_INDEX); + GateRef ignorePunctuation = GetBitField(thisValue); + ignorePunctuation = BooleanToTaggedBooleanPtr(TruncInt32ToInt1(ignorePunctuation)); + CreateDataProperty(glue, *options, ignorePunctuationKey, ignorePunctuation); + + // [[Collation]] + Label undefined(env); + Label notUndefined(env); + GateRef collation = Load(VariableType::JS_ANY(), thisValue, IntPtr(JSCollator::COLLATION_OFFSET)); + GateRef collationKey = GetGlobalConstantValue(VariableType::JS_ANY(), glue, + ConstantIndex::COLLATION_STRING_CLASS_INDEX); + DEFVARIABLE(collationVar, VariableType::JS_ANY(), collation); + BRANCH_NO_WEIGHT(TaggedIsUndefined(*collationVar), &undefined, ¬Undefined); + Bind(&undefined); + { + collationVar = CallRuntime(glue, RTSTUB_ID(GetCollationValueFromIcuCollator), {thisValue}); + Jump(¬Undefined); + } + Bind(¬Undefined); + CreateDataProperty(glue, *options, collationKey, *collationVar); + + // [[Numeric]] + GateRef numericKey = GetGlobalConstantValue(VariableType::JS_ANY(), glue, + ConstantIndex::NUMERIC_STRING_CLASS_INDEX); + GateRef numeric = GetBitField(thisValue); + numeric = BooleanToTaggedBooleanPtr(TruncInt32ToInt1(numeric)); + CreateDataProperty(glue, *options, numericKey, numeric); + + // [[CaseFirst]] + GateRef caseFirstKey = GetGlobalConstantValue(VariableType::JS_ANY(), glue, + ConstantIndex::CASE_FIRST_STRING_CLASS_INDEX); + GateRef caseFirst = GetBitField(thisValue); + caseFirst = CaseFirstOptionsToEcmaString(glue, caseFirst); + CreateDataProperty(glue, *options, caseFirstKey, caseFirst); + res->WriteVariable(*options); + Jump(exit); + env->SubCfgExit(); +} + +template +GateRef BuiltinsCollatorStubBuilder::GetBitField(GateRef collator) +{ + GateRef bitFieldOffset = IntPtr(JSCollator::BIT_FIELD_OFFSET); + GateRef bitfield = Load(VariableType::INT32(), collator, bitFieldOffset); + GateRef bits = Int32And(Int32LSR(bitfield, Int32(BitType::START_BIT)), + Int32((1LU << BitType::SIZE) - 1)); + return bits; +} + +GateRef BuiltinsCollatorStubBuilder::UsageOptionsToEcmaString(GateRef glue, GateRef usage) +{ + auto env = GetEnvironment(); + Label subEntry(env); + env->SubCfgEntry(&subEntry); + Label exit(env); + DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); + + Label sort(env); + Label next(env); + Label search(env); + Label fatal(env); + BRANCH_NO_WEIGHT(Int32Equal(usage, Int32(static_cast(UsageOption::SORT))), &sort, &next) + Bind(&sort); + { + result = GetGlobalConstantValue(VariableType::JS_ANY(), glue, ConstantIndex::SORT_INDEX); + Jump(&exit); + } + + Bind(&next); + BRANCH_NO_WEIGHT(Int32Equal(usage, Int32(static_cast(UsageOption::SEARCH))), &search, &fatal) + Bind(&search); + { + result = GetGlobalConstantValue(VariableType::JS_ANY(), glue, ConstantIndex::SEARCH_INDEX); + Jump(&exit); + } + Bind(&fatal); + { + FatalPrint(glue, {Int32(GET_MESSAGE_STRING_ID(ThisBranchIsUnreachable))}); + Jump(&exit); + } + + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + +GateRef BuiltinsCollatorStubBuilder::SensitivityOptionsToEcmaString(GateRef glue, GateRef sensitivity) +{ + auto env = GetEnvironment(); + Label subEntry(env); + env->SubCfgEntry(&subEntry); + Label exit(env); + + DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); + Label undefined(env); + Label fatal(env); + + Label labels[SENSITIVITY_OPTION_CASES_NUM] = {Label(env), Label(env), Label(env), Label(env), undefined}; + Switch(ZExtInt32ToInt64(sensitivity), &fatal, SensitivityOptionCases, labels, SENSITIVITY_OPTION_CASES_NUM); + + for (size_t i = 0; i < SENSITIVITY_OPTION_CASES_NUM - 1; i++) { + Bind(&labels[i]); + result = GetGlobalConstantValue(VariableType::JS_ANY(), glue, SensitivityOptionIndexes[i]); + Jump(&exit); + } + + Bind(&undefined); + { + Jump(&exit); + } + Bind(&fatal); + { + FatalPrint(glue, {Int32(GET_MESSAGE_STRING_ID(ThisBranchIsUnreachable))}); + Jump(&exit); + } + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + +GateRef BuiltinsCollatorStubBuilder::CaseFirstOptionsToEcmaString(GateRef glue, GateRef sensitivity) +{ + auto env = GetEnvironment(); + Label subEntry(env); + env->SubCfgEntry(&subEntry); + Label exit(env); + + DEFVARIABLE(result, VariableType::JS_ANY(), Undefined()); + Label fatal(env); + + Label labels[CASE_FIRST_OPTION_NUM] = {Label(env), Label(env), Label(env), Label(env)}; + Switch(ZExtInt32ToInt64(sensitivity), &fatal, CaseFirstOptionCases, labels, CASE_FIRST_OPTION_NUM); + + for (size_t i = 0; i < CASE_FIRST_OPTION_NUM; i++) { + Bind(&labels[i]); + result = GetGlobalConstantValue(VariableType::JS_ANY(), glue, CaseFirstOptionIndexes[i]); + Jump(&exit); + } + + Bind(&fatal); + { + FatalPrint(glue, {Int32(GET_MESSAGE_STRING_ID(ThisBranchIsUnreachable))}); + Jump(&exit); + } + Bind(&exit); + auto ret = *result; + env->SubCfgExit(); + return ret; +} + +int64_t BuiltinsCollatorStubBuilder::SensitivityOptionCases[SENSITIVITY_OPTION_CASES_NUM] = { + static_cast(SensitivityOption::BASE), static_cast(SensitivityOption::ACCENT), + static_cast(SensitivityOption::CASE), static_cast(SensitivityOption::VARIANT), + static_cast(SensitivityOption::UNDEFINED) +}; + +ConstantIndex BuiltinsCollatorStubBuilder::SensitivityOptionIndexes[SENSITIVITY_OPTION_CASES_NUM - 1] = { + ConstantIndex::BASE_INDEX, ConstantIndex::ACCENT_INDEX, + ConstantIndex::CASE_INDEX, ConstantIndex::VARIANT_INDEX +}; + +int64_t BuiltinsCollatorStubBuilder::CaseFirstOptionCases[CASE_FIRST_OPTION_NUM] = { + static_cast(CaseFirstOption::UPPER), static_cast(CaseFirstOption::LOWER), + static_cast(CaseFirstOption::FALSE_OPTION), static_cast(CaseFirstOption::UNDEFINED) +}; + +ConstantIndex BuiltinsCollatorStubBuilder::CaseFirstOptionIndexes[CASE_FIRST_OPTION_NUM] = { + ConstantIndex::UPPER_INDEX, ConstantIndex::LOWER_INDEX, + ConstantIndex::FALSE_STRING_INDEX, ConstantIndex::UPPER_INDEX +}; +} diff --git a/ecmascript/compiler/builtins/builtins_collator_stub_builder.h b/ecmascript/compiler/builtins/builtins_collator_stub_builder.h new file mode 100644 index 0000000000..3711a97b47 --- /dev/null +++ b/ecmascript/compiler/builtins/builtins_collator_stub_builder.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023-2024 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. + */ + +#ifndef ECMASCRIPT_COMPILER_BUILTINS_COLLATOR_STUB_BUILDER_H +#define ECMASCRIPT_COMPILER_BUILTINS_COLLATOR_STUB_BUILDER_H +#include + +#include "ecmascript/compiler/builtins/builtins_stubs.h" + +namespace panda::ecmascript::kungfu { +class BuiltinsCollatorStubBuilder : public BuiltinsStubBuilder { +public: + explicit BuiltinsCollatorStubBuilder(BuiltinsStubBuilder *parent) : BuiltinsStubBuilder(parent) {} + ~BuiltinsCollatorStubBuilder() override = default; + NO_MOVE_SEMANTIC(BuiltinsCollatorStubBuilder); + NO_COPY_SEMANTIC(BuiltinsCollatorStubBuilder); + void GenerateCircuit() override {} + void ResolvedOptions(GateRef glue, GateRef thisValue, GateRef numArgs, Variable* res, Label *exit, Label *slowPath); + +private: + template + GateRef GetBitField(GateRef collator); + GateRef UsageOptionsToEcmaString(GateRef glue, GateRef usage); + GateRef SensitivityOptionsToEcmaString(GateRef glue, GateRef sensitivity); + GateRef CaseFirstOptionsToEcmaString(GateRef glue, GateRef sensitivity); + + static constexpr size_t SENSITIVITY_OPTION_CASES_NUM = 5; + static int64_t SensitivityOptionCases[SENSITIVITY_OPTION_CASES_NUM]; + static ConstantIndex SensitivityOptionIndexes[SENSITIVITY_OPTION_CASES_NUM - 1]; + + static constexpr size_t CASE_FIRST_OPTION_NUM = 4; + static int64_t CaseFirstOptionCases[CASE_FIRST_OPTION_NUM]; + static ConstantIndex CaseFirstOptionIndexes[CASE_FIRST_OPTION_NUM]; +}; +} // namespace panda::ecmascript::kungfu +#endif // ECMASCRIPT_COMPILER_BUILTINS_COLLATOR_STUB_BUILDER_H \ No newline at end of file diff --git a/ecmascript/compiler/builtins/builtins_stubs.cpp b/ecmascript/compiler/builtins/builtins_stubs.cpp index 023f5002b7..c040d11772 100644 --- a/ecmascript/compiler/builtins/builtins_stubs.cpp +++ b/ecmascript/compiler/builtins/builtins_stubs.cpp @@ -27,6 +27,7 @@ #include "ecmascript/compiler/builtins/builtins_typedarray_stub_builder.h" #include "ecmascript/compiler/builtins/containers_vector_stub_builder.h" #include "ecmascript/compiler/builtins/containers_stub_builder.h" +#include "ecmascript/compiler/builtins/builtins_collator_stub_builder.h" #include "ecmascript/compiler/builtins/builtins_collection_stub_builder.h" #include "ecmascript/compiler/builtins/builtins_object_stub_builder.h" #include "ecmascript/compiler/codegen/llvm/llvm_ir_builder.h" diff --git a/ecmascript/compiler/stub_builder-inl.h b/ecmascript/compiler/stub_builder-inl.h index 090aac383c..077b697710 100644 --- a/ecmascript/compiler/stub_builder-inl.h +++ b/ecmascript/compiler/stub_builder-inl.h @@ -1601,6 +1601,12 @@ inline GateRef StubBuilder::IsJSAPIArrayList(GateRef obj) return Int32Equal(objectType, Int32(static_cast(JSType::JS_API_ARRAY_LIST))); } +inline GateRef StubBuilder::IsJSCollator(GateRef obj) +{ + GateRef objectType = GetObjectType(LoadHClass(obj)); + return Int32Equal(objectType, Int32(static_cast(JSType::JS_COLLATOR))); +} + inline GateRef StubBuilder::IsJSObjectType(GateRef obj, JSType jsType) { auto env = GetEnvironment(); diff --git a/ecmascript/compiler/stub_builder.h b/ecmascript/compiler/stub_builder.h index 35e050372f..5f61c1765d 100644 --- a/ecmascript/compiler/stub_builder.h +++ b/ecmascript/compiler/stub_builder.h @@ -466,6 +466,7 @@ ShortcutBoolOr([&]{ return first; }, [&]{ return second; }) GateRef IsJSAPILinkedList(GateRef obj); GateRef IsJSAPIList(GateRef obj); GateRef IsJSAPIArrayList(GateRef obj); + GateRef IsJSCollator(GateRef obj); GateRef IsJSObjectType(GateRef obj, JSType jsType); GateRef IsJSRegExp(GateRef obj); GateRef GetTarget(GateRef proxyObj); diff --git a/ecmascript/stubs/runtime_stub_list.h b/ecmascript/stubs/runtime_stub_list.h index 144fb91358..28189d8817 100644 --- a/ecmascript/stubs/runtime_stub_list.h +++ b/ecmascript/stubs/runtime_stub_list.h @@ -478,7 +478,8 @@ namespace panda::ecmascript { V(SetPrototypeTransition) \ V(GetSharedModule) \ V(SuperCallForwardAllArgs) \ - V(OptSuperCallForwardAllArgs) + V(OptSuperCallForwardAllArgs) \ + V(GetCollationValueFromIcuCollator) #define RUNTIME_STUB_LIST(V) \ diff --git a/ecmascript/stubs/runtime_stubs.cpp b/ecmascript/stubs/runtime_stubs.cpp index 69243dc75b..1acb42d240 100644 --- a/ecmascript/stubs/runtime_stubs.cpp +++ b/ecmascript/stubs/runtime_stubs.cpp @@ -3994,6 +3994,22 @@ DEF_RUNTIME_STUBS(FunctionPrototypeCall) return JSFunction::Call(info).GetRawData(); } +DEF_RUNTIME_STUBS(GetCollationValueFromIcuCollator) +{ + RUNTIME_STUBS_HEADER(GetCollationValueFromIcuCollator); + JSHandle collator = GetHArg(argv, argc, 0); // 0: means the zeroth parameter + + UErrorCode status = U_ZERO_ERROR; + icu::Collator *icuCollator = collator->GetIcuCollator(); + icu::Locale icu_locale(icuCollator->getLocale(ULOC_VALID_LOCALE, status)); + std::string collation_value = + icu_locale.getUnicodeKeywordValue("co", status); + if (collation_value != "search" && collation_value != "") { + return thread->GetEcmaVM()->GetFactory()->NewFromStdString(collation_value).GetTaggedValue().GetRawData(); + } + return thread->GlobalConstants()->GetDefaultString().GetRawData(); +} + void RuntimeStubs::Initialize(JSThread *thread) { #define DEF_RUNTIME_STUB(name) kungfu::RuntimeStubCSigns::ID_##name diff --git a/test/moduletest/intl/expect_output.txt b/test/moduletest/intl/expect_output.txt index 05c8655301..63ef4c58d6 100644 --- a/test/moduletest/intl/expect_output.txt +++ b/test/moduletest/intl/expect_output.txt @@ -22,3 +22,4 @@ true true true true +true diff --git a/test/moduletest/intl/intl.js b/test/moduletest/intl/intl.js index 6468100699..513c0cf318 100644 --- a/test/moduletest/intl/intl.js +++ b/test/moduletest/intl/intl.js @@ -57,3 +57,12 @@ try { print(o instanceof c); } } + +{ + try { + const proto = this.Intl.Collator.prototype; + proto.resolvedOptions(); + } catch (err) { + print(err instanceof TypeError); + } +} \ No newline at end of file