Skip to content

Commit

Permalink
[browser] Separate hybrid globalization JS code to es6 module (dotnet…
Browse files Browse the repository at this point in the history
…#101543)

* dotnet.hybrid.js module creation when MT is off

* Importing module

* Remove globalization code from main module, create the globalization module for ST runtime.

* Fix: `runtimeHelper` is not null if we cache it on module init.

* Export functions used by hybrid globalization only when HG is on.

* Do not try to call when methods are not initialized.

* `mono_wasm_get_locale_info` is used also in non-HG mode, add it to the main module.

* Check WBT for presence of globalization module if HG is switched on.

* Fix MT globalization tests - NativeName is fixed there.

* Globalization module is not expected to change on relink.

* Fix blazor's assets.
  • Loading branch information
ilonatommy authored and Ruihan-Yin committed May 30, 2024
1 parent 3d6ffc5 commit 17fd264
Show file tree
Hide file tree
Showing 42 changed files with 448 additions and 185 deletions.
1 change: 1 addition & 0 deletions eng/liveBuilds.targets
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@
$(LibrariesNativeArtifactsPath)dotnet.js;
$(LibrariesNativeArtifactsPath)dotnet.js.map;
$(LibrariesNativeArtifactsPath)dotnet.native.js;
$(LibrariesNativeArtifactsPath)dotnet.globalization.js;
$(LibrariesNativeArtifactsPath)dotnet.runtime.js;
$(LibrariesNativeArtifactsPath)dotnet.runtime.js.map;
$(LibrariesNativeArtifactsPath)dotnet.d.ts;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@
<PlatformManifestFileEntry Include="dotnet.runtime.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.runtime.js.map" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.native.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.globalization.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.native.worker.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.native.js.symbols" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.d.ts" IsNative="true" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ private unsafe string IcuGetTimeFormatString(bool shortFormat)

private string IcuGetLanguageDisplayName(string cultureName)
{
#if TARGET_BROWSER && !FEATURE_WASM_MANAGED_THREADS
#if TARGET_BROWSER
return JSGetNativeDisplayName(CultureInfo.CurrentUICulture.Name, cultureName);
#else
return IcuGetLocaleInfo(cultureName, LocaleStringData.LocalizedDisplayName, CultureInfo.CurrentUICulture.Name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ private static string NormalizeCultureName(string name, out bool isNeutralName)
{
return null;
}
#if TARGET_BROWSER && !FEATURE_WASM_MANAGED_THREADS
#if TARGET_BROWSER
// populate fields for which ICU does not provide data in Hybrid mode
if (GlobalizationMode.Hybrid && !string.IsNullOrEmpty(culture._sName))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ namespace System.Globalization.Tests
public class CultureInfoEnglishName
{
// Android has its own ICU, which doesn't 100% map to UsingLimitedCultures
// Browser uses JS to get the NativeName that is missing in ICU (in the singlethreaded runtime only)
public static bool SupportFullGlobalizationData =>
(!PlatformDetection.IsWasi || PlatformDetection.IsHybridGlobalizationOnApplePlatform) && !PlatformDetection.IsWasmThreadingSupported;
// Browser uses JS to get the NativeName that is missing in ICU
public static bool SupportFullGlobalizationData => !PlatformDetection.IsWasi || PlatformDetection.IsHybridGlobalizationOnApplePlatform;

public static IEnumerable<object[]> EnglishName_TestData()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ namespace System.Globalization.Tests
public class CultureInfoNativeName
{
// Android has its own ICU, which doesn't 100% map to UsingLimitedCultures
// Browser uses JS to get the NativeName that is missing in ICU (in the singlethreaded runtime only)
private static bool SupportFullIcuResources =>
(!PlatformDetection.IsWasi || PlatformDetection.IsHybridGlobalizationOnApplePlatform) && !PlatformDetection.IsWasmThreadingSupported;
// Browser uses JS to get the NativeName that is missing in ICU
private static bool SupportFullIcuResources => !PlatformDetection.IsWasi || PlatformDetection.IsHybridGlobalizationOnApplePlatform;

public static IEnumerable<object[]> NativeName_TestData()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ namespace System.Globalization.Tests
public class RegionInfoPropertyTests
{
// Android has its own ICU, which doesn't 100% map to UsingLimitedCultures
// Browser uses JS to get the NativeName that is missing in ICU (in the singlethreaded runtime only)
public static bool SupportFullGlobalizationData =>
(!PlatformDetection.IsWasi || PlatformDetection.IsHybridGlobalizationOnApplePlatform) && !PlatformDetection.IsWasmThreadingSupported;
// Browser uses JS to get the NativeName that is missing in ICU
public static bool SupportFullGlobalizationData => !PlatformDetection.IsWasi || PlatformDetection.IsHybridGlobalizationOnApplePlatform;

[Theory]
[InlineData("US", "US", "US")]
Expand Down
5 changes: 3 additions & 2 deletions src/mono/browser/browser.proj
Original file line number Diff line number Diff line change
Expand Up @@ -480,13 +480,14 @@
$(NativeBinDir)dotnet.native.js;
$(NativeBinDir)dotnet.d.ts;
$(NativeBinDir)package.json;
$(NativeBinDir)dotnet.native.wasm"
$(NativeBinDir)dotnet.native.wasm;
$(NativeBinDir)dotnet.globalization.js"
DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)"
SkipUnchangedFiles="true" />

<Copy SourceFiles="$(NativeBinDir)dotnet.native.worker.js"
DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)"
Condition="Exists('$(NativeBinDir)dotnet.native.worker.js')"
Condition="Exists('$(NativeBinDir)dotnet.native.worker.js')"
SkipUnchangedFiles="true" />

<Copy SourceFiles="$(NativeBinDir)dotnet.native.js.symbols"
Expand Down
7 changes: 7 additions & 0 deletions src/mono/browser/build/BrowserWasmApp.targets
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
<_HasDotnetJsWorker Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.native.worker.js'">true</_HasDotnetJsWorker>
<_HasDotnetJsSymbols Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.native.js.symbols'">true</_HasDotnetJsSymbols>
<_HasDotnetNativeJs Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.native.js'">true</_HasDotnetNativeJs>
<_HasDotnetGlobalizationJs Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.globalization.js'">true</_HasDotnetGlobalizationJs>
<HybridGlobalization Condition="'$(InvariantGlobalization)' == 'true'">false</HybridGlobalization>
<_WasmIcuDataFileName Condition="'$(HybridGlobalization)' != 'true' and '$(WasmIcuDataFileName)' != '' and Exists('$(WasmIcuDataFileName)')">$(WasmIcuDataFileName)</_WasmIcuDataFileName>
<_WasmIcuDataFileName Condition="'$(HybridGlobalization)' != 'true' and '$(WasmIcuDataFileName)' != '' and !Exists('$(WasmIcuDataFileName)')">$(MicrosoftNetCoreAppRuntimePackRidNativeDir)$(WasmIcuDataFileName)</_WasmIcuDataFileName>
Expand All @@ -88,6 +89,10 @@
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.native.wasm" Condition="'$(_HasDotnetWasm)' != 'true'" />
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.native.js" Condition="'$(_HasDotnetNativeJs)' != 'true'" />
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.native.worker.js" Condition="'$(_HasDotnetJsWorker)' != 'true' and Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.native.worker.js')" />
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.globalization.js"
Condition="'$(HybridGlobalization)' == 'true' and
'$(_HasDotnetGlobalizationJs)' != 'true' and
Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.globalization.js')" />
<WasmNativeAsset Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)dotnet.native.js.symbols"
Condition="'$(WasmEmitSymbolMap)' == 'true' and
'$(_HasDotnetJsSymbols)' != 'true' and
Expand Down Expand Up @@ -491,6 +496,7 @@
<ItemGroup>
<FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.native.wasm" />
<FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.native.js" />
<FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.globalization.js" Condition="'$(HybridGlobalization)' == 'true'" />
<FileWrites Include="$(_WasmIntermediateOutputPath)dotnet.native.js.symbols" Condition="'$(WasmEmitSymbolMap)' == 'true'" />
</ItemGroup>

Expand Down Expand Up @@ -546,6 +552,7 @@
<WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.native.wasm" />
<WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.native.js" />
<WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.native.worker.js" Condition="Exists('$(_WasmIntermediateOutputPath)dotnet.native.worker.js')" />
<WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.globalization.js" Condition="'$(HybridGlobalization)' == 'true' and Exists('$(_WasmIntermediateOutputPath)dotnet.globalization.js')" />
<WasmNativeAsset Include="$(_WasmIntermediateOutputPath)dotnet.native.js.symbols" Condition="'$(WasmEmitSymbolMap)' == 'true' and Exists('$(_WasmIntermediateOutputPath)dotnet.native.js.symbols')" />
<_WasmAssembliesInternal Remove="$(_WasmDedupAssembly)"/>
</ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions src/mono/browser/runtime/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import type { AssetEntryInternal } from "./types/internal";

import cwraps from "./cwraps";
import { mono_wasm_load_icu_data } from "./icu";
import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals";
import { Module, globalizationHelpers, 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";
import { endMeasure, MeasuredBlock, startMeasure } from "./profiler";
import { AssetEntry } from "./types";
import { VoidPtr } from "./types/emscripten";
import { setSegmentationRulesFromJson } from "./hybrid-globalization/grapheme-segmenter";

// this need to be run only after onRuntimeInitialized event, when the memory is ready
export function instantiate_asset (asset: AssetEntry, url: string, bytes: Uint8Array): void {
Expand All @@ -26,6 +25,7 @@ export function instantiate_asset (asset: AssetEntry, url: string, bytes: Uint8A
switch (asset.behavior) {
case "dotnetwasm":
case "js-module-threads":
case "js-module-globalization":
case "symbols":
case "segmentation-rules":
// do nothing
Expand Down Expand Up @@ -110,7 +110,7 @@ export async function instantiate_segmentation_rules_asset (pendingAsset: AssetE
try {
const response = await pendingAsset.pendingDownloadInternal!.response;
const json = await response.json();
setSegmentationRulesFromJson(json);
globalizationHelpers.setSegmentationRulesFromJson(json);
} catch (error: any) {
mono_log_info(`Error loading static json asset ${pendingAsset.name}: ${JSON.stringify(error)}`);
}
Expand Down
3 changes: 2 additions & 1 deletion src/mono/browser/runtime/corebindings.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ void bindings_initialize_internals (void)
mono_add_internal_call ("Interop/Runtime::AssemblyGetEntryPoint", mono_wasm_assembly_get_entry_point);
mono_add_internal_call ("Interop/Runtime::BindAssemblyExports", mono_wasm_bind_assembly_exports);
mono_add_internal_call ("Interop/Runtime::GetAssemblyExport", mono_wasm_get_assembly_export);
mono_add_internal_call ("System.ConsolePal::Clear", mono_wasm_console_clear);

// HybridGlobalization
mono_add_internal_call ("Interop/JsGlobalization::ChangeCaseInvariant", mono_wasm_change_case_invariant);
mono_add_internal_call ("Interop/JsGlobalization::ChangeCase", mono_wasm_change_case);
mono_add_internal_call ("Interop/JsGlobalization::CompareString", mono_wasm_compare_string);
Expand All @@ -113,7 +115,6 @@ void bindings_initialize_internals (void)
mono_add_internal_call ("Interop/JsGlobalization::GetCultureInfo", mono_wasm_get_culture_info);
mono_add_internal_call ("Interop/JsGlobalization::GetFirstDayOfWeek", mono_wasm_get_first_day_of_week);
mono_add_internal_call ("Interop/JsGlobalization::GetFirstWeekOfYear", mono_wasm_get_first_week_of_year);
mono_add_internal_call ("System.ConsolePal::Clear", mono_wasm_console_clear);
}

static MonoAssembly* _mono_wasm_assembly_load (char *assembly_name)
Expand Down
5 changes: 5 additions & 0 deletions src/mono/browser/runtime/dotnet.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ interface ResourceGroups {
corePdb?: ResourceList;
pdb?: ResourceList;
jsModuleWorker?: ResourceList;
jsModuleGlobalization?: ResourceList;
jsModuleNative: ResourceList;
jsModuleRuntime: ResourceList;
wasmSymbols?: ResourceList;
Expand Down Expand Up @@ -360,6 +361,10 @@ type SingleAssetBehaviors =
* The javascript module for emscripten.
*/
| "js-module-native"
/**
* The javascript module for hybrid globalization.
*/
| "js-module-globalization"
/**
* Typically blazor.boot.json
*/
Expand Down
2 changes: 2 additions & 0 deletions src/mono/browser/runtime/es6/dotnet.es6.lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ function createWasmImportStubsFrom(collection) {
// we will replace them with the real implementation in replace_linker_placeholders
function injectDependencies() {
createWasmImportStubsFrom(methodIndexByName.mono_wasm_imports);
// mono_wasm_hybrid_globalization_imports is empty in non-hybrid globalization mode
createWasmImportStubsFrom(methodIndexByName.mono_wasm_hybrid_globalization_imports);

#if USE_PTHREADS
createWasmImportStubsFrom(methodIndexByName.mono_wasm_threads_imports);
Expand Down
21 changes: 4 additions & 17 deletions src/mono/browser/runtime/exports-binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ import { mono_wasm_diagnostic_server_on_runtime_server_init, mono_wasm_event_pip
import { mono_wasm_diagnostic_server_stream_signal_work_available } from "./diagnostics/server_pthread/stream-queue";
import { mono_log_warn, mono_wasm_console_clear, mono_wasm_trace_logger } from "./logging";
import { mono_wasm_profiler_leave, mono_wasm_profiler_enter } from "./profiler";
import { mono_wasm_change_case, mono_wasm_change_case_invariant } from "./hybrid-globalization/change-case";
import { mono_wasm_compare_string, mono_wasm_ends_with, mono_wasm_starts_with, mono_wasm_index_of } from "./hybrid-globalization/collations";
import { mono_wasm_get_calendar_info } from "./hybrid-globalization/calendar";

import { mono_wasm_get_culture_info } from "./hybrid-globalization/culture-info";
import { mono_wasm_get_locale_info, mono_wasm_get_first_day_of_week, mono_wasm_get_first_week_of_year } from "./hybrid-globalization/locales";
import { mono_wasm_browser_entropy } from "./crypto";
import { mono_wasm_cancel_promise } from "./cancelable-promise";

Expand All @@ -33,6 +27,7 @@ import {
} from "./pthreads";
import { mono_wasm_dump_threads } from "./pthreads/ui-thread";
import { mono_wasm_schedule_synchronization_context } from "./pthreads/shared";
import { mono_wasm_hybrid_globalization_imports } from "./globalization";

// the JS methods would be visible to EMCC linker and become imports of the WASM module

Expand Down Expand Up @@ -101,23 +96,15 @@ export const mono_wasm_imports = [
mono_wasm_invoke_jsimport_ST,
mono_wasm_resolve_or_reject_promise,
mono_wasm_cancel_promise,
mono_wasm_change_case_invariant,
mono_wasm_change_case,
mono_wasm_compare_string,
mono_wasm_starts_with,
mono_wasm_ends_with,
mono_wasm_index_of,
mono_wasm_get_calendar_info,
mono_wasm_get_culture_info,
mono_wasm_get_locale_info,
mono_wasm_get_first_day_of_week,
mono_wasm_get_first_week_of_year,
];


const wasmImports: Function[] = [
...mono_wasm_imports,
// threading exports, if threading is enabled
...mono_wasm_threads_imports,
// hybrid globalization exports
...mono_wasm_hybrid_globalization_imports,
];

export function replace_linker_placeholders (imports: WebAssembly.Imports) {
Expand Down
6 changes: 6 additions & 0 deletions src/mono/browser/runtime/exports-linker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

import { mono_wasm_imports, mono_wasm_threads_imports } from "./exports-binding";
import gitHash from "consts:gitHash";
import { mono_wasm_hybrid_globalization_imports } from "./globalization";

export function export_linker_indexes_as_code (): string {
const indexByName: any = {
mono_wasm_imports: {},
mono_wasm_threads_imports: {},
mono_wasm_hybrid_globalization_imports: {},
};
let idx = 0;
for (const wi of mono_wasm_imports) {
Expand All @@ -18,6 +20,10 @@ export function export_linker_indexes_as_code (): string {
indexByName.mono_wasm_threads_imports[wi.name] = idx;
idx++;
}
for (const wi of mono_wasm_hybrid_globalization_imports) {
indexByName.mono_wasm_hybrid_globalization_imports[wi.name] = idx;
idx++;
}
return `
var gitHash = "${gitHash}";
var methodIndexByName = ${JSON.stringify(indexByName, null, 2)};
Expand Down
14 changes: 13 additions & 1 deletion src/mono/browser/runtime/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads";
import WasmEnableSIMD from "consts:wasmEnableSIMD";
import WasmEnableExceptionHandling from "consts:wasmEnableExceptionHandling";

import type { RuntimeAPI } from "./types";
import { GlobalizationMode, type RuntimeAPI } from "./types";

import { Module, exportedRuntimeAPI, loaderHelpers, passEmscriptenInternals, runtimeHelpers, setRuntimeGlobals, } from "./globals";
import { GlobalObjects, RuntimeHelpers } from "./types/internal";
Expand All @@ -25,6 +25,8 @@ import { forceDisposeProxies } from "./gc-handles";
import { mono_wasm_dump_threads } from "./pthreads";

import { threads_c_functions as tcwraps } from "./cwraps";
import { stringToUTF16, stringToUTF16Ptr, utf16ToString, utf16ToStringLoop } from "./strings";
import { localHeapViewU16, setI32, setU16_local } from "./memory";

export let runtimeList: RuntimeList;

Expand All @@ -47,6 +49,16 @@ function initializeExports (globalObjects: GlobalObjects): RuntimeAPI {
rh.dumpThreads = mono_wasm_dump_threads;
rh.mono_wasm_print_thread_dump = () => tcwraps.mono_wasm_print_thread_dump();
}
if (loaderHelpers.config.globalizationMode === GlobalizationMode.Hybrid) {
rh.stringToUTF16 = stringToUTF16;
rh.stringToUTF16Ptr = stringToUTF16Ptr;
rh.utf16ToString = utf16ToString;
rh.utf16ToStringLoop = utf16ToStringLoop;
rh.localHeapViewU16 = localHeapViewU16;
rh.setU16_local = setU16_local;
rh.setI32 = setI32;
}

Object.assign(runtimeHelpers, rh);

const API = export_api();
Expand Down
Loading

0 comments on commit 17fd264

Please sign in to comment.