diff --git a/static_core/plugins/ets/runtime/CMakeLists.txt b/static_core/plugins/ets/runtime/CMakeLists.txt index 5c9a3ab390d80df64d75d202975ec98b9e3e4bc7..f7ba1d969a7aeafa474f74a619f1e94e6247142e 100644 --- a/static_core/plugins/ets/runtime/CMakeLists.txt +++ b/static_core/plugins/ets/runtime/CMakeLists.txt @@ -138,6 +138,7 @@ set(ETS_RUNTIME_SOURCES ${PANDA_ETS_PLUGIN_SOURCE}/stdlib/native/core/IntlLocaleMatch.cpp ${PANDA_ETS_PLUGIN_SOURCE}/stdlib/native/core/IntlCollator.cpp ${PANDA_ETS_PLUGIN_SOURCE}/stdlib/native/core/IntlLocale.cpp + ${PANDA_ETS_PLUGIN_SOURCE}/stdlib/native/core/IntlLocaleHelper.cpp ${PANDA_ETS_PLUGIN_SOURCE}/stdlib/native/core/IntlPluralRules.cpp ${PANDA_ETS_PLUGIN_SOURCE}/stdlib/native/core/IntlSegmenter.cpp ${PANDA_ETS_PLUGIN_SOURCE}/stdlib/native/core/stdlib_ani_helpers.cpp diff --git a/static_core/plugins/ets/stdlib/native/core/Intl.cpp b/static_core/plugins/ets/stdlib/native/core/Intl.cpp index 54d73226fdf7dd3676ffa71f130ad89bdb8e1daf..e2a127f49d6f153f5e73c050a8a338a2026dfdf5 100644 --- a/static_core/plugins/ets/stdlib/native/core/Intl.cpp +++ b/static_core/plugins/ets/stdlib/native/core/Intl.cpp @@ -21,6 +21,7 @@ #include "plugins/ets/stdlib/native/core/IntlSegmenter.h" #include "plugins/ets/stdlib/native/core/IntlCommon.h" #include "plugins/ets/stdlib/native/core/IntlLocale.h" +#include "plugins/ets/stdlib/native/core/IntlLocaleHelper.h" #include "plugins/ets/stdlib/native/core/IntlPluralRules.h" #include "plugins/ets/stdlib/native/core/IntlDateTimeFormat.h" #include "plugins/ets/stdlib/native/core/IntlListFormat.h" @@ -40,6 +41,7 @@ ani_status InitCoreIntl(ani_env *env) // Register Native methods. Stop if an error occurred ani_status err = RegisterIntlNumberFormatNativeMethods(env); err = err == ANI_OK ? RegisterIntlLocaleMatch(env) : err; + err = err == ANI_OK ? RegisterIntlLocaleHelper(env) : err; err = err == ANI_OK ? RegisterIntlCollator(env) : err; err = err == ANI_OK ? RegisterIntlLocaleNativeMethods(env) : err; err = err == ANI_OK ? RegisterIntlPluralRules(env) : err; diff --git a/static_core/plugins/ets/stdlib/native/core/IntlLocaleHelper.cpp b/static_core/plugins/ets/stdlib/native/core/IntlLocaleHelper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3bc5f3ed8de920afad1742e53b409458a7a0a21c --- /dev/null +++ b/static_core/plugins/ets/stdlib/native/core/IntlLocaleHelper.cpp @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2025 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 "plugins/ets/stdlib/native/core/IntlLocaleHelper.h" +#include "plugins/ets/stdlib/native/core/IntlLanguageTag.h" +#include "stdlib_ani_helpers.h" + +#include +#include + +namespace ark::ets::stdlib::intl { + +void StdCoreVerifyBCP47LanguageTag(ani_env *env, [[maybe_unused]] ani_class klass, ani_string locale) +{ + auto localeCStr = ConvertFromAniString(env, locale); + if (localeCStr.length() == 0) { + std::string message = "Incorrect locale information provided"; + ThrowNewError(env, "std.core.RangeError", message.c_str(), "C{std.core.String}:"); + } + if (!intl::IsStructurallyValidLanguageTag(localeCStr)) { + std::string message = "Incorrect locale information provided"; + ThrowNewError(env, "std.core.RangeError", message.c_str(), "C{std.core.String}:"); + } + + return; +} + +ani_status RegisterIntlLocaleHelper(ani_env *env) +{ + const auto methods = std::array {ani_native_function { + "verifyBCP47LanguageTag", "C{std.core.String}:", reinterpret_cast(StdCoreVerifyBCP47LanguageTag)}}; + + ani_class localeHelperClass; + ANI_FATAL_IF_ERROR(env->FindClass("std.core.LocaleHelper", &localeHelperClass)); + + return env->Class_BindStaticNativeMethods(localeHelperClass, methods.data(), methods.size()); +} + +} // namespace ark::ets::stdlib::intl \ No newline at end of file diff --git a/static_core/plugins/ets/stdlib/native/core/IntlLocaleHelper.h b/static_core/plugins/ets/stdlib/native/core/IntlLocaleHelper.h new file mode 100644 index 0000000000000000000000000000000000000000..240f16e880ae16e84fe9952b0af22fe938da0f70 --- /dev/null +++ b/static_core/plugins/ets/stdlib/native/core/IntlLocaleHelper.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2025 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 PANDA_PLUGINS_ETS_STDLIB_NATIVE_CORE_INTLLOCALEHELPER_H +#define PANDA_PLUGINS_ETS_STDLIB_NATIVE_CORE_INTLLOCALEHELPER_H + +#include "stdlib_ani_helpers.h" + +#include +#include + +namespace ark::ets::stdlib::intl { + +ani_status RegisterIntlLocaleHelper(ani_env *env); + +void StdCoreVerifyBCP47LanguageTag(ani_env *env, [[maybe_unused]] ani_class klass, ani_string locale); + +} // namespace ark::ets::stdlib::intl + +#endif // PANDA_PLUGINS_ETS_STDLIB_NATIVE_CORE_INTLLOCALEHELPER_H diff --git a/static_core/plugins/ets/stdlib/std/core/LocaleMatch.ets b/static_core/plugins/ets/stdlib/std/core/LocaleMatch.ets index 616bb80fb52997482a35443685390bb0f8e337f9..68622ab247cf09aa2bf25067f53da4cfe42fd6db 100644 --- a/static_core/plugins/ets/stdlib/std/core/LocaleMatch.ets +++ b/static_core/plugins/ets/stdlib/std/core/LocaleMatch.ets @@ -22,6 +22,10 @@ export class LocaleMatch { static native lookupLocales(locale: string[]): string[] } +class LocaleHelper { + static native verifyBCP47LanguageTag(locale: string): void +} + export function intlLookUpLocale(locale: Intl.BCP47LanguageTag | Intl.BCP47LanguageTag[]): string { const tagList = locale instanceof Intl.BCP47LanguageTag ? [locale] : locale return LocaleMatch.lookupLocale(tagList) @@ -48,6 +52,8 @@ export function intlLocalesToLanguageTags(locales: string | Intl.Locale | Readon } if (locales instanceof string) { + const locale = locales as string + LocaleHelper.verifyBCP47LanguageTag(locale) return [locales] } else if (locales instanceof Intl.Locale) { return [locales.toString()] diff --git a/static_core/plugins/ets/subproject_sources.gn b/static_core/plugins/ets/subproject_sources.gn index 07480231de6fa821817972a703f481c6d90a28dc..4b4c4dcebc5c5d2fe8a8ec587d08e81398de3b0e 100644 --- a/static_core/plugins/ets/subproject_sources.gn +++ b/static_core/plugins/ets/subproject_sources.gn @@ -214,6 +214,7 @@ srcs_runtime = [ "stdlib/native/core/IntlNumberFormatters.cpp", "stdlib/native/core/IntlFormattersCache.cpp", "stdlib/native/core/IntlLocale.cpp", + "stdlib/native/core/IntlLocaleHelper.cpp", "stdlib/native/core/IntlLocaleMatch.cpp", "stdlib/native/core/IntlCollator.cpp", "stdlib/native/core/IntlPluralRules.cpp", diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/core/IntlLocalesToLanguageTagsTest.ets b/static_core/plugins/ets/tests/ets_func_tests/std/core/IntlLocalesToLanguageTagsTest.ets new file mode 100644 index 0000000000000000000000000000000000000000..6165640e60f7848a9914e5eaa0074e6bd24d608e --- /dev/null +++ b/static_core/plugins/ets/tests/ets_func_tests/std/core/IntlLocalesToLanguageTagsTest.ets @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2024-2025 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. + */ + +function main(): int { + const suite = new arktest.ArkTestsuite('IntlLocalesToLanguageTags Tests'); + suite.addTest('testEmptyStringInvalid', testEmptyStringInvalid); + suite.addTest('testNumericInvalid', testNumericInvalid); + suite.addTest('testDoubleHyphenInvalid', testDoubleHyphenInvalid); + suite.addTest('testValidSimpleTag', testValidSimpleTag); + suite.addTest('testValidWithRegion', testValidWithRegion); + suite.addTest('testGrandfatheredInvalidTag1', testGrandfatheredInvalidTag1); + suite.addTest('testGrandfatheredInvalidTag2', testGrandfatheredInvalidTag2); + return suite.run(); +} + +function testEmptyStringInvalid() { + const loc = ''; + arktest.expectError((): void => { intlLocalesToLanguageTags(loc) }); +} + +function testNumericInvalid() { + const loc = '123'; + arktest.expectError((): void => { intlLocalesToLanguageTags(loc) }); +} + +function testDoubleHyphenInvalid() { + const loc = 'en--US'; + arktest.expectError((): void => { intlLocalesToLanguageTags(loc) }); +} + +function testValidSimpleTag() { + const loc = 'en'; + const result = intlLocalesToLanguageTags(loc); + arktest.assertEQ(result.toString(), 'en'); +} + +function testValidWithRegion() { + const loc = 'zh-CN'; + const result = intlLocalesToLanguageTags(loc); + arktest.assertEQ(result.toString(), 'zh-CN'); +} + +function testGrandfatheredInvalidTag1() { + const loc = 'i-enochian'; + arktest.expectError((): void => { intlLocalesToLanguageTags(loc) }); +} + +function testGrandfatheredInvalidTag2() { + const loc = 'sgn-BE-FR'; + arktest.expectError((): void => { intlLocalesToLanguageTags(loc) }); +} diff --git a/static_core/plugins/ets/tests/ets_func_tests/std/core/IntlNumberFormatNumberingSystem.ets b/static_core/plugins/ets/tests/ets_func_tests/std/core/IntlNumberFormatNumberingSystem.ets index 33dfd7f43e84d769be4b3ed00082a01d42421568..dad8add427c63958363ec852f93c6ef2452b247f 100644 --- a/static_core/plugins/ets/tests/ets_func_tests/std/core/IntlNumberFormatNumberingSystem.ets +++ b/static_core/plugins/ets/tests/ets_func_tests/std/core/IntlNumberFormatNumberingSystem.ets @@ -20,8 +20,8 @@ function main(): int { } function testNumberingSystem() { - const locales: string[] = [ "en-US", "zh", "ar-SA", ""]; - const expected: string[] = [ "latn", "latn", "arab", "latn"]; + const locales: string[] = [ "en-US", "zh", "ar-SA"]; + const expected: string[] = [ "latn", "latn", "arab"]; let i: int = 0; for (const l of locales) { const nf = new Intl.NumberFormat(l);