Skip to content

Commit 5c53e8e

Browse files
authored
[browser] Fix: HybridGlobalization Invariant function differs from non-Hybrid (#103037)
* Remove invaraint cace change from JS and rely on methods used in InvariantGlobalization. * Fix - some internal libs are using `ChangeCaseCore` with invariant culture. * Fix NodeJS failures - it behaves like old chrome. * Remove duplicated code.
1 parent b2f0b7a commit 5c53e8e

File tree

12 files changed

+47
-79
lines changed

12 files changed

+47
-79
lines changed

src/libraries/Common/src/Interop/Browser/Interop.TextInfo.cs

-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ internal static partial class Interop
77
{
88
internal static unsafe partial class JsGlobalization
99
{
10-
[MethodImplAttribute(MethodImplOptions.InternalCall)]
11-
internal static extern unsafe nint ChangeCaseInvariant(char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper);
1210
[MethodImplAttribute(MethodImplOptions.InternalCall)]
1311
internal static extern unsafe nint ChangeCase(char* culture, int cultureLen, char* src, int srcLen, char* dstBuffer, int dstBufferCapacity, bool bToUpper);
1412
}

src/libraries/Common/tests/Tests/System/StringTests.cs

-2
Original file line numberDiff line numberDiff line change
@@ -1287,7 +1287,6 @@ public static void ContainsMatchDifferentSpans_StringComparison()
12871287

12881288
[Fact]
12891289
[ActiveIssue("https://github.com/dotnet/runtime/issues/95338", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnApplePlatform))]
1290-
[ActiveIssue("https://github.com/dotnet/runtime/issues/95471", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))]
12911290
public static void ContainsNoMatch_StringComparison()
12921291
{
12931292
for (int length = 1; length < 150; length++)
@@ -6041,7 +6040,6 @@ public static IEnumerable<object[]> ToUpper_TurkishI_InvariantCulture_MemberData
60416040

60426041
[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotInvariantGlobalization), nameof(PlatformDetection.IsNotHybridGlobalizationOnApplePlatform))]
60436042
[MemberData(nameof(ToUpper_TurkishI_InvariantCulture_MemberData))]
6044-
[ActiveIssue("https://github.com/dotnet/runtime/issues/95471", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))]
60456043
public static void ToUpper_TurkishI_InvariantCulture(string s, string expected)
60466044
{
60476045
using (new ThreadCultureChange(CultureInfo.InvariantCulture))

src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.WebAssembly.cs

+16-3
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,25 @@ internal unsafe void JsChangeCase(char* src, int srcLen, char* dstBuffer, int ds
1414
Debug.Assert(!GlobalizationMode.UseNls);
1515
Debug.Assert(GlobalizationMode.Hybrid);
1616

17+
if (HasEmptyCultureName)
18+
{
19+
ReadOnlySpan<char> source = new ReadOnlySpan<char>(src, srcLen);
20+
Span<char> destination = new Span<char>(dstBuffer, dstBufferCapacity);
21+
if (toUpper)
22+
{
23+
InvariantModeCasing.ToUpper(source, destination);
24+
}
25+
else
26+
{
27+
InvariantModeCasing.ToLower(source, destination);
28+
}
29+
return;
30+
}
31+
1732
ReadOnlySpan<char> cultureName = _cultureName.AsSpan();
1833
fixed (char* pCultureName = &MemoryMarshal.GetReference(cultureName))
1934
{
20-
nint exceptionPtr = HasEmptyCultureName ?
21-
Interop.JsGlobalization.ChangeCaseInvariant(src, srcLen, dstBuffer, dstBufferCapacity, toUpper) :
22-
Interop.JsGlobalization.ChangeCase(pCultureName, cultureName.Length, src, srcLen, dstBuffer, dstBufferCapacity, toUpper);
35+
nint exceptionPtr = Interop.JsGlobalization.ChangeCase(pCultureName, cultureName.Length, src, srcLen, dstBuffer, dstBufferCapacity, toUpper);
2336
Helper.MarshalAndThrowIfException(exceptionPtr);
2437
}
2538
}

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

+29-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,12 @@ public string ListSeparator
140140
/// </summary>
141141
public char ToLower(char c)
142142
{
143+
#if TARGET_BROWSER
144+
// for invariant culture _cultureName is empty - HybridGlobalization does not have to call JS
145+
if (GlobalizationMode.Invariant || (GlobalizationMode.Hybrid && HasEmptyCultureName))
146+
#else
143147
if (GlobalizationMode.Invariant)
148+
#endif
144149
{
145150
return InvariantModeCasing.ToLower(c);
146151
}
@@ -173,7 +178,12 @@ public string ToLower(string str)
173178
{
174179
ArgumentNullException.ThrowIfNull(str);
175180

181+
#if TARGET_BROWSER
182+
// for invariant culture _cultureName is empty - HybridGlobalization does not have to call JS
183+
if (GlobalizationMode.Invariant || (GlobalizationMode.Hybrid && HasEmptyCultureName))
184+
#else
176185
if (GlobalizationMode.Invariant)
186+
#endif
177187
{
178188
return InvariantModeCasing.ToLower(str);
179189
}
@@ -184,7 +194,9 @@ public string ToLower(string str)
184194
private unsafe char ChangeCase(char c, bool toUpper)
185195
{
186196
Debug.Assert(!GlobalizationMode.Invariant);
187-
197+
#if TARGET_BROWSER
198+
Debug.Assert(!(GlobalizationMode.Hybrid && HasEmptyCultureName));
199+
#endif
188200
char dst = default;
189201
ChangeCaseCore(&c, 1, &dst, 1, toUpper);
190202
return dst;
@@ -227,6 +239,9 @@ private unsafe void ChangeCaseCommon<TConversion>(ReadOnlySpan<char> source, Spa
227239
{
228240
Debug.Assert(!GlobalizationMode.Invariant);
229241
Debug.Assert(typeof(TConversion) == typeof(ToUpperConversion) || typeof(TConversion) == typeof(ToLowerConversion));
242+
#if TARGET_BROWSER
243+
Debug.Assert(!(GlobalizationMode.Hybrid && HasEmptyCultureName));
244+
#endif
230245

231246
if (source.IsEmpty)
232247
{
@@ -263,6 +278,9 @@ private unsafe string ChangeCaseCommon<TConversion>(string source) where TConver
263278

264279
Debug.Assert(!GlobalizationMode.Invariant);
265280
Debug.Assert(source != null);
281+
#if TARGET_BROWSER
282+
Debug.Assert(!(GlobalizationMode.Hybrid && HasEmptyCultureName));
283+
#endif
266284

267285
// If the string is empty, we're done.
268286
if (source.Length == 0)
@@ -412,7 +430,12 @@ private static char ToLowerAsciiInvariant(char c)
412430
/// </summary>
413431
public char ToUpper(char c)
414432
{
433+
#if TARGET_BROWSER
434+
// for invariant culture _cultureName is empty - HybridGlobalization does not have to call JS
435+
if (GlobalizationMode.Invariant || (GlobalizationMode.Hybrid && HasEmptyCultureName))
436+
#else
415437
if (GlobalizationMode.Invariant)
438+
#endif
416439
{
417440
return InvariantModeCasing.ToUpper(c);
418441
}
@@ -445,7 +468,12 @@ public string ToUpper(string str)
445468
{
446469
ArgumentNullException.ThrowIfNull(str);
447470

471+
#if TARGET_BROWSER
472+
// for invariant culture _cultureName is empty - HybridGlobalization does not have to call JS
473+
if (GlobalizationMode.Invariant || (GlobalizationMode.Hybrid && HasEmptyCultureName))
474+
#else
448475
if (GlobalizationMode.Invariant)
476+
#endif
449477
{
450478
return InvariantModeCasing.ToUpper(str);
451479
}

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs

-1
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,6 @@ public static IEnumerable<object[]> Replace_StringComparisonCulture_TestData()
872872

873873
[Theory]
874874
[MemberData(nameof(Replace_StringComparisonCulture_TestData))]
875-
[ActiveIssue("https://github.com/dotnet/runtime/issues/95471", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))]
876875
[ActiveIssue("https://github.com/dotnet/runtime/issues/95503", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))]
877876
public void Replace_StringComparisonCulture_ReturnsExpected(string original, string oldValue, string newValue, bool ignoreCase, CultureInfo culture, string expected)
878877
{

src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/RegexCultureTests.cs

-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ Regex[] Create(string input, CultureInfo info, RegexOptions additional)
161161

162162
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Doesn't support NonBacktracking")]
163163
[Fact]
164-
[ActiveIssue("https://github.com/dotnet/runtime/issues/95471", typeof(PlatformDetection), nameof(PlatformDetection.IsHybridGlobalizationOnBrowser))]
165164
[ActiveIssue("https://github.com/dotnet/runtime/issues/60568", TestPlatforms.Android | TestPlatforms.LinuxBionic)]
166165
public async Task TurkishI_Is_Differently_LowerUpperCased_In_Turkish_Culture_NonBacktracking()
167166
{

src/mono/browser/runtime/corebindings.c

-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ extern void mono_wasm_invoke_jsimport_ST (int function_handle, void *args);
5959
#endif /* DISABLE_THREADS */
6060

6161
// HybridGlobalization
62-
extern char16_t* mono_wasm_change_case_invariant (const uint16_t* src, int32_t srcLength, uint16_t* dst, int32_t dstLength, mono_bool bToUpper);
6362
extern char16_t* mono_wasm_change_case (const uint16_t* culture, int32_t cultureLength, const uint16_t* src, int32_t srcLength, uint16_t* dst, int32_t dstLength, mono_bool bToUpper);
6463
extern char16_t* mono_wasm_compare_string (const uint16_t* culture, int32_t cultureLength, const uint16_t* str1, int32_t str1Length, const uint16_t* str2, int32_t str2Length, int32_t options, int *resultPtr);
6564
extern char16_t* mono_wasm_starts_with (const uint16_t* culture, int32_t cultureLength, const uint16_t* str1, int32_t str1Length, const uint16_t* str2, int32_t str2Length, int32_t options, mono_bool *resultPtr);
@@ -104,7 +103,6 @@ void bindings_initialize_internals (void)
104103
mono_add_internal_call ("System.ConsolePal::Clear", mono_wasm_console_clear);
105104

106105
// HybridGlobalization
107-
mono_add_internal_call ("Interop/JsGlobalization::ChangeCaseInvariant", mono_wasm_change_case_invariant);
108106
mono_add_internal_call ("Interop/JsGlobalization::ChangeCase", mono_wasm_change_case);
109107
mono_add_internal_call ("Interop/JsGlobalization::CompareString", mono_wasm_compare_string);
110108
mono_add_internal_call ("Interop/JsGlobalization::StartsWith", mono_wasm_starts_with);

src/mono/browser/runtime/globalization-stubs.ts

-8
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,6 @@ import { globalizationHelpers } from "./globals";
55
import { Int32Ptr, VoidPtr } from "./types/emscripten";
66
import { VoidPtrNull } from "./types/internal";
77

8-
9-
export function mono_wasm_change_case_invariant (src: number, srcLength: number, dst: number, dstLength: number, toUpper: number) : VoidPtr {
10-
if (typeof globalizationHelpers.mono_wasm_change_case_invariant === "function") {
11-
return globalizationHelpers.mono_wasm_change_case_invariant(src, srcLength, dst, dstLength, toUpper);
12-
}
13-
return VoidPtrNull;
14-
}
15-
168
export function mono_wasm_change_case (culture: number, cultureLength: number, src: number, srcLength: number, dst: number, dstLength: number, toUpper: number) : VoidPtr {
179
if (typeof globalizationHelpers.mono_wasm_change_case === "function") {
1810
return globalizationHelpers.mono_wasm_change_case(culture, cultureLength, src, srcLength, dst, dstLength, toUpper);

src/mono/browser/runtime/globalization.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
import { mono_wasm_change_case, mono_wasm_change_case_invariant, mono_wasm_compare_string, mono_wasm_ends_with, mono_wasm_get_calendar_info, mono_wasm_get_culture_info, mono_wasm_get_first_day_of_week, mono_wasm_get_first_week_of_year, mono_wasm_index_of, mono_wasm_starts_with } from "./globalization-stubs";
4+
import { mono_wasm_change_case, mono_wasm_compare_string, mono_wasm_ends_with, mono_wasm_get_calendar_info, mono_wasm_get_culture_info, mono_wasm_get_first_day_of_week, mono_wasm_get_first_week_of_year, mono_wasm_index_of, mono_wasm_starts_with } from "./globalization-stubs";
55
import { mono_wasm_get_locale_info } from "./locales-common";
66

77
export const mono_wasm_hybrid_globalization_imports = [
8-
mono_wasm_change_case_invariant,
98
mono_wasm_change_case,
109
mono_wasm_compare_string,
1110
mono_wasm_starts_with,

src/mono/browser/runtime/hybrid-globalization/change-case.ts

-54
Original file line numberDiff line numberDiff line change
@@ -6,60 +6,6 @@ import { runtimeHelpers } from "./module-exports";
66
import { VoidPtr } from "../types/emscripten";
77
import { isSurrogate } from "./helpers";
88

9-
export function mono_wasm_change_case_invariant (src: number, srcLength: number, dst: number, dstLength: number, toUpper: number): VoidPtr {
10-
try {
11-
const input = runtimeHelpers.utf16ToStringLoop(src, src + 2 * srcLength);
12-
const result = toUpper ? input.toUpperCase() : input.toLowerCase();
13-
// Unicode defines some codepoints which expand into multiple codepoints,
14-
// originally we do not support this expansion
15-
if (result.length <= dstLength) {
16-
runtimeHelpers.stringToUTF16(dst, dst + 2 * dstLength, result);
17-
return VoidPtrNull;
18-
}
19-
20-
// workaround to maintain the ICU-like behavior
21-
const heapI16 = runtimeHelpers.localHeapViewU16();
22-
let jump = 1;
23-
if (toUpper) {
24-
for (let i = 0; i < input.length; i += jump) {
25-
// surrogate parts have to enter ToUpper/ToLower together to give correct output
26-
if (isSurrogate(input, i)) {
27-
jump = 2;
28-
const surrogate = input.substring(i, i + 2);
29-
const upperSurrogate = surrogate.toUpperCase();
30-
const appendedSurrogate = upperSurrogate.length > 2 ? surrogate : upperSurrogate;
31-
appendSurrogateToMemory(heapI16, dst, appendedSurrogate, i);
32-
33-
} else {
34-
jump = 1;
35-
const upperChar = input[i].toUpperCase();
36-
const appendedChar = upperChar.length > 1 ? input[i] : upperChar;
37-
runtimeHelpers.setU16_local(heapI16, dst + i * 2, appendedChar.charCodeAt(0));
38-
}
39-
}
40-
} else {
41-
for (let i = 0; i < input.length; i += jump) {
42-
if (isSurrogate(input, i)) {
43-
jump = 2;
44-
const surrogate = input.substring(i, i + 2);
45-
const upperSurrogate = surrogate.toLowerCase();
46-
const appendedSurrogate = upperSurrogate.length > 2 ? surrogate : upperSurrogate;
47-
appendSurrogateToMemory(heapI16, dst, appendedSurrogate, i);
48-
49-
} else {
50-
jump = 1;
51-
const upperChar = input[i].toLowerCase();
52-
const appendedChar = upperChar.length > 1 ? input[i] : upperChar;
53-
runtimeHelpers.setU16_local(heapI16, dst + i * 2, appendedChar.charCodeAt(0));
54-
}
55-
}
56-
}
57-
return VoidPtrNull;
58-
} catch (ex: any) {
59-
return runtimeHelpers.stringToUTF16Ptr(ex.toString());
60-
}
61-
}
62-
639
export function mono_wasm_change_case (culture: number, cultureLength: number, src: number, srcLength: number, dst: number, dstLength: number, toUpper: number): VoidPtr {
6410
try {
6511
const cultureName = runtimeHelpers.utf16ToString(<any>culture, <any>(culture + 2 * cultureLength));

src/mono/browser/runtime/hybrid-globalization/module-exports.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { GlobalizationHelpers, RuntimeHelpers } from "../types/internal";
22
import { mono_wasm_get_calendar_info } from "./calendar";
3-
import { mono_wasm_change_case, mono_wasm_change_case_invariant } from "./change-case";
3+
import { mono_wasm_change_case } from "./change-case";
44
import { mono_wasm_compare_string, mono_wasm_starts_with, mono_wasm_ends_with, mono_wasm_index_of } from "./collations";
55
import { mono_wasm_get_culture_info } from "./culture-info";
66
import { setSegmentationRulesFromJson } from "./grapheme-segmenter";
@@ -10,7 +10,6 @@ export let globalizationHelpers: GlobalizationHelpers;
1010
export let runtimeHelpers: RuntimeHelpers;
1111

1212
export function initHybrid (gh: GlobalizationHelpers, rh: RuntimeHelpers) {
13-
gh.mono_wasm_change_case_invariant = mono_wasm_change_case_invariant;
1413
gh.mono_wasm_change_case = mono_wasm_change_case;
1514
gh.mono_wasm_compare_string = mono_wasm_compare_string;
1615
gh.mono_wasm_starts_with = mono_wasm_starts_with;

src/mono/browser/runtime/types/internal.ts

-1
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,6 @@ export type RuntimeHelpers = {
254254
}
255255
export type GlobalizationHelpers = {
256256

257-
mono_wasm_change_case_invariant: (src: number, srcLength: number, dst: number, dstLength: number, toUpper: number) => VoidPtr;
258257
mono_wasm_change_case: (culture: number, cultureLength: number, src: number, srcLength: number, dst: number, dstLength: number, toUpper: number) => VoidPtr;
259258
mono_wasm_compare_string: (culture: number, cultureLength: number, str1: number, str1Length: number, str2: number, str2Length: number, options: number, resultPtr: Int32Ptr) => VoidPtr;
260259
mono_wasm_starts_with: (culture: number, cultureLength: number, str1: number, str1Length: number, str2: number, str2Length: number, options: number, resultPtr: Int32Ptr) => VoidPtr;

0 commit comments

Comments
 (0)