diff --git a/eng/testing/scenarios/BuildWasmAppsJobsList.txt b/eng/testing/scenarios/BuildWasmAppsJobsList.txt index 5ccb34b25e18ac..63c4453b4ff66b 100644 --- a/eng/testing/scenarios/BuildWasmAppsJobsList.txt +++ b/eng/testing/scenarios/BuildWasmAppsJobsList.txt @@ -36,7 +36,7 @@ Wasm.Build.Tests.TestAppScenarios.AppSettingsTests Wasm.Build.Tests.TestAppScenarios.LazyLoadingTests Wasm.Build.Tests.TestAppScenarios.LibraryInitializerTests Wasm.Build.Tests.TestAppScenarios.SatelliteLoadingTests -Wasm.Build.Tests.TestAppScenarios.DownloadResourceProgressTests +Wasm.Build.Tests.TestAppScenarios.ModuleConfigTests Wasm.Build.Tests.TestAppScenarios.SignalRClientTests Wasm.Build.Tests.WasmBuildAppTest Wasm.Build.Tests.WasmNativeDefaultsTests diff --git a/src/mono/browser/runtime/loader/assets.ts b/src/mono/browser/runtime/loader/assets.ts index 3ef34458dd839b..0cf7e8a6ab6ab6 100644 --- a/src/mono/browser/runtime/loader/assets.ts +++ b/src/mono/browser/runtime/loader/assets.ts @@ -12,6 +12,7 @@ import { mono_exit } from "./exit"; import { addCachedReponse, findCachedResponse } from "./assetsCache"; import { getIcuResourceName } from "./icu"; import { makeURLAbsoluteWithApplicationBase } from "./polyfills"; +import { mono_log_info } from "./logging"; let throttlingPromise: PromiseAndController | undefined; @@ -536,7 +537,7 @@ async function start_asset_download_sources (asset: AssetEntryInternal): Promise err.status = response.status; throw err; } else { - loaderHelpers.out(`optional download '${response.url}' for ${asset.name} failed ${response.status} ${response.statusText}`); + mono_log_info(`optional download '${response.url}' for ${asset.name} failed ${response.status} ${response.statusText}`); return undefined; } } diff --git a/src/mono/browser/runtime/loader/icu.ts b/src/mono/browser/runtime/loader/icu.ts index 0a7823497aa853..b9bf54eb31f10b 100644 --- a/src/mono/browser/runtime/loader/icu.ts +++ b/src/mono/browser/runtime/loader/icu.ts @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +import { mono_log_error } from "./logging"; import { GlobalizationMode, MonoConfig } from "../types"; import { ENVIRONMENT_IS_WEB, loaderHelpers } from "./globals"; import { mono_log_info, mono_log_debug } from "./logging"; @@ -18,7 +19,7 @@ export function init_globalization () { loaderHelpers.preferredIcuAsset = null; } else { const msg = "invariant globalization mode is inactive and no ICU data archives are available"; - loaderHelpers.err(`ERROR: ${msg}`); + mono_log_error(`ERROR: ${msg}`); throw new Error(msg); } } diff --git a/src/mono/browser/runtime/loader/run.ts b/src/mono/browser/runtime/loader/run.ts index e6cb897a0c95d9..5c5010a2a5356f 100644 --- a/src/mono/browser/runtime/loader/run.ts +++ b/src/mono/browser/runtime/loader/run.ts @@ -437,7 +437,7 @@ async function initializeModules (es6Modules: [RuntimeModuleExportsInternal, Nat const { default: emscriptenFactory } = es6Modules[1]; setRuntimeGlobals(globalObjectsRoot); initializeExports(globalObjectsRoot); - await configureRuntimeStartup(); + await configureRuntimeStartup(emscriptenModule); loaderHelpers.runtimeModuleLoaded.promise_control.resolve(); emscriptenFactory((originalModule: EmscriptenModuleInternal) => { diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index 24fbb1d16a04f2..85096a94aefea0 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -35,7 +35,23 @@ import { nativeAbort, nativeExit } from "./run"; import { mono_wasm_init_diagnostics } from "./diagnostics"; import { replaceEmscriptenPThreadInit } from "./pthreads/worker-thread"; -export async function configureRuntimeStartup (): Promise { +export async function configureRuntimeStartup (module: DotnetModuleInternal): Promise { + if (!module.out) { + // eslint-disable-next-line no-console + module.out = console.log.bind(console); + } + if (!module.err) { + // eslint-disable-next-line no-console + module.err = console.error.bind(console); + } + if (!module.print) { + module.print = module.out; + } + if (!module.printErr) { + module.printErr = module.err; + } + loaderHelpers.out = module.print; + loaderHelpers.err = module.printErr; await init_polyfills_async(); } @@ -49,17 +65,6 @@ export function configureEmscriptenStartup (module: DotnetModuleInternal): void module.locateFile = module.__locateFile = (path) => loaderHelpers.scriptDirectory + path; } - if (!module.out) { - // eslint-disable-next-line no-console - module.out = console.log.bind(console); - } - - if (!module.err) { - // eslint-disable-next-line no-console - module.err = console.error.bind(console); - } - loaderHelpers.out = module.out; - loaderHelpers.err = module.err; module.mainScriptUrlOrBlob = loaderHelpers.scriptUrl;// this is needed by worker threads // these all could be overridden on DotnetModuleConfig, we are chaing them to async below, as opposed to emscripten diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index 2017a86922423e..ca4bf09ecdf87a 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -433,6 +433,8 @@ export declare interface EmscriptenModuleInternal { runtimeKeepalivePop(): void; maybeExit(): void; __emscripten_thread_init(pthread_ptr: PThreadPtr, isMainBrowserThread: number, isMainRuntimeThread: number, canBlock: number): void; + print(message: string): void; + printErr(message: string): void; } /// A PromiseController encapsulates a Promise together with easy access to its resolve and reject functions. @@ -462,7 +464,7 @@ export type initializeExportsType = (globalObjects: GlobalObjects) => RuntimeAPI export type initializeReplacementsType = (replacements: EmscriptenReplacements) => void; export type afterInitializeType = (module: EmscriptenModuleInternal) => void; export type configureEmscriptenStartupType = (module: DotnetModuleInternal) => void; -export type configureRuntimeStartupType = () => Promise; +export type configureRuntimeStartupType = (module: DotnetModuleInternal) => Promise; export type configureWorkerStartupType = (module: DotnetModuleInternal) => Promise diff --git a/src/mono/sample/wasm/browser-advanced/main.js b/src/mono/sample/wasm/browser-advanced/main.js index f3c7f8db928f34..115514db72991f 100644 --- a/src/mono/sample/wasm/browser-advanced/main.js +++ b/src/mono/sample/wasm/browser-advanced/main.js @@ -64,6 +64,7 @@ try { console.log('user code Module.onDotnetReady'); }, postRun: () => { console.log('user code Module.postRun'); }, + out: (text) => { console.log("ADVANCED:" + text) }, }) .withResourceLoader((type, name, defaultUri, integrity, behavior) => { // loadBootResource could return string with unqualified name of resource. It assumes that we resolve it with document.baseURI diff --git a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DownloadResourceProgressTests.cs b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/ModuleConfigTests.cs similarity index 68% rename from src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DownloadResourceProgressTests.cs rename to src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/ModuleConfigTests.cs index 7cc55ebd07ae20..4dc20e7358aa5b 100644 --- a/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/DownloadResourceProgressTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/ModuleConfigTests.cs @@ -13,9 +13,9 @@ namespace Wasm.Build.Tests.TestAppScenarios; -public class DownloadResourceProgressTests : AppTestBase +public class ModuleConfigTests : AppTestBase { - public DownloadResourceProgressTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + public ModuleConfigTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) : base(output, buildContext) { } @@ -25,7 +25,7 @@ public DownloadResourceProgressTests(ITestOutputHelper output, SharedBuildPerTes [InlineData(true)] public async Task DownloadProgressFinishes(bool failAssemblyDownload) { - CopyTestAsset("WasmBasicTestApp", $"DownloadResourceProgressTests_{failAssemblyDownload}"); + CopyTestAsset("WasmBasicTestApp", $"ModuleConfigTests_DownloadProgressFinishes_{failAssemblyDownload}"); PublishProject("Debug"); var result = await RunSdkStyleAppForPublish(new( @@ -54,4 +54,24 @@ public async Task DownloadProgressFinishes(bool failAssemblyDownload) : "The download progress test did emit unexpected message about failing download" ); } + + [Fact] + public async Task OutErrOverrideWorks() + { + CopyTestAsset("WasmBasicTestApp", $"ModuleConfigTests_OutErrOverrideWorks"); + PublishProject("Debug"); + + var result = await RunSdkStyleAppForPublish(new( + Configuration: "Debug", + TestScenario: "OutErrOverrideWorks" + )); + Assert.True( + result.ConsoleOutput.Any(m => m.Contains("Emscripten out override works!")), + "Emscripten out override doesn't work" + ); + Assert.True( + result.ConsoleOutput.Any(m => m.Contains("Emscripten err override works!")), + "Emscripten err override doesn't work" + ); + } } diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/Common/Program.cs b/src/mono/wasm/testassets/WasmBasicTestApp/App/Common/Program.cs index 4f38c02031c7ec..eda19b81f506eb 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/Common/Program.cs +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/Common/Program.cs @@ -2,3 +2,4 @@ // The .NET Foundation licenses this file to you under the MIT license. System.Console.WriteLine("WasmBasicTestApp"); +System.Console.Error.WriteLine("WasmBasicTestApp stderr"); diff --git a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js index 3a01053875c0a6..5aeeb024096d56 100644 --- a/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js +++ b/src/mono/wasm/testassets/WasmBasicTestApp/App/wwwroot/main.js @@ -66,6 +66,18 @@ switch (testCase) { } }); break; + case "OutErrOverrideWorks": + dotnet.withModuleConfig({ + out: (message) => { + console.log("Emscripten out override works!"); + console.log(message) + }, + err: (message) => { + console.error("Emscripten err override works!"); + console.error(message) + }, + }); + break; } const { getAssemblyExports, getConfig, INTERNAL } = await dotnet.create(); @@ -97,6 +109,9 @@ try { case "DownloadResourceProgressTest": exit(0); break; + case "OutErrOverrideWorks": + dotnet.run(); + break; case "DebugLevelTest": testOutput("WasmDebugLevel: " + config.debugLevel); exit(0);