diff --git a/docs/design/features/hybrid-globalization.md b/docs/design/features/hybrid-globalization.md index c6767e7d0c08d..b7e74af942007 100644 --- a/docs/design/features/hybrid-globalization.md +++ b/docs/design/features/hybrid-globalization.md @@ -8,7 +8,16 @@ Hybrid mode does not use ICU data for some functions connected with globalizatio ### WASM -For WebAssembly in Browser we are using Web API instead of some ICU data. +For WebAssembly in Browser we are using Web API instead of some ICU data. Ideally, we would use `System.Runtime.InteropServices.JavaScript` to call JS code from inside of C# but we cannot reference any assemblies from inside of `System.Private.CoreLib`. That is why we are using iCalls instead. + +**SortKey** + +Affected public APIs: +- CompareInfo.GetSortKey +- CompareInfo.GetSortKeyLength +- CompareInfo.GetHashCode + +Web API does not have an equivalent, so they throw `PlatformNotSupportedException`. **Case change** diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index aed60f2516334..e26bbb982ec3d 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -356,6 +356,7 @@ public static string GetDistroVersionString() public static bool IsInvariantGlobalization => m_isInvariant.Value; public static bool IsHybridGlobalizationOnBrowser => m_isHybrid.Value && IsBrowser; + public static bool IsNotHybridGlobalizationOnBrowser => !IsHybridGlobalizationOnBrowser; public static bool IsNotInvariantGlobalization => !IsInvariantGlobalization; public static bool IsIcuGlobalization => ICUVersion > new Version(0, 0, 0, 0); public static bool IsNlsGlobalization => IsNotInvariantGlobalization && !IsIcuGlobalization; diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs index 3725cf4fa037a..c7c24b92f8097 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs @@ -7,32 +7,8 @@ namespace System.Globalization.Tests { - public class CompareInfoCompareTests + public class CompareInfoCompareTests : CompareInfoTestsBase { - private static CompareInfo s_invariantCompare = CultureInfo.InvariantCulture.CompareInfo; - private static CompareInfo s_currentCompare = CultureInfo.CurrentCulture.CompareInfo; - private static CompareInfo s_hungarianCompare = new CultureInfo("hu-HU").CompareInfo; - private static CompareInfo s_turkishCompare = new CultureInfo("tr-TR").CompareInfo; - private static CompareInfo s_japaneseCompare = new CultureInfo("ja-JP").CompareInfo; - private static CompareOptions supportedIgnoreNonSpaceOption = - PlatformDetection.IsHybridGlobalizationOnBrowser ? - CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreKanaType : - CompareOptions.IgnoreNonSpace; - - private static CompareOptions supportedIgnoreCaseIgnoreNonSpaceOptions = - PlatformDetection.IsHybridGlobalizationOnBrowser ? - CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreKanaType : - CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace; - - // On Windows, hiragana characters sort after katakana. - // On ICU, it is the opposite - private static int s_expectedHiraganaToKatakanaCompare = PlatformDetection.IsNlsGlobalization ? 1 : -1; - - // On Windows, all halfwidth characters sort before fullwidth characters. - // On ICU, half and fullwidth characters that aren't in the "Halfwidth and fullwidth forms" block U+FF00-U+FFEF - // sort before the corresponding characters that are in the block U+FF00-U+FFEF - private static int s_expectedHalfToFullFormsComparison = PlatformDetection.IsNlsGlobalization ? -1 : 1; - private const string SoftHyphen = "\u00AD"; public static IEnumerable Compare_Kana_TestData() @@ -329,11 +305,6 @@ public static IEnumerable Compare_TestData() yield return new object[] { s_invariantCompare, "\U00010400", "\U00010428", CompareOptions.OrdinalIgnoreCase, useNls ? -1 : 0}; } - // There is a regression in Windows 190xx version with the Kana comparison. Avoid running this test there. - public static bool IsNotWindowsKanaRegressedVersion() => !PlatformDetection.IsWindows10Version1903OrGreater || - PlatformDetection.IsIcuGlobalization || - s_invariantCompare.Compare("\u3060", "\uFF80\uFF9E", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase) == 0; - [Fact] public void CompareWithUnassignedChars() { diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IndexOf.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IndexOf.cs index 58d66e93be6d6..256e394f3673e 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IndexOf.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.IndexOf.cs @@ -8,15 +8,8 @@ namespace System.Globalization.Tests { - public class CompareInfoIndexOfTests + public class CompareInfoIndexOfTests : CompareInfoTestsBase { - private static CompareInfo s_invariantCompare = CultureInfo.InvariantCulture.CompareInfo; - private static CompareInfo s_currentCompare = CultureInfo.CurrentCulture.CompareInfo; - private static CompareInfo s_germanCompare = new CultureInfo("de-DE").CompareInfo; - private static CompareInfo s_hungarianCompare = new CultureInfo("hu-HU").CompareInfo; - private static CompareInfo s_turkishCompare = new CultureInfo("tr-TR").CompareInfo; - private static CompareInfo s_slovakCompare = new CultureInfo("sk-SK").CompareInfo; - public static IEnumerable IndexOf_TestData() { // Empty string diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.LastIndexOf.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.LastIndexOf.cs index 09d0cc0e50449..3ef9b9143b47f 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.LastIndexOf.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.LastIndexOf.cs @@ -8,14 +8,8 @@ namespace System.Globalization.Tests { - public class CompareInfoLastIndexOfTests + public class CompareInfoLastIndexOfTests : CompareInfoTestsBase { - private static CompareInfo s_invariantCompare = CultureInfo.InvariantCulture.CompareInfo; - private static CompareInfo s_germanCompare = new CultureInfo("de-DE").CompareInfo; - private static CompareInfo s_hungarianCompare = new CultureInfo("hu-HU").CompareInfo; - private static CompareInfo s_turkishCompare = new CultureInfo("tr-TR").CompareInfo; - private static CompareInfo s_slovakCompare = new CultureInfo("sk-SK").CompareInfo; - public static IEnumerable LastIndexOf_TestData() { bool useNls = PlatformDetection.IsNlsGlobalization; diff --git a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs index 59472739e04ff..9c8dc7287178f 100644 --- a/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs +++ b/src/libraries/System.Globalization/tests/CompareInfo/CompareInfoTests.cs @@ -10,7 +10,7 @@ namespace System.Globalization.Tests { - public partial class CompareInfoTests + public class CompareInfoTests : CompareInfoTestsBase { [Theory] [InlineData("")] @@ -60,7 +60,7 @@ public void EqualsTest(CompareInfo compare1, object value, bool expected) new object[] { "", CompareOptions.None, "\u200c", CompareOptions.None, true }, // see comment at bottom of SortKey_TestData }; - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] [MemberData(nameof(GetHashCodeTestData))] public void GetHashCodeTest(string source1, CompareOptions options1, string source2, CompareOptions options2, bool expected) { @@ -97,19 +97,6 @@ public static IEnumerable CompareInfo_TestData() yield return new object[] { "tr-TR" , 0x041f }; } - // On NLS, hiragana characters sort after katakana. - // On ICU, it is the opposite - private static int s_expectedHiraganaToKatakanaCompare = PlatformDetection.IsNlsGlobalization ? 1 : -1; - - // On NLS, all halfwidth characters sort before fullwidth characters. - // On ICU, half and fullwidth characters that aren't in the "Halfwidth and fullwidth forms" block U+FF00-U+FFEF - // sort before the corresponding characters that are in the block U+FF00-U+FFEF - private static int s_expectedHalfToFullFormsComparison = PlatformDetection.IsNlsGlobalization ? -1 : 1; - - private static CompareInfo s_hungarianCompare = new CultureInfo("hu-HU").CompareInfo; - private static CompareInfo s_invariantCompare = CultureInfo.InvariantCulture.CompareInfo; - private static CompareInfo s_turkishCompare = new CultureInfo("tr-TR").CompareInfo; - public static IEnumerable SortKey_Kana_TestData() { CompareOptions ignoreKanaIgnoreWidthIgnoreCase = CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase; @@ -124,6 +111,7 @@ public static IEnumerable SortKey_Kana_TestData() yield return new object[] { s_invariantCompare, "\u3070\u3073\u3076\u3079\u307C", "\u30D0\u30D3\u3076\u30D9\uFF8E\uFF9E", CompareOptions.None, s_expectedHiraganaToKatakanaCompare }; yield return new object[] { s_invariantCompare, "\u3060", "\uFF80\uFF9E", CompareOptions.None, s_expectedHiraganaToKatakanaCompare }; } + public static IEnumerable SortKey_TestData() { CompareOptions ignoreKanaIgnoreWidthIgnoreCase = CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase; @@ -357,13 +345,48 @@ public static void LcidTest(string cultureName, int lcid) Assert.Equal(lcid, ci.LCID); } - [ConditionalTheory(typeof(CompareInfoCompareTests), nameof(CompareInfoCompareTests.IsNotWindowsKanaRegressedVersion))] + [ConditionalTheory(typeof(CompareInfoTests), nameof(IsNotWindowsKanaRegressedVersionAndNotHybridGlobalizationOnWasm))] [MemberData(nameof(SortKey_Kana_TestData))] public void SortKeyKanaTest(CompareInfo compareInfo, string string1, string string2, CompareOptions options, int expected) { SortKeyTest(compareInfo, string1, string2, options, expected); } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))] + public void SortKeyTestNotSupported() + { + try + { + s_invariantCompare.GetSortKey(""); + AssertNotReached(); + } + catch(PlatformNotSupportedException pnse) + { + Assert.Equal(GetPNSEText("SortKey"), pnse.Message); + } + try + { + s_invariantCompare.GetSortKeyLength(ReadOnlySpan.Empty); + AssertNotReached(); + } + catch(PlatformNotSupportedException pnse) + { + Assert.Equal(GetPNSEText("SortKey"), pnse.Message); + } + + try + { + s_invariantCompare.GetHashCode("", CompareOptions.None); + AssertNotReached(); + } + catch(PlatformNotSupportedException pnse) + { + Assert.Equal(GetPNSEText("HashCode"), pnse.Message); + } + + string GetPNSEText(string funcName) => $"{funcName} is not supported when HybridGlobalization=true. Disable it to load larger ICU bundle, then use this option."; + void AssertNotReached() => Assert.True(false); + } [DllImport("kernel32", CharSet = CharSet.Unicode)] private static extern int CompareStringEx(string lpLocaleName, uint dwCmpFlags, string lpString1, int cchCount1, string lpString2, int cchCount2, IntPtr lpVersionInformation, IntPtr lpReserved, int lParam); @@ -372,7 +395,7 @@ public void SortKeyKanaTest(CompareInfo compareInfo, string string1, string stri private static bool WindowsVersionHasTheCompareStringRegression => PlatformDetection.IsNlsGlobalization && CompareStringEx("", NORM_LINGUISTIC_CASING, "", 0, "\u200C", 1, IntPtr.Zero, IntPtr.Zero, 0) != 2; - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotHybridGlobalizationOnBrowser))] [MemberData(nameof(SortKey_TestData))] public void SortKeyTest(CompareInfo compareInfo, string string1, string string2, CompareOptions options, int expectedSign) { @@ -421,7 +444,7 @@ unsafe static void RunSpanSortKeyTest(CompareInfo compareInfo, ReadOnlySpan !PlatformDetection.IsWindows10Version1903OrGreater || + PlatformDetection.IsIcuGlobalization || + s_invariantCompare.Compare("\u3060", "\uFF80\uFF9E", CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | CompareOptions.IgnoreCase) == 0; + + protected static bool IsNotWindowsKanaRegressedVersionAndNotHybridGlobalizationOnWasm() => !PlatformDetection.IsHybridGlobalizationOnBrowser && IsNotWindowsKanaRegressedVersion(); + } +} diff --git a/src/libraries/System.Globalization/tests/Hybrid/Hybrid.WASM.Tests.csproj b/src/libraries/System.Globalization/tests/Hybrid/Hybrid.WASM.Tests.csproj index a142fa3556055..a65d8448c4d70 100644 --- a/src/libraries/System.Globalization/tests/Hybrid/Hybrid.WASM.Tests.csproj +++ b/src/libraries/System.Globalization/tests/Hybrid/Hybrid.WASM.Tests.csproj @@ -1,11 +1,14 @@ - $(NetCoreAppCurrent)-browser + $(NetCoreAppCurrent)-browser + true true true + + diff --git a/src/libraries/System.Globalization/tests/NlsTests/System.Globalization.Nls.Tests.csproj b/src/libraries/System.Globalization/tests/NlsTests/System.Globalization.Nls.Tests.csproj index a9aeef49807f0..51ba3e856eb76 100644 --- a/src/libraries/System.Globalization/tests/NlsTests/System.Globalization.Nls.Tests.csproj +++ b/src/libraries/System.Globalization/tests/NlsTests/System.Globalization.Nls.Tests.csproj @@ -15,6 +15,8 @@ + + diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 25201a91ceddc..5a0f015a59e52 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4046,6 +4046,9 @@ Arrays with non-zero lower bounds are not supported. + + {0} is not supported when HybridGlobalization=true. Disable it to load larger ICU bundle, then use this option. + The body of this method was removed by the AOT compiler because it's not callable. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs index 32eef9a1f885c..3a3a5ba955375 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs @@ -1425,6 +1425,10 @@ public SortKey GetSortKey(string source) private SortKey CreateSortKeyCore(string source, CompareOptions options) => GlobalizationMode.UseNls ? NlsCreateSortKey(source, options) : +#if TARGET_BROWSER + GlobalizationMode.Hybrid ? + throw new PlatformNotSupportedException(GetPNSEText("SortKey")) : +#endif IcuCreateSortKey(source, options); /// @@ -1462,9 +1466,13 @@ public int GetSortKey(ReadOnlySpan source, Span destination, Compare } private int GetSortKeyCore(ReadOnlySpan source, Span destination, CompareOptions options) => - GlobalizationMode.UseNls ? - NlsGetSortKey(source, destination, options) : - IcuGetSortKey(source, destination, options); + GlobalizationMode.UseNls ? + NlsGetSortKey(source, destination, options) : +#if TARGET_BROWSER + GlobalizationMode.Hybrid ? + throw new PlatformNotSupportedException(GetPNSEText("SortKey")) : +#endif + IcuGetSortKey(source, destination, options); /// /// Returns the length (in bytes) of the sort key that would be produced from the specified input. @@ -1495,8 +1503,12 @@ public int GetSortKeyLength(ReadOnlySpan source, CompareOptions options = } private int GetSortKeyLengthCore(ReadOnlySpan source, CompareOptions options) => - GlobalizationMode.UseNls ? + GlobalizationMode.UseNls ? NlsGetSortKeyLength(source, options) : +#if TARGET_BROWSER + GlobalizationMode.Hybrid ? + throw new PlatformNotSupportedException(GetPNSEText("SortKey")) : +#endif IcuGetSortKeyLength(source, options); public override bool Equals([NotNullWhen(true)] object? value) @@ -1570,6 +1582,10 @@ public int GetHashCode(ReadOnlySpan source, CompareOptions options) private unsafe int GetHashCodeOfStringCore(ReadOnlySpan source, CompareOptions options) => GlobalizationMode.UseNls ? NlsGetHashCodeOfString(source, options) : +#if TARGET_BROWSER + GlobalizationMode.Hybrid ? + throw new PlatformNotSupportedException(GetPNSEText("HashCode")) : +#endif IcuGetHashCodeOfString(source, options); public override string ToString() => "CompareInfo - " + Name; @@ -1599,5 +1615,9 @@ public SortVersion Version } public int LCID => CultureInfo.GetCultureInfo(Name).LCID; + +#if TARGET_BROWSER + private static string GetPNSEText(string funcName) => SR.Format(SR.PlatformNotSupported_HybridGlobalization, funcName); +#endif } }