Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e8c094d
Hacking..
maraf Oct 31, 2022
50439ab
Copy memory after views are created.
maraf Nov 8, 2022
9fbac0d
Import and export memory.
maraf Nov 9, 2022
b119017
JSImport and JSExport.
maraf Nov 9, 2022
bfe721e
Fix args.
maraf Nov 9, 2022
53f44f3
Change memory extension so dotnet serve can brotli compress it.
maraf Nov 9, 2022
31317b9
Drop spare assets.
maraf Nov 9, 2022
7720592
WIP
maraf Nov 14, 2022
17ea006
WIP
maraf Nov 15, 2022
7318f0a
Split, nodejs.
maraf Nov 15, 2022
8b70764
Renames. Initial heap size.
maraf Nov 16, 2022
4034401
Take the snapshot before user cctors.
maraf Nov 24, 2022
2e97e55
Automate taking memory snapshot.
maraf Nov 24, 2022
4f26a01
Use correct AppBundle path.
maraf Nov 24, 2022
5f1e453
Use memory snapshots in uni tests.
maraf Nov 24, 2022
594cb9d
Clean up.
maraf Nov 25, 2022
c8742ff
Dummy fetch polyfill for V8.
maraf Nov 25, 2022
ab88572
Use node from emsdk.
maraf Nov 29, 2022
1a63251
Cleanup sample.
maraf Nov 29, 2022
bb43b98
Support for built-in memory.
maraf Nov 29, 2022
2a4d710
Disable advanced sample on CI.
maraf Nov 29, 2022
f6db54e
Use INITIAL_MEMORY in wasm.proj
maraf Nov 30, 2022
752517e
Use wa-edit to medge snapshot into dotnet.wasm
maraf Nov 30, 2022
d3fe16f
Update wa-edit path for CI.
maraf Nov 30, 2022
b17bfaa
One more round on how to use dotnet tool
maraf Dec 1, 2022
bfdd2fb
Rewrite wa-edit as temporal msbuild task.
maraf Dec 5, 2022
0d39804
ICU+timezones
maraf Dec 7, 2022
f2508f9
More debug message for creating snapshot.
maraf Dec 7, 2022
ac5a7e4
Fix need for ICU
maraf Dec 7, 2022
eb12295
Initial heap ~64MB and print snapshot size
maraf Dec 7, 2022
15949d0
Update browser-bench.
maraf Dec 8, 2022
fc087c8
Optional path to node.
maraf Dec 15, 2022
088a346
Zero out unmanaged memory.
maraf Jan 9, 2023
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
1 change: 1 addition & 0 deletions src/libraries/tests.proj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<ProjectExclusions Include="$(MonoProjectRoot)sample\wasm\browser-webpack\Wasm.Browser.WebPack.Sample.csproj" />
<ProjectExclusions Include="$(MonoProjectRoot)sample\wasm\node-webpack\Wasm.Node.WebPack.Sample.csproj" />
<ProjectExclusions Include="$(MonoProjectRoot)sample\wasm\browser-nextjs\Wasm.Browser.NextJs.Sample.csproj" />
<ProjectExclusions Include="$(MonoProjectRoot)sample\wasm\browser-advanced\Wasm.Advanced.Sample.csproj" />

<!-- These tests are completely disabled on wasm -->
<ProjectExclusions Include="$(MSBuildThisFileDirectory)System.Text.RegularExpressions/tests/System.Text.RegularExpressions.Generators.Tests/System.Text.RegularExpressions.Generators.Tests.csproj" />
Expand Down
9 changes: 9 additions & 0 deletions src/mono/sample/wasm/browser-bench/frame-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,16 @@ try {
}

const runtime = await dotnet
.withConfig({
mainAssemblyName: "Wasm.Browser.Bench.Sample.dll",
assets: [{
behavior: "dotnetwasm",
name: "dotnet.wasm"
}],
memory: true
})
.withModuleConfig({
configSrc: null,
printErr: () => undefined,
print: () => undefined,
onConfigLoaded: (config) => {
Expand Down
11 changes: 11 additions & 0 deletions src/mono/sample/wasm/browser-bench/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ try {
.withRuntimeOptions(["--jiterpreter-stats-enabled"])
.withElementOnExit()
.withExitCodeLogging()
.withConfig({
mainAssemblyName: "Wasm.Browser.Bench.Sample.dll",
assets: [{
behavior: "dotnetwasm",
name: "dotnet.wasm"
}],
memory: true
})
.withModuleConfig({
configSrc: null,
})
.create();

await mainApp.init(runtime);
Expand Down
12 changes: 9 additions & 3 deletions src/mono/sample/wasm/browser/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ namespace Sample
{
public partial class Test
{
public static readonly DateTime Created = DateTime.Now;
public static readonly string Location = GetLocation();

public static int Main(string[] args)
{
DisplayMeaning(42);
Console.WriteLine($".NET, args({args.Length}): {String.Join(", ", args)}, static location: '{Location}', location: '{GetLocation()}', created: '{Created.ToString("yyyy-MM-dd HH:mm:ss")}'");
return 0;
}

[JSImport("Sample.Test.displayMeaning", "main.js")]
internal static partial void DisplayMeaning(int meaning);
[JSImport("location.href", "main.js")]
internal static partial string GetLocation();

[JSExport]
internal static string Greet() => "JSExport";
}
}
10 changes: 10 additions & 0 deletions src/mono/sample/wasm/browser/Wasm.Browser.Sample.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\DefaultBrowserSample.targets" />
<PropertyGroup>
<WasmBuildNative>true</WasmBuildNative>
<WasmDebugLevel>0</WasmDebugLevel>
<EmccInitialHeapSize>105250816</EmccInitialHeapSize>
<!--
<WasmNativeStrip>false</WasmNativeStrip>
-->
<_SampleProject>Wasm.Browser.Sample.csproj</_SampleProject>
</PropertyGroup>
<ItemGroup>
<WasmExtraFilesToDeploy Include="node-capture.js" />
<WasmExtraFilesToDeploy Include="node-import.js" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion src/mono/sample/wasm/browser/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<body>
<h3 id="header">Wasm Browser Sample</h3>
Answer to the Ultimate Question of Life, the Universe, and Everything is : <span id="out"></span>
<span id="out"></span>
</body>

</html>
38 changes: 18 additions & 20 deletions src/mono/sample/wasm/browser/main.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import { dotnet, exit } from './dotnet.js'
import { dotnet } from "./dotnet.js"

function displayMeaning(meaning) {
document.getElementById("out").innerHTML = `${meaning}`;
}
const { setModuleImports, getAssemblyExports } = await dotnet
.withConfig({
mainAssemblyName: "Wasm.Browser.Sample.dll",
assets: [{
behavior: "dotnetwasm",
name: "dotnet.wasm"
}],
memory: true
})
.withModuleConfig({
configSrc: null
})
.create();

try {
const { setModuleImports } = await dotnet
.withElementOnExit()
.create();
setModuleImports("main.js", { location: { href: () => window.location.href } });

setModuleImports("main.js", {
Sample: {
Test: {
displayMeaning
}
}
});
await dotnet.withApplicationArguments("Single file .NET").run();

await dotnet.run();
}
catch (err) {
exit(2, err);
}
const exports = await getAssemblyExports("Wasm.Browser.Sample.dll");
console.log(exports.Sample.Test.Greet());
18 changes: 18 additions & 0 deletions src/mono/sample/wasm/browser/node-capture.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
async function exportMemory(memory) { // NodeJS
const require = await import('module').then(mod => mod.createRequire(import.meta.url));
const fs = require("fs");
fs.promises.writeFile("./memory.dat", memory);
}

async function runtime1() {
console.log("Runtime 1");
const dotnet = (await import("./dotnet.js")).dotnet;
const { setModuleImports, getAssemblyExports, getConfig, Module } = await dotnet.create();

// setModuleImports("main.js", { location: { href: () => "window.location.href" } });
// const exports = getAssemblyExports(getConfig().mainAssemblyName);

await exportMemory(Module.HEAP8);
}

runtime1();
34 changes: 34 additions & 0 deletions src/mono/sample/wasm/browser/node-import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

async function importMemory() { // NodeJS
const require = await import('module').then(mod => mod.createRequire(import.meta.url));
const fs = require("fs");
const buffer = await fs.promises.readFile("./memory.dat");
return new Int8Array(buffer);
}

async function runtime2() {
console.log("Runtime 2");
const dotnet = (await import("./dotnet.js?2")).dotnet;
const memory = await importMemory();
const { setModuleImports, getAssemblyExports, getConfig } = await dotnet
.withConfig({
mainAssemblyName: "Wasm.Browser.Sample.dll",
assets: [{
behavior: "dotnetwasm",
name: "dotnet.wasm"
}],
memory: memory
})
.withModuleConfig({
configSrc: null
})
.create();

setModuleImports("main.js", { location: { href: () => "window.location.href" } });

await dotnet.withApplicationArguments("Runtime 2").run();
}

runtime2();
32 changes: 30 additions & 2 deletions src/mono/wasm/build/WasmApp.Native.targets
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<!-- not really meant to be used w/o WasmApp.targets -->

<UsingTask TaskName="ManagedToNativeGenerator" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
<UsingTask TaskName="AppendMemorySnapshotToWasmFile" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />
<UsingTask TaskName="Microsoft.WebAssembly.Build.Tasks.EmccCompile" AssemblyFile="$(WasmAppBuilderTasksAssemblyPath)" />

<PropertyGroup>
Expand Down Expand Up @@ -58,13 +59,15 @@
<EmscriptenSdkToolsPath Condition="'$(EmscriptenSdkToolsPath)' != ''" >$([MSBuild]::NormalizeDirectory($(EmscriptenSdkToolsPath)))</EmscriptenSdkToolsPath>
<EmscriptenNodeToolsPath Condition="'$(EmscriptenNodeToolsPath)' != ''" >$([MSBuild]::NormalizeDirectory($(EmscriptenNodeToolsPath)))</EmscriptenNodeToolsPath>
<EmscriptenUpstreamBinPath Condition="'$(EmscriptenUpstreamBinPath)' != ''">$([MSBuild]::NormalizeDirectory($(EmscriptenUpstreamBinPath)))</EmscriptenUpstreamBinPath>

<EmscriptenNodeExecutable>$([MSBuild]::NormalizePath($(EmscriptenNodeToolsPath), 'bin', 'node$(_ExeExt)'))</EmscriptenNodeExecutable>
</PropertyGroup>

<!-- Environment variables required for running emsdk commands like `emcc` -->
<ItemGroup Condition="'$(EmscriptenSdkToolsPath)' != ''">
<EmscriptenEnvVars Include="DOTNET_EMSCRIPTEN_LLVM_ROOT=$(EmscriptenSdkToolsPath)bin" />
<EmscriptenEnvVars Include="DOTNET_EMSCRIPTEN_BINARYEN_ROOT=$(EmscriptenSdkToolsPath)" />
<EmscriptenEnvVars Include="DOTNET_EMSCRIPTEN_NODE_JS=$([MSBuild]::NormalizePath($(EmscriptenNodeToolsPath), 'bin', 'node$(_ExeExt)'))" />
<EmscriptenEnvVars Include="DOTNET_EMSCRIPTEN_NODE_JS=$(EmscriptenNodeExecutable)" />
</ItemGroup>

<ItemGroup>
Expand Down Expand Up @@ -188,7 +191,7 @@
<_EmccLinkRsp>$(_WasmIntermediateOutputPath)emcc-link.rsp</_EmccLinkRsp>

<EmccInitialHeapSize Condition="'$(EmccInitialHeapSize)' == ''">$(EmccTotalMemory)</EmccInitialHeapSize>
<EmccInitialHeapSize Condition="'$(EmccInitialHeapSize)' == ''">536870912</EmccInitialHeapSize>
<EmccInitialHeapSize Condition="'$(EmccInitialHeapSize)' == ''">67108864</EmccInitialHeapSize>
</PropertyGroup>

<ItemGroup>
Expand All @@ -197,6 +200,7 @@
<_EmccCommonFlags Include="$(_DefaultEmccFlags)" />
<_EmccCommonFlags Include="$(EmccFlags)" />
<_EmccCommonFlags Include="-s EXPORT_ES6=1" />
<_EmccCommonFlags Include="--tracing" Condition="'$(WasmMemorySnapshotClearUnmanaged)' != 'false'" />
<_EmccCommonFlags Include="-g" Condition="'$(WasmNativeStrip)' == 'false'" />
<_EmccCommonFlags Include="-v" Condition="'$(EmccVerbose)' != 'false'" />
<_EmccCommonFlags Include="-s DISABLE_EXCEPTION_CATCHING=0" Condition="'$(WasmEnableExceptionHandling)' == 'false'" />
Expand Down Expand Up @@ -657,4 +661,28 @@
<EmccDefaultExportedRuntimeMethods ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="false" Output="true" />
</ParameterGroup>
</UsingTask>

<Target Name="_WasmMemorySnapshot" DependsOnTargets="_SetupEmscripten"
Inputs="@(_WasmAssembliesInternal);$(WasmMainJSPath);$(WasmIcuDataFileName);@(WasmNativeAsset)"
Outputs="$(WasmAppDir)\memory.dat">
<PropertyGroup>
<WasmMemorySnapshotCaptureGenerator>wasm-memory-snapshot-capture.js</WasmMemorySnapshotCaptureGenerator>
<WasmMemorySnapshotFileName>memory.dat</WasmMemorySnapshotFileName>
<WasmMemorySnapshotNodeExecutable Condition="'$(WasmMemorySnapshotNodeExecutable)' == ''">$(EmscriptenNodeExecutable)</WasmMemorySnapshotNodeExecutable>
</PropertyGroup>
<ItemGroup>
<WasmMemorySnapshotCaptureLine Include="const dotnet = (await import('./dotnet.js')).dotnet%3B" />
<WasmMemorySnapshotCaptureLine Include="const { Module, totalFreed } = await dotnet.withMemoryCleanup().create()%3B" />
<WasmMemorySnapshotCaptureLine Include="(await import('fs')).promises.writeFile('./$(WasmMemorySnapshotFileName)', Module.HEAP8)%3B" />
<WasmMemorySnapshotCaptureLine Include="console.log(`Total zeroed unmanaged memory '${totalFreed}'`)%3B" />
</ItemGroup>
<WriteLinesToFile File="$(WasmAppDir)$(WasmMemorySnapshotCaptureGenerator)" Lines="@(WasmMemorySnapshotCaptureLine)" Overwrite="true" Encoding="UTF-8" />
<Message Text="Snapshot runner generated at $(WasmAppDir)$(WasmMemorySnapshotCaptureGenerator)" Importance="High" />
<Exec WorkingDirectory="$(WasmAppDir)" Command="$(WasmMemorySnapshotNodeExecutable) $(WasmMemorySnapshotCaptureGenerator)" />
<Message Text="Snapshot taken at $(WasmAppDir)$(WasmMemorySnapshotFileName)" Importance="High" />
<AppendMemorySnapshotToWasmFile Source="$(WasmAppDir)dotnet.wasm" Destination="$(WasmAppDir)dotnet2.wasm" DataSectionFile="$(WasmAppDir)$(WasmMemorySnapshotFileName)" />
<Delete Files="$(WasmAppDir)$(WasmMemorySnapshotCaptureGenerator);$(WasmAppDir)dotnet.wasm;$(WasmAppDir)$(WasmMemorySnapshotFileName)" />
<Move SourceFiles="$(WasmAppDir)dotnet2.wasm" DestinationFiles="$(WasmAppDir)dotnet.wasm" />
<Message Text="Memory snapshot merged into the $(WasmAppDir)dotnet.wasm" Importance="High" />
</Target>
</Project>
3 changes: 2 additions & 1 deletion src/mono/wasm/build/WasmApp.props
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
_WasmStripAOTAssemblies;
_WasmBuildNativeCore;
_WasmGenerateAppBundle;
_AfterWasmBuildApp
_AfterWasmBuildApp;
_WasmMemorySnapshot
</_WasmBuildCoreDependsOn>

<WasmBuildAppDependsOn>
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/build/WasmApp.targets
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@
<_WasmPThreadPoolSize Condition="'$(_WasmPThreadPoolSize)' == '' and ('$(WasmEnableThreads)' == 'true' or '$(WasmEnablePerfTracing)' == 'true')">-1</_WasmPThreadPoolSize>
</PropertyGroup>

<RemoveDir Directories="$(WasmAppDir)" />

<WasmAppBuilder
AppDir="$(WasmAppDir)"
MainJS="$(WasmMainJSPath)"
Expand Down
1 change: 1 addition & 0 deletions src/mono/wasm/runtime/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ export async function instantiate_wasm_asset(
let compiledModule: WebAssembly.Module;
if (typeof WebAssembly.instantiateStreaming === "function" && contentType === "application/wasm") {
if (runtimeHelpers.diagnosticTracing) console.debug("MONO_WASM: instantiate_wasm_module streaming");

const streamingResult = await WebAssembly.instantiateStreaming(response, wasmModuleImports!);
compiledInstance = streamingResult.instance;
compiledModule = streamingResult.module;
Expand Down
3 changes: 3 additions & 0 deletions src/mono/wasm/runtime/dotnet.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ type MonoConfig = {
* initial number of workers to add to the emscripten pthread pool
*/
pthreadPoolSize?: number;
memory?: Int8Array | boolean;
};
interface ResourceRequest {
name: string;
Expand Down Expand Up @@ -178,6 +179,8 @@ type DotnetModuleConfig = {
configSrc?: string;
onConfigLoaded?: (config: MonoConfig) => void | Promise<void>;
onDotnetReady?: () => void | Promise<void>;
onMalloc?: (pointer: number, length: number) => void;
onFree?: (pointer: number) => void;
imports?: any;
exports?: string[];
downloadResource?: (request: ResourceRequest) => LoadingResource | undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/runtime/icu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function mono_wasm_globalization_init(): void {
invariantMode = true;

if (!invariantMode) {
if (num_icu_assets_loaded_successfully > 0) {
if (num_icu_assets_loaded_successfully > 0 || config.memory) {
if (runtimeHelpers.diagnosticTracing) {
console.debug("MONO_WASM: ICU data archive(s) loaded, disabling invariant mode");
}
Expand Down
4 changes: 4 additions & 0 deletions src/mono/wasm/runtime/polyfills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ export function init_polyfills(replacements: EarlyReplacements): void {
replacements.updateGlobalBufferAndViews = (buffer: ArrayBufferLike) => {
originalUpdateGlobalBufferAndViews(buffer);
afterUpdateGlobalBufferAndViews(buffer);

if (runtimeHelpers.config.memory && runtimeHelpers.config.memory !== true) {
Module.HEAP8.set(runtimeHelpers.config.memory, 0);
}
};
}

Expand Down
20 changes: 19 additions & 1 deletion src/mono/wasm/runtime/run-outer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

// WARNING: code in this file is executed before any of the emscripten code, so there is very little initialized already
import { emscriptenEntrypoint, runtimeHelpers } from "./imports";
import { emscriptenEntrypoint, Module, runtimeHelpers } from "./imports";
import { setup_proxy_console } from "./logging";
import { mono_exit } from "./run";
import { DotnetModuleConfig, MonoConfig, MonoConfigInternal, mono_assert, RuntimeAPI } from "./types";
Expand Down Expand Up @@ -30,6 +30,7 @@ class HostBuilder implements DotnetHostBuilder {
private instance?: RuntimeAPI;
private applicationArguments?: string[];
private virtualWorkingDirectory?: string;
private totalFreed = 0;
private moduleConfig: DotnetModuleConfig = {
disableDotnet6Compatibility: true,
configSrc: "./mono-config.json",
Expand Down Expand Up @@ -251,6 +252,22 @@ class HostBuilder implements DotnetHostBuilder {
}
}

withMemoryCleanup(): DotnetHostBuilder {
const pointers:any[] = [];
return this.withModuleConfig({
onMalloc: (pointer:number, length:number) => pointers[pointer] = length,
onFree: (pointer) => {
const length = pointers[pointer];
if (length) {
this.totalFreed += length;
for (let p = 0; p < length; p++) {
Module.HEAP8.set([0], pointer + p);
}
}
}
});
}

withApplicationArgumentsFromQuery(): DotnetHostBuilder {
try {
if (!globalThis.window) {
Expand Down Expand Up @@ -287,6 +304,7 @@ class HostBuilder implements DotnetHostBuilder {
mono_assert(this.moduleConfig, "Null moduleConfig");
mono_assert(this.moduleConfig.config, "Null moduleConfig.config");
this.instance = await emscriptenEntrypoint(this.moduleConfig);
(this.instance as any).totalFreed = this.totalFreed;
}
if (this.virtualWorkingDirectory) {
const FS = (this.instance!.Module as any).FS;
Expand Down
Loading