Skip to content

Commit de62bfc

Browse files
authored
Unify KC/KD normalization behavior on Wasm (#121110)
Fixes #117116. - Call `CheckNormalizationForm` before the ASCII fast path so Browser/WASI throw `PlatformNotSupportedException` for FormKC/FormKD regardless of input. - Add wasm-specific test for checking if PNSE was thrown for both, ASCII and non-ASCII with KC and KD.
1 parent ac7666f commit de62bfc

File tree

4 files changed

+40
-12
lines changed

4 files changed

+40
-12
lines changed

src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public static partial class PlatformDetection
5050
public static bool IsSolaris => RuntimeInformation.IsOSPlatform(OSPlatform.Create("SOLARIS"));
5151
public static bool IsBrowser => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));
5252
public static bool IsWasi => RuntimeInformation.IsOSPlatform(OSPlatform.Create("WASI"));
53+
public static bool IsWasm => IsBrowser || IsWasi;
5354
public static bool IsNotBrowser => !IsBrowser;
5455
public static bool IsNotWasi => !IsWasi;
5556
public static bool IsMobile => IsBrowser || IsWasi || IsAppleMobile || IsAndroid;

src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.Icu.cs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ private static unsafe bool IcuIsNormalized(ReadOnlySpan<char> source, Normalizat
1515
Debug.Assert(!GlobalizationMode.Invariant);
1616
Debug.Assert(!GlobalizationMode.UseNls);
1717
Debug.Assert(!source.IsEmpty);
18-
#pragma warning disable CA1416 // FormKC and FormKD are unsupported on browser, ValidateArguments is throwing PlatformNotSupportedException in that case so suppressing the warning here
18+
#pragma warning disable CA1416 // FormKC and FormKD are unsupported on browser, CheckNormalizationForm is throwing PlatformNotSupportedException in that case so suppressing the warning here
1919
Debug.Assert(normalizationForm is NormalizationForm.FormC or NormalizationForm.FormD or NormalizationForm.FormKC or NormalizationForm.FormKD);
2020
#pragma warning restore CA1416
2121

22-
ValidateArguments(source, normalizationForm, nameof(source));
22+
ValidateArguments(source, nameof(source));
2323

2424
int ret;
2525
fixed (char* pInput = source)
@@ -50,7 +50,7 @@ private static unsafe string IcuNormalize(string strInput, NormalizationForm nor
5050
Debug.Assert(!GlobalizationMode.UseNls);
5151
Debug.Assert(normalizationForm == NormalizationForm.FormC || normalizationForm == NormalizationForm.FormD || normalizationForm == NormalizationForm.FormKC || normalizationForm == NormalizationForm.FormKD);
5252

53-
ValidateArguments(strInput, normalizationForm);
53+
ValidateArguments(strInput);
5454

5555
char[]? toReturn = null;
5656
try
@@ -132,7 +132,7 @@ private static unsafe bool IcuTryNormalize(ReadOnlySpan<char> source, Span<char>
132132
return false;
133133
}
134134

135-
ValidateArguments(source, normalizationForm, nameof(source));
135+
ValidateArguments(source, nameof(source));
136136

137137
int realLen;
138138
fixed (char* pInput = source)
@@ -172,7 +172,7 @@ private static unsafe int IcuGetNormalizedLength(ReadOnlySpan<char> source, Norm
172172
Debug.Assert(!source.IsEmpty);
173173
Debug.Assert(normalizationForm == NormalizationForm.FormC || normalizationForm == NormalizationForm.FormD || normalizationForm == NormalizationForm.FormKC || normalizationForm == NormalizationForm.FormKD);
174174

175-
ValidateArguments(source, normalizationForm, nameof(source));
175+
ValidateArguments(source, nameof(source));
176176

177177
int realLen;
178178
fixed (char* pInput = source)
@@ -197,14 +197,8 @@ private static unsafe int IcuGetNormalizedLength(ReadOnlySpan<char> source, Norm
197197
return realLen;
198198
}
199199

200-
private static void ValidateArguments(ReadOnlySpan<char> strInput, NormalizationForm normalizationForm, string paramName = "strInput")
200+
private static void ValidateArguments(ReadOnlySpan<char> strInput, string paramName = "strInput")
201201
{
202-
if ((OperatingSystem.IsBrowser() || OperatingSystem.IsWasi()) && (normalizationForm == NormalizationForm.FormKC || normalizationForm == NormalizationForm.FormKD))
203-
{
204-
// Browser's ICU doesn't contain data needed for FormKC and FormKD
205-
throw new PlatformNotSupportedException(SR.Argument_UnsupportedNormalizationFormInBrowser);
206-
}
207-
208202
if (HasInvalidUnicodeSequence(strInput))
209203
{
210204
throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex, paramName);

src/libraries/System.Private.CoreLib/src/System/Globalization/Normalization.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ private static void CheckNormalizationForm(NormalizationForm normalizationForm)
9494
{
9595
throw new ArgumentException(SR.Argument_InvalidNormalizationForm, nameof(normalizationForm));
9696
}
97+
98+
if ((OperatingSystem.IsBrowser() || OperatingSystem.IsWasi()) &&
99+
!GlobalizationMode.Invariant &&
100+
!GlobalizationMode.UseNls &&
101+
(normalizationForm == NormalizationForm.FormKC || normalizationForm == NormalizationForm.FormKD))
102+
{
103+
// Browser/WASI builds ship without compatibility normalization data.
104+
throw new PlatformNotSupportedException(SR.Argument_UnsupportedNormalizationFormInBrowser);
105+
}
97106
}
98107
}
99108
}

src/libraries/System.Runtime/tests/System.Globalization.Extensions.Tests/Normalization/StringNormalizationTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,5 +138,29 @@ public void Normalize_Null()
138138
{
139139
AssertExtensions.Throws<ArgumentNullException>("strInput", () => StringNormalizationExtensions.Normalize(null));
140140
}
141+
142+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWasm))]
143+
public void NormalizationForms_ThrowOnNotSupportedPlatforms()
144+
{
145+
AssertNormalizationFormThrows(NormalizationForm.FormKC);
146+
AssertNormalizationFormThrows(NormalizationForm.FormKD);
147+
}
148+
149+
private static void AssertNormalizationFormThrows(NormalizationForm normalizationForm)
150+
{
151+
foreach (string value in new[] { "ascii", "😊" })
152+
{
153+
Assert.Throws<PlatformNotSupportedException>(() => value.IsNormalized(normalizationForm));
154+
Assert.Throws<PlatformNotSupportedException>(() => value.AsSpan().IsNormalized(normalizationForm));
155+
Assert.Throws<PlatformNotSupportedException>(() => value.Normalize(normalizationForm));
156+
157+
Assert.Throws<PlatformNotSupportedException>(() =>
158+
{
159+
Span<char> destination = stackalloc char[32];
160+
value.AsSpan().TryNormalize(destination, out _, normalizationForm);
161+
});
162+
Assert.Throws<PlatformNotSupportedException>(() => value.AsSpan().GetNormalizedLength(normalizationForm));
163+
}
164+
}
141165
}
142166
}

0 commit comments

Comments
 (0)