From eea9ec54bb2b808f09a9eb6585d69b4d0af28ec8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 20 Aug 2021 15:16:32 -0700 Subject: [PATCH] [release/6.0] Fix Culture creation regression when using invalid names on Windows (#57853) * Fix Culture creation regression when using invalid names on Windows * Address the feedback Co-authored-by: Tarek Mahmoud Sayed --- .../tests/CultureInfo/CultureInfoAll.cs | 22 ++++++++++------------ .../tests/CultureInfo/GetCultureInfo.cs | 22 ++++++++++++++++++---- .../System/Globalization/CultureData.Windows.cs | 9 +++++---- .../src/System/Globalization/IcuLocaleData.cs | 7 +++---- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs index c89088b..dac3b98 100644 --- a/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs +++ b/src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs @@ -667,12 +667,6 @@ namespace System.Globalization.Tests CultureInfo ci = new CultureInfo(lcid); Assert.Contains(ci.Name, cultureNames, StringComparer.OrdinalIgnoreCase); - if (ci.LCID == 0x1000) - { - // Unsupported culture. - return; - } - Assert.True(lcid == ci.LCID || (ushort)lcid == (ushort)ci.LCID); Assert.True(ci.UseUserOverride, "UseUserOverride for lcid created culture expected to be true"); Assert.False(ci.IsReadOnly, "IsReadOnly for lcid created culture expected to be false"); @@ -719,10 +713,14 @@ namespace System.Globalization.Tests ci = CultureInfo.CreateSpecificCulture(cultureNames[0]); TestCultureName(specificCultureName, ci.Name); - ci = CultureInfo.GetCultureInfoByIetfLanguageTag(cultureNames[0]); - Assert.Contains(ci.Name, cultureNames, StringComparer.OrdinalIgnoreCase); - TestCultureName(ci.Name, ci.IetfLanguageTag); - Assert.True(lcid == ci.KeyboardLayoutId || (ushort)lcid == (ushort)ci.KeyboardLayoutId); + // CultureInfo.GetCultureInfoByIetfLanguageTag doesn't support alternative sort LCID's. + if (lcid <= 0xffff && lcid != 0x040a) + { + ci = CultureInfo.GetCultureInfoByIetfLanguageTag(cultureNames[0]); + Assert.Contains(ci.Name, cultureNames, StringComparer.OrdinalIgnoreCase); + TestCultureName(ci.Name, ci.IetfLanguageTag); + Assert.True(lcid == ci.KeyboardLayoutId || (ushort)lcid == (ushort)ci.KeyboardLayoutId); + } if (ci.GetConsoleFallbackUICulture().Name != "") { @@ -733,8 +731,8 @@ namespace System.Globalization.Tests { AssertExtensions.Throws(() => new CultureInfo(lcid)); } - - } + + } private static string[] hans = new[] { "zh-CN", "zh-CHS", "zh-Hans" }; private static string[] hant = new[] { "zh-HK", "zh-CHT", "zh-Hant" }; diff --git a/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs b/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs index e6399bb..5a5e3da 100644 --- a/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs +++ b/src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs @@ -32,12 +32,15 @@ namespace System.Globalization.Tests if (PlatformDetection.IsIcuGlobalization) { - yield return new object[] { "x\u0000X-Yy", "x" }; // Null byte + if (PlatformDetection.IsNotWindows) + { + yield return new object[] { "x\u0000X-Yy", "x" }; // Null byte + yield return new object[] { "zh-cmn", "zh-CMN" }; + yield return new object[] { "zh-CMN-HANS" }; + yield return new object[] { "zh-cmn-Hant", "zh-CMN-HANT" }; + } yield return new object[] { "sgn-BE-FR" }; yield return new object[] { "zh-min-nan", "nan" }; - yield return new object[] { "zh-cmn", "zh-CMN" }; - yield return new object[] { "zh-CMN-HANS" }; - yield return new object[] { "zh-cmn-Hant", "zh-CMN-HANT" }; yield return new object[] { "zh-gan", "gan" }; yield return new object[] { "zh-Hans-CN" }; yield return new object[] { "zh-Hans-SG" }; @@ -58,6 +61,17 @@ namespace System.Globalization.Tests } } + [Theory] + [PlatformSpecific(TestPlatforms.Windows)] // Windows specific behavior + [InlineData("x\u0000X-Yy")] + [InlineData("zh-cmn")] + [InlineData("zh-CMN-HANS")] + [InlineData("zh-cmn-Hant")] + public void TestIvalidCultureNames(string cultureName) + { + Assert.Throws(() => new CultureInfo(cultureName)); + } + [ConditionalTheory(nameof(PlatformSupportsFakeCulture))] [MemberData(nameof(GetCultureInfoTestData))] public void GetCultureInfo(string name, string expected = null) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs index 74854c3..69de37e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs @@ -42,16 +42,17 @@ namespace System.Globalization /// private unsafe bool InitCultureDataCore() { - if (!ShouldUseUserOverrideNlsData) + char* pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH]; + if (!GlobalizationMode.UseNls) { - return InitIcuCultureDataCore(); + return InitIcuCultureDataCore() && + GetLocaleInfoEx(_sRealName, Interop.Kernel32.LOCALE_SNAME, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH) != 0; // Ensure the culture name is supported by Windows. } Debug.Assert(!GlobalizationMode.Invariant); int result; string realNameBuffer = _sRealName; - char* pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH]; result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_SNAME, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH); @@ -120,7 +121,7 @@ namespace System.Globalization // We need the IETF name (sname) // If we aren't an alt sort locale then this is the same as the windows name. // If we are an alt sort locale then this is the same as the part before the _ in the windows name - // This is for like de-DE_phoneb and es-ES_tradnl that hsouldn't have the _ part + // This is for like de-DE_phoneb and es-ES_tradnl that shouldn't have the _ part result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_ILANGUAGE | Interop.Kernel32.LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char)); if (result == 0) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs index 1ed01ba..370e458 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs @@ -3806,7 +3806,7 @@ namespace System.Globalization return GetLocaleDataMappedCulture(cultureName, IcuLocaleDataParts.ConsoleLocaleIndex); } - // Returns index of the culture or -1 if it fail finding any match + // Returns index of the culture or less than 0 if it fail finding any match private static int SearchCultureName(string name) { if (name.Length > LocaleLongestName) @@ -3816,10 +3816,9 @@ namespace System.Globalization for (int i = 0; i < name.Length; ++i) { char ch = name[i]; - if (ch > 'z') - return -1; - lower_case[i] = (byte)(ch | 0x20); + Debug.Assert(ch <= 'z'); + lower_case[i] = ((uint)(ch - 'A') <= 'Z' - 'A') ? (byte)(ch | 0x20) : (byte)ch; } ReadOnlySpan lname = lower_case; -- 2.7.4