Skip to content

Commit

Permalink
Use ICU on Windows when available (#34645)
Browse files Browse the repository at this point in the history
* Use ICU when available on Windows

* Fix System.Globalization tests

* Fix System.Globalization.Extensions tests

* Fix System.Runtime tests

* Add System.Globalization.Nls.Tests to test UseNls runtime switch

* Add System.Globalization.Extensions.Nls.Tests to force them to run on NLS

* Add System.Runtime.Nls.Tests.csproj to test Nls behavior

* Fix left over from GlobalizationTestMode

* PR Feedback

* PR Feedback 2

* Fix mono build

* Fix failing tests

* Fix typo

* Fix bad merge in projitems for tvOS

* PR Feedback 3

* PR Feedback 4
  • Loading branch information
safern authored Apr 14, 2020
1 parent a5ff881 commit 1db45ed
Show file tree
Hide file tree
Showing 95 changed files with 1,556 additions and 611 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,41 @@ normaliz.dll!NormalizeString
user32.dll!GetProcessWindowStation
user32.dll!GetUserObjectInformationW

<!-- GetGeoInfo is supported by the analyzer complain for nit using GetGeoInfoW instead and we need to keep the style of not using 'W' in the names -->
kernel32.dll!GetGeoInfo
<!-- GetGeoInfo is supported by the analyzer complain for not using GetGeoInfoW instead and we need to keep the style of not using 'W' in the names -->
kernel32.dll!GetGeoInfo

<!-- PInvokes to System.Globalization.Native shim -->
libSystem.Globalization.Native!GlobalizationNative_ChangeCase
libSystem.Globalization.Native!GlobalizationNative_ChangeCaseInvariant
libSystem.Globalization.Native!GlobalizationNative_ChangeCaseTurkish
libSystem.Globalization.Native!GlobalizationNative_CloseSortHandle
libSystem.Globalization.Native!GlobalizationNative_CompareString
libSystem.Globalization.Native!GlobalizationNative_CompareStringOrdinalIgnoreCase
libSystem.Globalization.Native!GlobalizationNative_EndsWith
libSystem.Globalization.Native!GlobalizationNative_EnumCalendarInfo
libSystem.Globalization.Native!GlobalizationNative_GetCalendarInfo
libSystem.Globalization.Native!GlobalizationNative_GetCalendars
libSystem.Globalization.Native!GlobalizationNative_GetDefaultLocaleName
libSystem.Globalization.Native!GlobalizationNative_GetICUVersion
libSystem.Globalization.Native!GlobalizationNative_GetJapaneseEraStartDate
libSystem.Globalization.Native!GlobalizationNative_GetLatestJapaneseEra
libSystem.Globalization.Native!GlobalizationNative_GetLocaleInfoGroupingSizes
libSystem.Globalization.Native!GlobalizationNative_GetLocaleInfoInt
libSystem.Globalization.Native!GlobalizationNative_GetLocaleInfoString
libSystem.Globalization.Native!GlobalizationNative_GetLocaleName
libSystem.Globalization.Native!GlobalizationNative_GetLocales
libSystem.Globalization.Native!GlobalizationNative_GetLocaleTimeFormat
libSystem.Globalization.Native!GlobalizationNative_GetSortHandle
libSystem.Globalization.Native!GlobalizationNative_GetSortKey
libSystem.Globalization.Native!GlobalizationNative_GetSortVersion
libSystem.Globalization.Native!GlobalizationNative_GetTimeZoneDisplayName
libSystem.Globalization.Native!GlobalizationNative_IndexOf
libSystem.Globalization.Native!GlobalizationNative_IndexOfOrdinalIgnoreCase
libSystem.Globalization.Native!GlobalizationNative_IsNormalized
libSystem.Globalization.Native!GlobalizationNative_IsPredefinedLocale
libSystem.Globalization.Native!GlobalizationNative_LastIndexOf
libSystem.Globalization.Native!GlobalizationNative_LoadICU
libSystem.Globalization.Native!GlobalizationNative_NormalizeString
libSystem.Globalization.Native!GlobalizationNative_StartsWith
libSystem.Globalization.Native!GlobalizationNative_ToAscii
libSystem.Globalization.Native!GlobalizationNative_ToUnicode
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,6 @@
<Compile Include="$(BclSourcesRoot)\System\Variant.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
<Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"> <!-- The CLR internally uses a BOOL type analogous to the Windows BOOL type on Unix -->
<Link>Common\Interop\Windows\Interop.BOOL.cs</Link>
</Compile>
<Compile Include="$(BclSourcesRoot)\Interop\Unix\Interop.Libraries.cs" />
<Compile Include="$(BclSourcesRoot)\System\DateTime.Unix.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\GlobalizationMode.Unix.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ namespace System.Globalization
{
internal static partial class GlobalizationMode
{
// Order of these properties in Windows matter because GetUseIcuMode is dependent on Invariant.
// So we need Invariant to be initialized first.
internal static bool Invariant { get; } = GetGlobalizationInvariantMode();

internal static bool UseNls => false;

private static bool GetGlobalizationInvariantMode()
{
bool invariantEnabled = GetInvariantSwitchValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ namespace System.Globalization
{
internal static partial class GlobalizationMode
{
private static bool GetGlobalizationInvariantMode()
{
return GetInvariantSwitchValue();
}
// Order of these properties in Windows matter because GetUseIcuMode is dependent on Invariant.
// So we need Invariant to be initialized first.
internal static bool Invariant { get; } = GetInvariantSwitchValue();

internal static bool UseNls { get; } = !Invariant &&
(GetSwitchValue("System.Globalization.UseNls", "DOTNET_SYSTEM_GLOBALIZATION_USENLS") ||
Interop.Globalization.LoadICU() == 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ namespace System.Globalization
{
internal static partial class GlobalizationMode
{
internal static bool Invariant { get; } = GetGlobalizationInvariantMode();
private static bool GetInvariantSwitchValue() =>
GetSwitchValue("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");

// GetInvariantSwitchValue calls CLRConfig first to detect if the switch is defined in the config file.
// GetSwitchValue calls CLRConfig first to detect if the switch is defined in the config file.
// if the switch is defined we just use the value of this switch. otherwise, we'll try to get the switch
// value from the environment variable if it is defined.
internal static bool GetInvariantSwitchValue()
private static bool GetSwitchValue(string switchName, string envVariable)
{
bool ret = CLRConfig.GetBoolValue("System.Globalization.Invariant", out bool exist);
bool ret = CLRConfig.GetBoolValue(switchName, out bool exist);
if (!exist)
{
// Linux doesn't support environment variable names include dots
string? switchValue = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");
string? switchValue = Environment.GetEnvironmentVariable(envVariable);
if (switchValue != null)
{
ret = bool.IsTrueStringIgnoreCase(switchValue) || switchValue.Equals("1");
Expand Down
11 changes: 11 additions & 0 deletions src/libraries/Common/src/Interop/Interop.Libraries.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

internal static partial class Interop
{
internal static partial class Libraries
{
internal const string GlobalizationNative = "libSystem.Globalization.Native";
}
}
1 change: 0 additions & 1 deletion src/libraries/Common/src/Interop/Unix/Interop.Libraries.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ internal static partial class Libraries
{
// Shims
internal const string SystemNative = "libSystem.Native";
internal const string GlobalizationNative = "libSystem.Globalization.Native";
internal const string NetSecurityNative = "libSystem.Net.Security.Native";
internal const string CryptoNative = "libSystem.Security.Cryptography.Native.OpenSsl";
internal const string CompressionNative = "libSystem.IO.Compression.Native";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml.Linq;

Expand Down Expand Up @@ -51,9 +50,6 @@ public static partial class PlatformDetection
public static bool IsNotFedoraOrRedHatFamily => !IsFedora && !IsRedHatFamily;
public static bool IsNotDebian10 => !IsDebian10;

private static Lazy<Version> m_icuVersion = new Lazy<Version>(GetICUVersion);
public static Version ICUVersion => m_icuVersion.Value;

public static bool IsSuperUser => !IsWindows ?
libc.geteuid() == 0 :
throw new PlatformNotSupportedException();
Expand Down Expand Up @@ -110,25 +106,6 @@ public static string LibcVersion
}
}

private static Version GetICUVersion()
{
int version = 0;
Type interopGlobalization = Type.GetType("Interop+Globalization");
if (interopGlobalization != null)
{
MethodInfo methodInfo = interopGlobalization.GetMethod("GetICUVersion", BindingFlags.NonPublic | BindingFlags.Static);
if (methodInfo != null)
{
version = (int)methodInfo.Invoke(null, null);
}
}

return new Version( version & 0xFF,
(version >> 8) & 0xFF,
(version >> 16) & 0xFF,
version >> 24);
}

private static Version GetOSXProductVersion()
{
if (IsOSX)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.IO;
using System.Security;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using Microsoft.Win32;
Expand Down Expand Up @@ -150,6 +151,35 @@ public static string GetDistroVersionString()
}
}

private static Lazy<Version> m_icuVersion = new Lazy<Version>(GetICUVersion);
public static Version ICUVersion => m_icuVersion.Value;

public static bool IsIcuGlobalization => ICUVersion > new Version(0,0,0,0);
public static bool IsNlsGlobalization => !IsIcuGlobalization;

private static Version GetICUVersion()
{
int version = 0;
try
{
Type interopGlobalization = Type.GetType("Interop+Globalization");
if (interopGlobalization != null)
{
MethodInfo methodInfo = interopGlobalization.GetMethod("GetICUVersion", BindingFlags.NonPublic | BindingFlags.Static);
if (methodInfo != null)
{
version = (int)methodInfo.Invoke(null, null);
}
}
}
catch { }

return new Version(version >> 24,
(version >> 16) & 0xFF,
(version >> 8) & 0xFF,
version & 0xFF);
}

private static bool GetIsInContainer()
{
if (IsWindows)
Expand Down
Loading

0 comments on commit 1db45ed

Please sign in to comment.