Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid loading ICU data early during mono startup #100587

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/testing/linker/trimmingTests.targets
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@
<Output TaskParameter="ExitCode" PropertyName="ExecutionExitCode" />
</Exec>

<Error Condition="'$(ExecutionExitCode)' != '100'" Text="Error: [Failed Test]: %(TestConsoleApps.ProjectCompileItems). The Command %(TestConsoleApps.TestCommand) return a non-success exit code." ContinueOnError="ErrorAndContinue" />
<Error Condition="'$(ExecutionExitCode)' != '100'" Text="Error: [Failed Test]: %(TestConsoleApps.ProjectCompileItems). The Command %(TestConsoleApps.TestCommand) return a non-success exit code $(ExecutionExitCode)." ContinueOnError="ErrorAndContinue" />
</Target>

<Target Name="Build" DependsOnTargets="ExecuteApplications" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,11 @@ private static CultureData CreateCultureWithInvariantData()
// all available calendar type(s). The first one is the default calendar
invariant._waCalendars = new CalendarId[] { CalendarId.GREGORIAN };

#if TARGET_BROWSER
if (!GlobalizationMode.InvariantFast)
#else
if (!GlobalizationMode.Invariant)
#endif
{
// Store for specific data about each calendar
invariant._calendars = new CalendarData[CalendarData.MAX_CALENDARS];
Expand All @@ -648,7 +652,11 @@ private static CultureData CreateCultureWithInvariantData()
invariant._iDefaultMacCodePage = 10000; // default macintosh code page
invariant._iDefaultEbcdicCodePage = 037; // default EBCDIC code page

#if TARGET_BROWSER
if (GlobalizationMode.InvariantFast)
#else
if (GlobalizationMode.Invariant)
#endif
{
invariant._sLocalizedCountry = invariant._sNativeCountry;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ private static partial class Settings
// This allows for the whole Settings nested class to be trimmed when Invariant=true, and allows for the Settings
// static cctor (on Unix) to be preserved when Invariant=false.
internal static bool Invariant => Settings.Invariant;

ilonatommy marked this conversation as resolved.
Show resolved Hide resolved
#if TARGET_BROWSER
// same as GlobalizationMode.Invariant but doesn't trigger ICU load in GlobalizationMode.Settings.cctor during runtime startup
internal static bool InvariantFast { get; } = AppContextConfigHelper.GetBooleanConfig("System.Globalization.Invariant", "DOTNET_SYSTEM_GLOBALIZATION_INVARIANT");
#endif

#if TARGET_MACCATALYST || TARGET_IOS || TARGET_TVOS || TARGET_BROWSER
internal static bool Hybrid => Settings.Hybrid;
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ static int Main(string[] args)
{
return -3;
}

// Ensure the internal GlobalizationMode class is trimmed correctly.
Type globalizationMode = GetCoreLibType("System.Globalization.GlobalizationMode");

if (OperatingSystem.IsWindows())
if (OperatingSystem.IsWindows() || OperatingSystem.IsBrowser())
{
string allowedMember = OperatingSystem.IsWindows() ? "UseNls" : "InvariantFast";
foreach (MemberInfo member in globalizationMode.GetMembers(allStatics))
{
// properties and their backing getter methods are OK
Expand All @@ -41,8 +42,8 @@ static int Main(string[] args)
continue;
}

// Windows still contains a static cctor and a backing field for UseNls.
if (member is ConstructorInfo || (member is FieldInfo field && field.Name.Contains("UseNls")))
// Windows still contains a static cctor and a backing field for UseNls or InvariantFast.
if (member is ConstructorInfo || (member is FieldInfo field && field.Name.Contains(allowedMember)))
{
continue;
}
Expand All @@ -52,10 +53,10 @@ static int Main(string[] args)
return -4;
}
}
// On non Windows platforms, the full type is trimmed.
// On non Windows platforms, the full type is trimmed, unless it's Browser where we use FastInvariant for lazy ICU loading
else if (globalizationMode is not null)
{
Console.WriteLine("It is expected to have System.Globalization.GlobalizationMode type trimmed in non-Windows platforms");
Console.WriteLine("It is expected to have System.Globalization.GlobalizationMode type trimmed in non-Windows and non-Browser platforms");
return -5;
}

Expand Down
5 changes: 2 additions & 3 deletions src/mono/browser/runtime/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import type { AssetEntryInternal } from "./types/internal";

import cwraps from "./cwraps";
import { mono_wasm_load_icu_data } from "./icu";
import { mono_wasm_prepare_icu_data } from "./icu";
import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
import { mono_log_info, mono_log_debug, parseSymbolMapFile } from "./logging";
import { mono_wasm_load_bytes_into_heap_persistent } from "./memory";
Expand Down Expand Up @@ -85,8 +85,7 @@ export function instantiate_asset (asset: AssetEntry, url: string, bytes: Uint8A
} else if (asset.behavior === "pdb") {
cwraps.mono_wasm_add_assembly(virtualName, offset!, bytes.length);
} else if (asset.behavior === "icu") {
if (!mono_wasm_load_icu_data(offset!))
Module.err(`Error loading ICU asset ${asset.name}`);
mono_wasm_prepare_icu_data(offset!);
} else if (asset.behavior === "resource") {
cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture || "", offset!, bytes.length);
}
Expand Down
15 changes: 13 additions & 2 deletions src/mono/browser/runtime/icu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,19 @@
import cwraps from "./cwraps";
import { VoidPtr } from "./types/emscripten";


let icuDataOffset: VoidPtr | null = null;
// @offset must be the address of an ICU data archive in the native heap.
// returns true on success.
export function mono_wasm_load_icu_data (offset: VoidPtr): boolean {
return (cwraps.mono_wasm_load_icu_data(offset)) === 1;
export function mono_wasm_prepare_icu_data (offset: VoidPtr) {
icuDataOffset = offset;
}

export function mono_wasm_load_icu_data () {
if (icuDataOffset === null) {
return;
}
if (!cwraps.mono_wasm_load_icu_data(icuDataOffset!)) {
throw new Error("Failed to load ICU data");
}
}
3 changes: 3 additions & 0 deletions src/mono/browser/runtime/startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { runtimeList } from "./exports";
import { nativeAbort, nativeExit } from "./run";
import { mono_wasm_init_diagnostics } from "./diagnostics";
import { replaceEmscriptenPThreadInit } from "./pthreads/worker-thread";
import { mono_wasm_load_icu_data } from "./icu";

export async function configureRuntimeStartup (module: DotnetModuleInternal): Promise<void> {
if (!module.out) {
Expand Down Expand Up @@ -307,6 +308,8 @@ async function onRuntimeInitializedAsync (userOnRuntimeInitialized: () => void)
await start_runtime();
}

mono_wasm_load_icu_data();

if (WasmEnableThreads) {
await runtimeHelpers.afterIOStarted.promise;
}
Expand Down
Loading