[Wasm] JS modularization (#61313)

Modularized dotnet.js
* By using emcc options MODULARIZE and EXPORT_ES6, depending on WasmEnableES6 to produce ES6 or CommonJS modules respectively.
* WasmEnableES6 enables WasmBuildNative because dotnet.js need to be re-linked
* CommonJS version is able to be loaded into global namespace and behaves as before this change.
* Added new es6/*.js and es6/*.js files which are included in dotnet.js creation. 
* Key aspects are documented in src/mono/wasm/runtime/modularize-dotnet.md of this PR.
* Improved dotnet.d.ts it is now generated into version control too, so that we could observe how it evolves.
* Removed legacy --testing argument and simplified is_testing logic in the the samples.
* Added browser-es6 sample app, which uses WasmEnableES6=true to compile dotnet.js as ES6 module.
* Added browser-legacy sample, which uses dotnet.js CommonJS module and loads it into global namespace.
* Added package.json into the nupkg

Co-authored-by: Ankit Jain <radical@gmail.com>
Co-authored-by: Katelyn Gadd <kg@luminance.org>
Co-authored-by: Marek Fišera <mara@neptuo.com>
This commit is contained in:
Pavel Savara 2021-12-02 10:38:00 +01:00 committed by GitHub
parent a61c1a3cf5
commit 1bd36302b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 1425 additions and 471 deletions

1
.gitignore vendored
View File

@ -186,6 +186,7 @@ node_modules/
*.metaproj
*.metaproj.tmp
bin.localpkg/
src/mono/wasm/runtime/dotnet.d.ts.sha256
# RIA/Silverlight projects
Generated_Code/

View File

@ -176,6 +176,8 @@
<LibrariesRuntimeFiles Condition="'$(TargetOS)' == 'Browser'"
Include="
$(LibrariesNativeArtifactsPath)dotnet.js;
$(LibrariesNativeArtifactsPath)dotnet.d.ts;
$(LibrariesNativeArtifactsPath)package.json;
$(LibrariesNativeArtifactsPath)dotnet.wasm;
$(LibrariesNativeArtifactsPath)dotnet.timezones.blat;
$(LibrariesNativeArtifactsPath)*.dat;"
@ -188,6 +190,15 @@
$(LibrariesNativeArtifactsPath)src\emcc-props.json;"
NativeSubDirectory="src"
IsNative="true" />
<LibrariesRuntimeFiles Condition="'$(TargetOS)' == 'Browser'"
Include="$(LibrariesNativeArtifactsPath)src\cjs\*.js"
NativeSubDirectory="src\cjs"
IsNative="true" />
<LibrariesRuntimeFiles Condition="'$(TargetOS)' == 'Browser'"
Include="$(LibrariesNativeArtifactsPath)src\es6\*.js"
NativeSubDirectory="src\es6"
IsNative="true" />
<LibrariesRuntimeFiles Condition="'$(TargetOS)' == 'Browser'"
Include="
$(LibrariesNativeArtifactsPath)include\wasm\*.h;"

View File

@ -138,6 +138,7 @@
<_WasmPropertyNames Include="WasmDedup" />
<_WasmPropertyNames Include="WasmLinkIcalls" />
<_WasmPropertyNames Include="WasmNativeStrip" />
<_WasmPropertyNames Include="WasmEnableES6" />
<_WasmPropertyNames Include="_WasmDevel" />
<_WasmPropertiesToPass

View File

@ -210,6 +210,7 @@
<PlatformManifestFileEntry Include="libmono-profiler-aot.a" IsNative="true" />
<PlatformManifestFileEntry Include="System.Private.Runtime.InteropServices.Javascript.dll" />
<PlatformManifestFileEntry Include="dotnet.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.d.ts" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.wasm" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.timezones.blat" IsNative="true" />
<PlatformManifestFileEntry Include="icudt.dat" IsNative="true" />
@ -218,10 +219,17 @@
<PlatformManifestFileEntry Include="icudt_EFIGS.dat" IsNative="true" />
<PlatformManifestFileEntry Include="icudt_optimal.dat" IsNative="true" />
<PlatformManifestFileEntry Include="icudt_optimal_no_CJK.dat" IsNative="true" />
<PlatformManifestFileEntry Include="runtime.iffe.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.d.ts" IsNative="true" />
<PlatformManifestFileEntry Include="library-dotnet.js" IsNative="true" />
<PlatformManifestFileEntry Include="pal_random.js" IsNative="true" />
<PlatformManifestFileEntry Include="package.json" IsNative="true" />
<PlatformManifestFileEntry Include="pal_random.lib.js" IsNative="true" />
<PlatformManifestFileEntry Include="runtime.cjs.iffe.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.cjs.lib.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.cjs.pre.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.cjs.post.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.cjs.extpost.js" IsNative="true" />
<PlatformManifestFileEntry Include="runtime.es6.iffe.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.es6.pre.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.es6.lib.js" IsNative="true" />
<PlatformManifestFileEntry Include="dotnet.es6.post.js" IsNative="true" />
<PlatformManifestFileEntry Include="corebindings.c" IsNative="true" />
<PlatformManifestFileEntry Include="driver.c" IsNative="true" />
<PlatformManifestFileEntry Include="pinvoke.c" IsNative="true" />

View File

@ -7,7 +7,7 @@ var Module = {
onConfigLoaded: function () {
MONO.config.environment_variables["DOTNET_MODIFIABLE_ASSEMBLIES"] = "debug";
},
onDotNetReady: function () {
onDotnetReady: function () {
App.init();
},
};

View File

@ -16,6 +16,9 @@
<Target Name="RunSampleWithV8" DependsOnTargets="BuildSampleInTree">
<Exec WorkingDirectory="bin/$(Configuration)/AppBundle" Command="v8 --expose_wasm main.js -- $(DOTNET_MONO_LOG_LEVEL) --run $(_SampleAssembly) $(Args)" IgnoreExitCode="true" />
</Target>
<Target Name="RunSampleWithNode" DependsOnTargets="BuildSampleInTree">
<Exec WorkingDirectory="bin/$(Configuration)/AppBundle" Command="node --expose_wasm main.js -- $(DOTNET_MONO_LOG_LEVEL) --run $(_SampleAssembly) $(Args)" IgnoreExitCode="true" />
</Target>
<Target Name="CheckServe">
<Exec Command="dotnet tool install -g dotnet-serve" IgnoreExitCode="true" />
</Target>

View File

@ -4,7 +4,7 @@
"use strict";
var Module = {
configSrc: "./mono-config.json",
onDotNetReady: () => {
onDotnetReady: () => {
try {
App.init();
} catch (error) {

View File

@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.CompilerServices;
namespace Sample
{
public class Test
{
public static void Main(string[] args)
{
Console.WriteLine ("Hello, World!");
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static int TestMeaning()
{
return 42;
}
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration>Debug</Configuration>
<WasmCopyAppZipToHelixTestDir Condition="'$(ArchiveTests)' == 'true'">true</WasmCopyAppZipToHelixTestDir>
<WasmMainJSPath>main.js</WasmMainJSPath>
<DebugSymbols>true</DebugSymbols>
<DebugType>embedded</DebugType>
<WasmDebugLevel>1</WasmDebugLevel>
<WasmEnableES6>true</WasmEnableES6>
</PropertyGroup>
<ItemGroup>
<WasmExtraFilesToDeploy Include="index.html" />
</ItemGroup>
<PropertyGroup>
<_SampleProject>Wasm.Browser.ES6.Sample.csproj</_SampleProject>
</PropertyGroup>
<Target Name="RunSample" DependsOnTargets="RunSampleWithBrowser" />
</Project>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<!-- Licensed to the .NET Foundation under one or more agreements. -->
<!-- The .NET Foundation licenses this file to you under the MIT license. -->
<html>
<head>
<title>Sample ES6</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="modulepreload" href="main.js" />
<link rel="modulepreload" href="dotnet.js" />
</head>
<body>
<h3 id="header">Wasm Browser ES6 Sample</h3>
Answer to the Ultimate Question of Life, the Universe, and Everything is : <span id="out"></span>
<script type='module' src="./main.js"></script>
</body>
</html>

View File

@ -0,0 +1,21 @@
import createDotnetRuntime from './dotnet.js'
const { MONO, BINDING, Module, RuntimeBuildInfo } = await createDotnetRuntime((api) => ({
disableDotnet6Compatibility: true,
configSrc: "./mono-config.json",
onAbort: () => {
wasm_exit(1);
},
}));
function wasm_exit(exit_code) {
console.log(`WASM EXIT ${exit_code}`);
}
const testMeaning = BINDING.bind_static_method("[Wasm.Browser.ES6.Sample] Sample.Test:TestMeaning");
const ret = testMeaning();
document.getElementById("out").innerHTML = `${ret} as computed on dotnet ver ${RuntimeBuildInfo.ProductVersion}`;
console.debug(`ret: ${ret}`);
let exit_code = ret == 42 ? 0 : 1;
wasm_exit(exit_code);

View File

@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.CompilerServices;
namespace Sample
{
public class Test
{
public static void Main(string[] args)
{
Console.WriteLine ("Hello, World!");
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static int TestMeaning()
{
return 42;
}
}
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration>Debug</Configuration>
<WasmCopyAppZipToHelixTestDir Condition="'$(ArchiveTests)' == 'true'">true</WasmCopyAppZipToHelixTestDir>
<WasmMainJSPath>main.js</WasmMainJSPath>
<DebugSymbols>true</DebugSymbols>
<DebugType>embedded</DebugType>
<WasmDebugLevel>1</WasmDebugLevel>
<WasmBuildNative>true</WasmBuildNative>
</PropertyGroup>
<ItemGroup>
<WasmExtraFilesToDeploy Include="index.html" />
</ItemGroup>
<PropertyGroup>
<_SampleProject>Wasm.Browser.LegacySample.csproj</_SampleProject>
</PropertyGroup>
<Target Name="RunSample" DependsOnTargets="RunSampleWithBrowser" />
</Project>

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<!-- Licensed to the .NET Foundation under one or more agreements. -->
<!-- The .NET Foundation licenses this file to you under the MIT license. -->
<html>
<head>
<title>Legacy global module sample</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h3 id="header">Wasm Browser Legacy Sample</h3>
Result from Sample.Test.TestMeaning: <span id="out"></span>
<script type='text/javascript'>
function set_exit_code(exit_code, reason) {
/* Set result in a tests_done element, to be read by xharness */
const tests_done_elem = document.createElement("label");
tests_done_elem.id = "tests_done";
tests_done_elem.innerHTML = exit_code.toString();
document.body.appendChild(tests_done_elem);
console.log(`WASM EXIT ${exit_code}`);
};
const App = {
init: function () {
const testMeaning = BINDING.bind_static_method("[Wasm.Browser.LegacySample] Sample.Test:TestMeaning");
const ret = testMeaning();
document.getElementById("out").innerHTML = ret;
console.debug(`ret: ${ret}`);
let exit_code = ret == 42 ? 0 : 1;
set_exit_code(exit_code);
},
};
</script>
<script type="text/javascript" src="main.js"></script>
<script defer src="dotnet.js"></script>
</body>
</html>

View File

@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
"use strict";
var Module = {
configSrc: "./mono-config.json",
onDotnetReady: () => {
try {
App.init();
} catch (error) {
set_exit_code(1, error);
throw (error);
}
},
onAbort: (error) => {
set_exit_code(1, error);
},
};

View File

@ -12,7 +12,7 @@ var Module = {
}
}
},
onDotNetReady: () => {
onDotnetReady: () => {
try {
Module.init();
} catch (error) {

View File

@ -24,7 +24,7 @@
};
const App = {
init: () => {
init: ({ MONO, BINDING, Module }) => {
const testMeaning = BINDING.bind_static_method("[Wasm.Browser.Sample] Sample.Test:TestMeaning");
const ret = testMeaning();
document.getElementById("out").innerHTML = ret;
@ -35,8 +35,8 @@
},
};
</script>
<script type="text/javascript" src="dotnet.js"></script>
<script type="text/javascript" src="main.js"></script>
<script defer src="dotnet.js"></script>
</body>

View File

@ -3,11 +3,12 @@
"use strict";
var Module = {
createDotnetRuntime(({ MONO, BINDING, Module }) => ({
disableDotnet6Compatibility: true,
configSrc: "./mono-config.json",
onDotNetReady: () => {
onDotnetReady: () => {
try {
App.init();
App.init({ MONO, BINDING, Module });
} catch (error) {
set_exit_code(1, error);
throw (error);
@ -16,4 +17,4 @@ var Module = {
onAbort: (error) => {
set_exit_code(1, error);
},
};
}));

View File

@ -93,7 +93,7 @@ $(NATIVE_BIN_DIR)/include/wasm:
$(BUILDS_OBJ_DIR):
mkdir -p $$@
$(NATIVE_BIN_DIR)/dotnet.js: runtime/driver.c runtime/pinvoke.c runtime/pinvoke.h runtime/corebindings.c $(NATIVE_BIN_DIR)/src/runtime.iffe.js runtime/library-dotnet.js $(SYSTEM_NATIVE_LIBDIR)/pal_random.js $(MONO_LIBS) $(EMCC_DEFAULT_RSP) | $(NATIVE_BIN_DIR)
$(NATIVE_BIN_DIR)/dotnet.js: runtime/driver.c runtime/pinvoke.c runtime/pinvoke.h runtime/corebindings.c $(NATIVE_BIN_DIR)/src/cjs/runtime.cjs.iffe.js runtime/cjs/dotnet.cjs.lib.js $(SYSTEM_NATIVE_LIBDIR)/pal_random.lib.js $(MONO_LIBS) $(EMCC_DEFAULT_RSP) | $(NATIVE_BIN_DIR)
$(DOTNET) build $(CURDIR)/wasm.proj $(_MSBUILD_WASM_BUILD_ARGS) /t:BuildWasmRuntimes $(MSBUILD_ARGS)
$(EMCC_DEFAULT_RSP): $(CURDIR)/wasm.proj | $(NATIVE_BIN_DIR)/src Makefile
@ -113,7 +113,7 @@ clean:
icu-files: $(wildcard $(ICU_LIBDIR)/*.dat) $(ICU_LIBDIR)/libicuuc.a $(ICU_LIBDIR)/libicui18n.a | $(NATIVE_BIN_DIR)
cp $^ $(NATIVE_BIN_DIR)
source-files: runtime/driver.c runtime/pinvoke.c runtime/corebindings.c runtime/library-dotnet.js $(SYSTEM_NATIVE_LIBDIR)/pal_random.js | $(NATIVE_BIN_DIR)/src
source-files: runtime/driver.c runtime/pinvoke.c runtime/corebindings.c runtime/cjs/dotnet.cjs.lib.js $(SYSTEM_NATIVE_LIBDIR)/pal_random.lib.js | $(NATIVE_BIN_DIR)/src
cp $^ $(NATIVE_BIN_DIR)/src
header-files: runtime/pinvoke.h | $(NATIVE_BIN_DIR)/include/wasm

View File

@ -32,6 +32,8 @@ Implementation:
- *after* any of the wasm build targets, use `AfterTargets="WasmBuildApp"` on that target
- Avoid depending on this target, because it is available only when the workload is installed. Use `$(WasmNativeWorkload)` to check if it is installed.
- `WasmEnableES6` will cause native re-link and produce `dotnet.js` as ES6 module. When `Module.disableDotnet6Compatibility` is set it would not pollute global namespace. Currently debugger doesn't work in that pure mode.
## `Publish`
Implementation:

View File

@ -106,6 +106,9 @@
<WasmBuildNative Condition="'$(RunAOTCompilation)' == 'true' and '$(RunAOTCompilationAfterBuild)' == 'true'">true</WasmBuildNative>
<WasmBuildNative Condition="'$(WasmBuildNative)' == '' and @(NativeFileReference->Count()) > 0" >true</WasmBuildNative>
<!-- need to re-link dotnet.js when targeting ES6 -->
<WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(WasmEnableES6)' == 'true'" >true</WasmBuildNative>
<WasmBuildNative Condition="'$(WasmBuildNative)' == ''">false</WasmBuildNative>
</PropertyGroup>
@ -114,6 +117,8 @@
<!-- AOT==true overrides WasmBuildNative -->
<WasmBuildNative Condition="'$(RunAOTCompilation)' == 'true'">true</WasmBuildNative>
<WasmBuildNative Condition="'$(WasmBuildNative)' == '' and @(NativeFileReference->Count()) > 0" >true</WasmBuildNative>
<!-- need to re-link dotnet.js when targeting ES6 -->
<WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(WasmEnableES6)' == 'true'" >true</WasmBuildNative>
<!-- not aot, not trimmed app, no reason to relink -->
<WasmBuildNative Condition="'$(WasmBuildNative)' == '' and '$(PublishTrimmed)' != 'true'">false</WasmBuildNative>
@ -181,6 +186,7 @@
<_EmccCommonFlags Include="$(_DefaultEmccFlags)" />
<_EmccCommonFlags Include="$(EmccFlags)" />
<_EmccCommonFlags Include="-s DISABLE_EXCEPTION_CATCHING=0" />
<_EmccCommonFlags Include="-s EXPORT_ES6=1" Condition="'$(WasmEnableES6)' == 'true'" />
<_EmccCommonFlags Include="-g" Condition="'$(WasmNativeStrip)' == 'false'" />
<_EmccCommonFlags Include="-v" Condition="'$(EmccVerbose)' != 'false'" />
@ -222,8 +228,18 @@
<_WasmRuntimePackSrcFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" />
<_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" Exclude="$(_WasmRuntimePackSrcDir)\*.iffe.js"/>
<_WasmExtraJSFile Include="$(_WasmRuntimePackSrcDir)\*.iffe.js" Kind="extern-pre-js" />
<!-- See src\mono\wasm\runtime\modularize-dotnet.md -->
<JSFileType Include="extpre.js" Kind="extern-pre-js" />
<JSFileType Include="iffe.js" Kind="extern-pre-js" />
<JSFileType Include="pre.js" Kind="pre-js" />
<JSFileType Include="lib.js" Kind="js-library" />
<JSFileType Include="post.js" Kind="post-js" />
<JSFileType Include="extpost.js" Kind="extern-post-js" />
<_WasmExtraJSFile Include="$(_WasmRuntimePackSrcDir)\*.%(JSFileType.Identity)" Kind="%(JSFileType.Kind)" />
<_WasmExtraJSFile Include="$(_WasmRuntimePackSrcDir)\cjs\*.%(JSFileType.Identity)" Kind="%(JSFileType.Kind)" Condition="'$(WasmEnableES6)' != 'true'" />
<_WasmExtraJSFile Include="$(_WasmRuntimePackSrcDir)\es6\*.%(JSFileType.Identity)" Kind="%(JSFileType.Kind)" Condition="'$(WasmEnableES6)' == 'true'" />
<_WasmNativeFileForLinking Include="@(NativeFileReference)" />
<EmscriptenEnvVars Include="EMSDK_PYTHON=$(EmscriptenPythonToolsPath)python.exe" Condition="'$(OS)' == 'Windows_NT'" />
@ -270,7 +286,7 @@
</ItemGroup>
</Target>
<Target Name="_WasmSelectRuntimeComponentsForLinking" Condition="'$(WasmNativeWorkload)' == true" DependsOnTargets="_MonoSelectRuntimeComponents" />
<Target Name="_WasmSelectRuntimeComponentsForLinking" Condition="'$(WasmNativeWorkload)' == 'true'" DependsOnTargets="_MonoSelectRuntimeComponents" />
<Target Name="_WasmCompileNativeFiles" DependsOnTargets="_CheckEmccIsExpectedVersion">
<PropertyGroup>
@ -351,14 +367,9 @@
<_WasmExtraJSFile Include="@(Content)" Condition="'%(Content.Extension)' == '.js'" />
<_EmccLinkStepArgs Include="@(_EmccLDFlags)" />
<_EmccLinkStepArgs Include="--js-library &quot;%(_DotnetJSSrcFile.Identity)&quot;" />
<_WasmLinkDependencies Include="@(_DotnetJSSrcFile)" />
<_EmccLinkStepArgs Include="--js-library &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" />
<_EmccLinkStepArgs Include="--pre-js &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' == 'pre-js'" />
<_EmccLinkStepArgs Include="--extern-pre-js &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' == 'extern-pre-js'" />
<_EmccLinkStepArgs Include="--post-js &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' == 'post-js'" />
<_WasmLinkDependencies Include="@(_WasmExtraJSFile)" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library' or '%(_WasmExtraJSFile.Kind)' == 'pre-js' or '%(_WasmExtraJSFile.Kind)' == 'post-js' or '%(_WasmExtraJSFile.Kind)' == 'extern-post-js'" />
<_EmccLinkStepArgs Include="--%(_WasmExtraJSFile.Kind) &quot;%(_WasmExtraJSFile.Identity)&quot;" Condition="'%(_WasmExtraJSFile.Kind)' != ''" />
<_WasmLinkDependencies Include="@(_WasmExtraJSFile)" />
<_EmccLinkStepArgs Include="&quot;%(_WasmNativeFileForLinking.Identity)&quot;" />
<_WasmLinkDependencies Include="@(_WasmNativeFileForLinking)" />

View File

@ -574,7 +574,7 @@ namespace DebuggerTests
// Trying to access object as an array
if (!DotnetObjectId.TryParse(c_obj_id, out var id) || id.Scheme != "object")
Assert.True(false, "Unexpected object id format. Maybe this test is out of sync with the object id format in library-dotnet.js?");
Assert.True(false, "Unexpected object id format. Maybe this test is out of sync with the object id format in dotnet.cjs.lib.js?");
if (!int.TryParse(id.Value, out var idNum))
Assert.True(false, "Expected a numeric value part of the object id: {c_obj_id}");

View File

@ -808,7 +808,7 @@ namespace DebuggerTests
return null;
var locals = frame_props.Value["result"];
// FIXME: Should be done when generating the list in library-dotnet.js, but not sure yet
// FIXME: Should be done when generating the list in dotnet.cjs.lib.js, but not sure yet
// whether to remove it, and how to do it correctly.
if (locals is JArray)
{

View File

@ -17,7 +17,7 @@ var Module = {
};
*/
},
onDotNetReady: () => {
onDotnetReady: () => {
App.init();
},
};

View File

@ -16,7 +16,12 @@ module.exports = {
"plugins": [
"@typescript-eslint"
],
"ignorePatterns": ["node_modules/**/*.*", "bin/**/*.*"],
"ignorePatterns": [
"node_modules/**/*.*",
"bin/**/*.*",
"cjs/*.js",
"es6/*.js",
],
"rules": {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "off",

View File

@ -24,8 +24,8 @@ target_link_libraries(dotnet
${NATIVE_BIN_DIR}/libSystem.IO.Compression.Native.a)
set_target_properties(dotnet PROPERTIES
LINK_DEPENDS "${NATIVE_BIN_DIR}/src/emcc-default.rsp;${NATIVE_BIN_DIR}/src/runtime.iffe.js;${SOURCE_DIR}/library-dotnet.js;${SYSTEM_NATIVE_DIR}/pal_random.js"
LINK_FLAGS "@${NATIVE_BIN_DIR}/src/emcc-default.rsp ${CONFIGURATION_LINK_FLAGS} -DENABLE_NETCORE=1 --extern-pre-js ${NATIVE_BIN_DIR}/src/runtime.iffe.js --js-library ${SOURCE_DIR}/library-dotnet.js --js-library ${SYSTEM_NATIVE_DIR}/pal_random.js"
LINK_DEPENDS "${NATIVE_BIN_DIR}/src/emcc-default.rsp;${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.pre.js;${NATIVE_BIN_DIR}/src/cjs/runtime.cjs.iffe.js;${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.lib.js;${NATIVE_BIN_DIR}/src/pal_random.lib.js;${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.post.js;${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.extpost.js;"
LINK_FLAGS "@${NATIVE_BIN_DIR}/src/emcc-default.rsp ${CONFIGURATION_LINK_FLAGS} -DENABLE_NETCORE=1 --extern-pre-js ${NATIVE_BIN_DIR}/src/cjs/runtime.cjs.iffe.js --pre-js ${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.pre.js --js-library ${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.lib.js --js-library ${NATIVE_BIN_DIR}/src/pal_random.lib.js --post-js ${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.post.js --extern-post-js ${NATIVE_BIN_DIR}/src/cjs/dotnet.cjs.extpost.js "
RUNTIME_OUTPUT_DIRECTORY "${NATIVE_BIN_DIR}")
if(CMAKE_BUILD_TYPE STREQUAL "Release")

View File

@ -1,11 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import { Int32Ptr, JSHandle, MonoArray, MonoObject, MonoString, VoidPtr } from "./types";
import { JSHandle, MonoArray, MonoObject, MonoString } from "./types";
import { Module } from "./imports";
import { mono_wasm_get_jsobj_from_js_handle } from "./gc-handles";
import { wrap_error } from "./method-calls";
import { _js_to_mono_obj } from "./js-to-cs";
import { Int32Ptr, TypedArray, VoidPtr } from "./types/emscripten";
// Creates a new typed array from pinned array address from pinned_array allocated on the heap to the typed array.
// adress of managed pinned array -> copy from heap -> typed array memory

View File

@ -3,7 +3,8 @@
import { mono_wasm_get_jsobj_from_js_handle } from "./gc-handles";
import { wrap_error } from "./method-calls";
import { Int32Ptr, JSHandle, MonoString } from "./types";
import { JSHandle, MonoString } from "./types";
import { Int32Ptr } from "./types/emscripten";
export const _are_promises_supported = ((typeof Promise === "object") || (typeof Promise === "function")) && (typeof Promise.resolve === "function");
const promise_control_symbol = Symbol.for("wasm promise_control");

View File

@ -0,0 +1,4 @@
if (typeof globalThis.Module === "object") {
createDotnetRuntime(() => { return globalThis.Module; }).then((exports) => exports);
}

View File

@ -4,13 +4,20 @@
"use strict";
const DotNetSupportLib = {
const DotnetSupportLib = {
$DOTNET: {},
$MONO: {},
$BINDING: {},
$INTERNAL: {},
// this line will be executed early on runtime, passing import and export objects into __dotnet_runtime IFFE
$DOTNET__postset: "let api = __dotnet_runtime.__initializeImportsAndExports({isGlobal:true, isNode:ENVIRONMENT_IS_NODE, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, locateFile}, {mono:MONO, binding:BINDING, internal:INTERNAL, module:Module});",
// this line will be placed early on emscripten runtime creation, passing import and export objects into __dotnet_runtime IFFE
$DOTNET__postset: `
let __dotnet_replacements = {scriptDirectory, readAsync, fetch: globalThis.fetch};
let __dotnet_exportedAPI = __dotnet_runtime.__initializeImportsAndExports(
{ isGlobal:ENVIRONMENT_IS_GLOBAL, isNode:ENVIRONMENT_IS_NODE, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, locateFile },
{ mono:MONO, binding:BINDING, internal:INTERNAL, module:Module },
__dotnet_replacements);
// here we replace things which are not exposed in another way
__dirname = scriptDirectory = __dotnet_replacements.scriptDirectory;
readAsync = __dotnet_replacements.readAsync;
var fetch = __dotnet_replacements.fetch;`,
};
// the methods would be visible to EMCC linker
@ -65,11 +72,8 @@ const linked_functions = [
// we generate simple proxy for each exported function so that emcc will include them in the final output
for (let linked_function of linked_functions) {
const fn_template = `return __dotnet_runtime.__linker_exports.${linked_function}.apply(__dotnet_runtime, arguments)`;
DotNetSupportLib[linked_function] = new Function(fn_template);
DotnetSupportLib[linked_function] = new Function(fn_template);
}
autoAddDeps(DotNetSupportLib, "$DOTNET");
autoAddDeps(DotNetSupportLib, "$MONO");
autoAddDeps(DotNetSupportLib, "$BINDING");
autoAddDeps(DotNetSupportLib, "$INTERNAL");
mergeInto(LibraryManager.library, DotNetSupportLib);
autoAddDeps(DotnetSupportLib, "$DOTNET");
mergeInto(LibraryManager.library, DotnetSupportLib);

View File

@ -0,0 +1,3 @@
createDotnetRuntime.ready = createDotnetRuntime.ready.then(() => {
return __dotnet_exportedAPI;
})

View File

@ -0,0 +1,23 @@
const MONO = {}, BINDING = {}, INTERNAL = {};
let ENVIRONMENT_IS_GLOBAL = typeof globalThis.Module === "object";
if (ENVIRONMENT_IS_GLOBAL) {
if (globalThis.Module.ready) {
throw new Error("MONO_WASM: Module.ready couldn't be redefined.")
}
globalThis.Module.ready = Module.ready;
Module = createDotnetRuntime = globalThis.Module;
}
else if (typeof createDotnetRuntime === "function") {
ENVIRONMENT_IS_GLOBAL = false;
Module = { ready: Module.ready };
const extension = createDotnetRuntime({ MONO, BINDING, INTERNAL, Module })
if (extension.ready) {
throw new Error("MONO_WASM: Module.ready couldn't be redefined.")
}
Object.assign(Module, extension);
createDotnetRuntime = Module;
}
else {
throw new Error("MONO_WASM: Can't locate global Module object or moduleFactory callback of createDotnetRuntime function.")
}
let require = (name) => { return Module.imports.require(name) };

View File

@ -3,7 +3,7 @@
import { mono_wasm_new_root, WasmRoot } from "./roots";
import {
GCHandle, Int32Ptr, JSHandleDisposed, MonoArray,
GCHandle, JSHandleDisposed, MonoArray,
MonoArrayNull, MonoObject, MonoObjectNull, MonoString,
MonoType, MonoTypeNull
} from "./types";
@ -16,6 +16,7 @@ import { mono_method_get_call_signature, call_method, wrap_error } from "./metho
import { _js_to_mono_obj } from "./js-to-cs";
import { _are_promises_supported, _create_cancelable_promise } from "./cancelable-promise";
import { getU32, getI32, getF32, getF64 } from "./memory";
import { Int32Ptr, VoidPtr } from "./types/emscripten";
// see src/mono/wasm/driver.c MARSHAL_TYPE_xxx and Runtime.cs MarshalType
export enum MarshalType {

View File

@ -2,12 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
import {
CharPtr, CharPtrPtr, Int32Ptr,
MonoArray, MonoAssembly, MonoClass,
MonoMethod, MonoObject, MonoString,
MonoType, VoidPtr
MonoType
} from "./types";
import { Module } from "./imports";
import { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr } from "./types/emscripten";
const fn_signatures: [ident: string, returnType: string | null, argTypes?: string[], opts?: any][] = [
// MONO

View File

@ -4,7 +4,7 @@
import { INTERNAL, Module, MONO, runtimeHelpers } from "./imports";
import { toBase64StringImpl } from "./base64";
import cwraps from "./cwraps";
import { VoidPtr } from "./types";
import { VoidPtr, CharPtr } from "./types/emscripten";
let commands_received: CommandResponse;
let _call_function_res_cache: any = {};

351
src/mono/wasm/runtime/dotnet.d.ts vendored Normal file
View File

@ -0,0 +1,351 @@
//! Licensed to the .NET Foundation under one or more agreements.
//! The .NET Foundation licenses this file to you under the MIT license.
//!
//! This is generated file, see src/mono/wasm/runtime/rollup.config.js
declare interface ManagedPointer {
__brandManagedPointer: "ManagedPointer";
}
declare interface NativePointer {
__brandNativePointer: "NativePointer";
}
declare interface VoidPtr extends NativePointer {
__brand: "VoidPtr";
}
declare interface CharPtr extends NativePointer {
__brand: "CharPtr";
}
declare interface Int32Ptr extends NativePointer {
__brand: "Int32Ptr";
}
declare interface EmscriptenModule {
HEAP8: Int8Array;
HEAP16: Int16Array;
HEAP32: Int32Array;
HEAPU8: Uint8Array;
HEAPU16: Uint16Array;
HEAPU32: Uint32Array;
HEAPF32: Float32Array;
HEAPF64: Float64Array;
_malloc(size: number): VoidPtr;
_free(ptr: VoidPtr): void;
print(message: string): void;
printErr(message: string): void;
ccall<T>(ident: string, returnType?: string | null, argTypes?: string[], args?: any[], opts?: any): T;
cwrap<T extends Function>(ident: string, returnType: string, argTypes?: string[], opts?: any): T;
cwrap<T extends Function>(ident: string, ...args: any[]): T;
setValue(ptr: VoidPtr, value: number, type: string, noSafe?: number | boolean): void;
setValue(ptr: Int32Ptr, value: number, type: string, noSafe?: number | boolean): void;
getValue(ptr: number, type: string, noSafe?: number | boolean): number;
UTF8ToString(ptr: CharPtr, maxBytesToRead?: number): string;
UTF8ArrayToString(u8Array: Uint8Array, idx?: number, maxBytesToRead?: number): string;
FS_createPath(parent: string, path: string, canRead?: boolean, canWrite?: boolean): string;
FS_createDataFile(parent: string, name: string, data: TypedArray, canRead: boolean, canWrite: boolean, canOwn?: boolean): string;
removeRunDependency(id: string): void;
addRunDependency(id: string): void;
ready: Promise<unknown>;
preInit?: (() => any)[];
preRun?: (() => any)[];
postRun?: (() => any)[];
onRuntimeInitialized?: () => any;
instantiateWasm: (imports: any, successCallback: Function) => any;
}
declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array;
/**
* Allocates a block of memory that can safely contain pointers into the managed heap.
* The result object has get(index) and set(index, value) methods that can be used to retrieve and store managed pointers.
* Once you are done using the root buffer, you must call its release() method.
* For small numbers of roots, it is preferable to use the mono_wasm_new_root and mono_wasm_new_roots APIs instead.
*/
declare function mono_wasm_new_root_buffer(capacity: number, name?: string): WasmRootBuffer;
/**
* Allocates temporary storage for a pointer into the managed heap.
* Pointers stored here will be visible to the GC, ensuring that the object they point to aren't moved or collected.
* If you already have a managed pointer you can pass it as an argument to initialize the temporary storage.
* The result object has get() and set(value) methods, along with a .value property.
* When you are done using the root you must call its .release() method.
*/
declare function mono_wasm_new_root<T extends ManagedPointer | NativePointer>(value?: T | undefined): WasmRoot<T>;
/**
* Releases 1 or more root or root buffer objects.
* Multiple objects may be passed on the argument list.
* 'undefined' may be passed as an argument so it is safe to call this method from finally blocks
* even if you are not sure all of your roots have been created yet.
* @param {... WasmRoot} roots
*/
declare function mono_wasm_release_roots(...args: WasmRoot<any>[]): void;
declare class WasmRootBuffer {
private __count;
private length;
private __offset;
private __offset32;
private __handle;
private __ownsAllocation;
constructor(offset: VoidPtr, capacity: number, ownsAllocation: boolean, name?: string);
_throw_index_out_of_range(): void;
_check_in_range(index: number): void;
get_address(index: number): NativePointer;
get_address_32(index: number): number;
get(index: number): ManagedPointer;
set(index: number, value: ManagedPointer): ManagedPointer;
_unsafe_get(index: number): number;
_unsafe_set(index: number, value: ManagedPointer | NativePointer): void;
clear(): void;
release(): void;
toString(): string;
}
declare class WasmRoot<T extends ManagedPointer | NativePointer> {
private __buffer;
private __index;
constructor(buffer: WasmRootBuffer, index: number);
get_address(): NativePointer;
get_address_32(): number;
get(): T;
set(value: T): T;
get value(): T;
set value(value: T);
valueOf(): T;
clear(): void;
release(): void;
toString(): string;
}
declare const enum ArgsMarshal {
Int32 = "i",
Int32Enum = "j",
Int64 = "l",
Int64Enum = "k",
Float32 = "f",
Float64 = "d",
String = "s",
Char = "s",
JSObj = "o",
MONOObj = "m"
}
declare type _ExtraArgsMarshalOperators = "!" | "";
declare type ArgsMarshalString = "" | `${ArgsMarshal}${_ExtraArgsMarshalOperators}` | `${ArgsMarshal}${ArgsMarshal}${_ExtraArgsMarshalOperators}` | `${ArgsMarshal}${ArgsMarshal}${ArgsMarshal}${_ExtraArgsMarshalOperators}` | `${ArgsMarshal}${ArgsMarshal}${ArgsMarshal}${ArgsMarshal}${_ExtraArgsMarshalOperators}`;
interface MonoObject extends ManagedPointer {
__brandMonoObject: "MonoObject";
}
interface MonoString extends MonoObject {
__brand: "MonoString";
}
interface MonoArray extends MonoObject {
__brand: "MonoArray";
}
declare type MonoConfig = {
isError: false;
assembly_root: string;
assets: AllAssetEntryTypes[];
debug_level?: number;
enable_debugging?: number;
globalization_mode: GlobalizationMode;
diagnostic_tracing?: boolean;
remote_sources?: string[];
environment_variables?: {
[i: string]: string;
};
runtime_options?: string[];
aot_profiler_options?: AOTProfilerOptions;
coverage_profiler_options?: CoverageProfilerOptions;
ignore_pdb_load_errors?: boolean;
};
declare type MonoConfigError = {
isError: true;
message: string;
error: any;
};
declare type AllAssetEntryTypes = AssetEntry | AssemblyEntry | SatelliteAssemblyEntry | VfsEntry | IcuData;
declare type AssetEntry = {
name: string;
behavior: AssetBehaviours;
virtual_path?: string;
culture?: string;
load_remote?: boolean;
is_optional?: boolean;
};
interface AssemblyEntry extends AssetEntry {
name: "assembly";
}
interface SatelliteAssemblyEntry extends AssetEntry {
name: "resource";
culture: string;
}
interface VfsEntry extends AssetEntry {
name: "vfs";
virtual_path: string;
}
interface IcuData extends AssetEntry {
name: "icu";
load_remote: boolean;
}
declare const enum AssetBehaviours {
Resource = "resource",
Assembly = "assembly",
Heap = "heap",
ICU = "icu",
VFS = "vfs"
}
declare const enum GlobalizationMode {
ICU = "icu",
INVARIANT = "invariant",
AUTO = "auto"
}
declare type AOTProfilerOptions = {
write_at?: string;
send_to?: string;
};
declare type CoverageProfilerOptions = {
write_at?: string;
send_to?: string;
};
declare type DotnetModuleConfig = {
disableDotnet6Compatibility?: boolean;
config?: MonoConfig | MonoConfigError;
configSrc?: string;
scriptDirectory?: string;
onConfigLoaded?: () => void;
onDotnetReady?: () => void;
imports?: DotnetModuleConfigImports;
} & EmscriptenModule;
declare type DotnetModuleConfigImports = {
require?: (name: string) => any;
fetch?: (url: string) => Promise<Response>;
fs?: {
promises?: {
readFile?: (path: string) => Promise<string | Buffer>;
};
readFileSync?: (path: string, options: any | undefined) => string;
};
crypto?: {
randomBytes?: (size: number) => Buffer;
};
ws?: WebSocket & {
Server: any;
};
path?: {
normalize?: (path: string) => string;
dirname?: (path: string) => string;
};
url?: any;
};
declare function mono_wasm_runtime_ready(): void;
declare function mono_wasm_setenv(name: string, value: string): void;
declare function mono_load_runtime_and_bcl_args(config: MonoConfig | MonoConfigError | undefined): Promise<void>;
declare function mono_wasm_load_data_archive(data: Uint8Array, prefix: string): boolean;
/**
* Loads the mono config file (typically called mono-config.json) asynchroniously
* Note: the run dependencies are so emsdk actually awaits it in order.
*
* @param {string} configFilePath - relative path to the config file
* @throws Will throw an error if the config file loading fails
*/
declare function mono_wasm_load_config(configFilePath: string): Promise<void>;
declare function mono_wasm_load_icu_data(offset: VoidPtr): boolean;
declare function conv_string(mono_obj: MonoString): string | null;
declare function js_string_to_mono_string(string: string): MonoString | null;
declare function js_to_mono_obj(js_obj: any): MonoObject;
declare function js_typed_array_to_array(js_obj: any): MonoArray;
declare function unbox_mono_obj(mono_obj: MonoObject): any;
declare function mono_array_to_js_array(mono_array: MonoArray): any[] | null;
declare function mono_bind_static_method(fqn: string, signature?: ArgsMarshalString): Function;
declare function mono_call_assembly_entry_point(assembly: string, args: any[], signature: ArgsMarshalString): any;
declare function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr;
declare type _MemOffset = number | VoidPtr | NativePointer;
declare function setU8(offset: _MemOffset, value: number): void;
declare function setU16(offset: _MemOffset, value: number): void;
declare function setU32(offset: _MemOffset, value: number): void;
declare function setI8(offset: _MemOffset, value: number): void;
declare function setI16(offset: _MemOffset, value: number): void;
declare function setI32(offset: _MemOffset, value: number): void;
declare function setI64(offset: _MemOffset, value: number): void;
declare function setF32(offset: _MemOffset, value: number): void;
declare function setF64(offset: _MemOffset, value: number): void;
declare function getU8(offset: _MemOffset): number;
declare function getU16(offset: _MemOffset): number;
declare function getU32(offset: _MemOffset): number;
declare function getI8(offset: _MemOffset): number;
declare function getI16(offset: _MemOffset): number;
declare function getI32(offset: _MemOffset): number;
declare function getI64(offset: _MemOffset): number;
declare function getF32(offset: _MemOffset): number;
declare function getF64(offset: _MemOffset): number;
declare const MONO: {
mono_wasm_setenv: typeof mono_wasm_setenv;
mono_wasm_load_bytes_into_heap: typeof mono_wasm_load_bytes_into_heap;
mono_wasm_load_icu_data: typeof mono_wasm_load_icu_data;
mono_wasm_runtime_ready: typeof mono_wasm_runtime_ready;
mono_wasm_load_data_archive: typeof mono_wasm_load_data_archive;
mono_wasm_load_config: typeof mono_wasm_load_config;
mono_load_runtime_and_bcl_args: typeof mono_load_runtime_and_bcl_args;
mono_wasm_new_root_buffer: typeof mono_wasm_new_root_buffer;
mono_wasm_new_root: typeof mono_wasm_new_root;
mono_wasm_release_roots: typeof mono_wasm_release_roots;
mono_wasm_add_assembly: (name: string, data: VoidPtr, size: number) => number;
mono_wasm_load_runtime: (unused: string, debug_level: number) => void;
config: MonoConfig | MonoConfigError;
loaded_files: string[];
setI8: typeof setI8;
setI16: typeof setI16;
setI32: typeof setI32;
setI64: typeof setI64;
setU8: typeof setU8;
setU16: typeof setU16;
setU32: typeof setU32;
setF32: typeof setF32;
setF64: typeof setF64;
getI8: typeof getI8;
getI16: typeof getI16;
getI32: typeof getI32;
getI64: typeof getI64;
getU8: typeof getU8;
getU16: typeof getU16;
getU32: typeof getU32;
getF32: typeof getF32;
getF64: typeof getF64;
};
declare type MONOType = typeof MONO;
declare const BINDING: {
mono_obj_array_new: (size: number) => MonoArray;
mono_obj_array_set: (array: MonoArray, idx: number, obj: MonoObject) => void;
js_string_to_mono_string: typeof js_string_to_mono_string;
js_typed_array_to_array: typeof js_typed_array_to_array;
js_to_mono_obj: typeof js_to_mono_obj;
mono_array_to_js_array: typeof mono_array_to_js_array;
conv_string: typeof conv_string;
bind_static_method: typeof mono_bind_static_method;
call_assembly_entry_point: typeof mono_call_assembly_entry_point;
unbox_mono_obj: typeof unbox_mono_obj;
};
declare type BINDINGType = typeof BINDING;
interface DotnetPublicAPI {
MONO: typeof MONO;
BINDING: typeof BINDING;
INTERNAL: any;
Module: EmscriptenModule;
RuntimeId: number;
RuntimeBuildInfo: {
ProductVersion: string;
Configuration: string;
};
}
declare function createDotnetRuntime(moduleFactory: (api: DotnetPublicAPI) => DotnetModuleConfig): Promise<DotnetPublicAPI>;
declare type CreateDotnetRuntimeType = typeof createDotnetRuntime;
declare global {
function getDotnetRuntime(runtimeId: number): DotnetPublicAPI | undefined;
}
export { BINDINGType, CreateDotnetRuntimeType, DotnetModuleConfig, DotnetPublicAPI, EmscriptenModule, MONOType, MonoArray, MonoObject, MonoString, VoidPtr, createDotnetRuntime as default };

View File

@ -0,0 +1,106 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
/* eslint-disable no-undef */
"use strict";
const DotnetSupportLib = {
$DOTNET: {},
// this line will be placed early on emscripten runtime creation, passing import and export objects into __dotnet_runtime IFFE
$DOTNET__postset: `
let __dotnet_replacements = {scriptDirectory, readAsync, fetch: globalThis.fetch};
let __dotnet_exportedAPI = __dotnet_runtime.__initializeImportsAndExports(
{ isGlobal:ENVIRONMENT_IS_GLOBAL, isNode:ENVIRONMENT_IS_NODE, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, locateFile },
{ mono:MONO, binding:BINDING, internal:INTERNAL, module:Module },
__dotnet_replacements);
// here we replace things which are not exposed in another way
__dirname = scriptDirectory = __dotnet_replacements.scriptDirectory;
readAsync = __dotnet_replacements.readAsync;
var fetch = __dotnet_replacements.fetch;
// here we replace things which are broken on NodeJS for ES6
if (ENVIRONMENT_IS_NODE) {
getBinaryPromise = async () => {
if (!wasmBinary) {
try {
if (typeof fetch === 'function' && !isFileURI(wasmBinaryFile)) {
const response = await fetch(wasmBinaryFile, { credentials: 'same-origin' });
if (!response['ok']) {
throw "failed to load wasm binary file at '" + wasmBinaryFile + "'";
}
return response['arrayBuffer']();
}
else if (readAsync) {
return await new Promise(function (resolve, reject) {
readAsync(wasmBinaryFile, function (response) { resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))) }, reject)
});
}
}
catch (err) {
return getBinary(wasmBinaryFile);
}
}
return getBinary(wasmBinaryFile);
}
}`,
};
// the methods would be visible to EMCC linker
// --- keep in sync with exports.ts ---
const linked_functions = [
// mini-wasm.c
"mono_set_timeout",
// mini-wasm-debugger.c
"mono_wasm_asm_loaded",
"mono_wasm_fire_debugger_agent_message",
"mono_wasm_debugger_log",
"mono_wasm_add_dbg_command_received",
// mono-threads-wasm.c
"schedule_background_exec",
// driver.c
"mono_wasm_invoke_js",
"mono_wasm_invoke_js_blazor",
"mono_wasm_trace_logger",
// corebindings.c
"mono_wasm_invoke_js_with_args",
"mono_wasm_get_object_property",
"mono_wasm_set_object_property",
"mono_wasm_get_by_index",
"mono_wasm_set_by_index",
"mono_wasm_get_global_object",
"mono_wasm_create_cs_owned_object",
"mono_wasm_release_cs_owned_object",
"mono_wasm_typed_array_to_array",
"mono_wasm_typed_array_copy_to",
"mono_wasm_typed_array_from",
"mono_wasm_typed_array_copy_from",
"mono_wasm_add_event_listener",
"mono_wasm_remove_event_listener",
"mono_wasm_cancel_promise",
"mono_wasm_web_socket_open",
"mono_wasm_web_socket_send",
"mono_wasm_web_socket_receive",
"mono_wasm_web_socket_close",
"mono_wasm_web_socket_abort",
"mono_wasm_compile_function",
// pal_icushim_static.c
"mono_wasm_load_icu_data",
"mono_wasm_get_icudt_name",
];
// -- this javascript file is evaluated by emcc during compilation! --
// we generate simple proxy for each exported function so that emcc will include them in the final output
for (let linked_function of linked_functions) {
const fn_template = `return __dotnet_runtime.__linker_exports.${linked_function}.apply(__dotnet_runtime, arguments)`;
DotnetSupportLib[linked_function] = new Function(fn_template);
}
autoAddDeps(DotnetSupportLib, "$DOTNET");
mergeInto(LibraryManager.library, DotnetSupportLib);

View File

@ -0,0 +1,3 @@
createDotnetRuntime.ready = createDotnetRuntime.ready.then(() => {
return __dotnet_exportedAPI;
});

View File

@ -0,0 +1,16 @@
const MONO = {}, BINDING = {}, INTERNAL = {};
let ENVIRONMENT_IS_GLOBAL = false;
if (typeof createDotnetRuntime === "function") {
Module = { ready: Module.ready };
const extension = createDotnetRuntime({ MONO, BINDING, INTERNAL, Module })
if (extension.ready) {
throw new Error("MONO_WASM: Module.ready couldn't be redefined.")
}
Object.assign(Module, extension);
createDotnetRuntime = Module;
}
else {
throw new Error("MONO_WASM: Can't use moduleFactory callback of createDotnetRuntime function.")
}
let require = (name) => { return Module.imports.require(name) };
var __dirname = '';

View File

@ -1,18 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import { DotNetPublicAPI } from "./exports";
import { EmscriptenModuleConfig } from "./types";
import { BINDINGType, DotnetPublicAPI, MONOType } from "./exports";
import { DotnetModuleConfig, MonoArray, MonoObject, MonoString } from "./types";
import { EmscriptenModule, VoidPtr } from "./types/emscripten";
// -----------------------------------------------------------
// this files has all public exports from the dotnet.js module
// -----------------------------------------------------------
declare function createDotnetRuntime(moduleFactory: (api: DotNetPublicAPI) => EmscriptenModuleConfig): Promise<DotNetPublicAPI>;
declare function createDotnetRuntime(moduleFactory: (api: DotnetPublicAPI) => DotnetModuleConfig): Promise<DotnetPublicAPI>;
declare type CreateDotnetRuntimeType = typeof createDotnetRuntime;
// Here, declare things that go in the global namespace, or augment existing declarations in the global namespace
declare global {
function getDotnetRuntime(runtimeId: number): DotNetPublicAPI | undefined;
function getDotnetRuntime(runtimeId: number): DotnetPublicAPI | undefined;
}
export default createDotnetRuntime;
export {
VoidPtr,
MonoObject, MonoString, MonoArray,
BINDINGType, MONOType, EmscriptenModule,
DotnetPublicAPI, DotnetModuleConfig, CreateDotnetRuntimeType
};

View File

@ -5,8 +5,8 @@ import ProductVersion from "consts:productVersion";
import Configuration from "consts:configuration";
import {
mono_wasm_new_root, mono_wasm_new_roots, mono_wasm_release_roots,
mono_wasm_new_root_buffer, mono_wasm_new_root_buffer_from_pointer
mono_wasm_new_root, mono_wasm_release_roots,
mono_wasm_new_root_buffer
} from "./roots";
import {
mono_wasm_send_dbg_command_with_parms,
@ -26,13 +26,14 @@ import {
mono_wasm_add_dbg_command_received,
} from "./debug";
import { runtimeHelpers, setImportsAndExports } from "./imports";
import { EmscriptenModuleMono, MonoArray, MonoConfig, MonoConfigError, MonoObject } from "./types";
import { DotnetModuleConfigImports, DotnetModule } from "./types";
import {
mono_load_runtime_and_bcl_args, mono_wasm_load_config,
mono_wasm_setenv, mono_wasm_set_runtime_options,
mono_wasm_load_data_archive, mono_wasm_asm_loaded,
mono_wasm_set_main_args,
mono_wasm_pre_init,
mono_wasm_runtime_is_initialized,
mono_wasm_on_runtime_initialized
} from "./startup";
import { mono_set_timeout, schedule_background_exec } from "./scheduling";
@ -40,8 +41,7 @@ import { mono_wasm_load_icu_data, mono_wasm_get_icudt_name } from "./icu";
import { conv_string, js_string_to_mono_string, mono_intern_string } from "./strings";
import { js_to_mono_obj, js_typed_array_to_array, mono_wasm_typed_array_to_array } from "./js-to-cs";
import {
mono_array_to_js_array, mono_wasm_create_cs_owned_object, unbox_mono_obj,
_unbox_mono_obj_root_with_known_nonprimitive_type
mono_array_to_js_array, mono_wasm_create_cs_owned_object, unbox_mono_obj
} from "./cs-to-js";
import {
call_static_method, mono_bind_static_method, mono_call_assembly_entry_point,
@ -50,9 +50,7 @@ import {
mono_wasm_get_by_index, mono_wasm_get_global_object, mono_wasm_get_object_property,
mono_wasm_invoke_js,
mono_wasm_invoke_js_blazor,
mono_wasm_invoke_js_with_args, mono_wasm_set_by_index, mono_wasm_set_object_property,
_get_args_root_buffer_for_method_call, _get_buffer_for_method_call,
_handle_exception_for_call, _teardown_after_call
mono_wasm_invoke_js_with_args, mono_wasm_set_by_index, mono_wasm_set_object_property
} from "./method-calls";
import { mono_wasm_typed_array_copy_to, mono_wasm_typed_array_from, mono_wasm_typed_array_copy_from, mono_wasm_load_bytes_into_heap } from "./buffers";
import { mono_wasm_cancel_promise } from "./cancelable-promise";
@ -68,8 +66,10 @@ import {
getU8, getU16, getU32, getF32, getF64,
} from "./memory";
import { create_weak_ref } from "./weak-ref";
import { fetch_like, readAsync_like } from "./polyfills";
import { EmscriptenModule } from "./types/emscripten";
const MONO: MONO = <any>{
const MONO = {
// current "public" MONO API
mono_wasm_setenv,
mono_wasm_load_bytes_into_heap,
@ -87,14 +87,31 @@ const MONO: MONO = <any>{
mono_wasm_load_runtime: cwraps.mono_wasm_load_runtime,
config: runtimeHelpers.config,
loaded_files: [],
loaded_files: <string[]>[],
// generated bindings closure `library_mono`
mono_wasm_new_root_buffer_from_pointer,
mono_wasm_new_roots,
// memory accessors
setI8,
setI16,
setI32,
setI64,
setU8,
setU16,
setU32,
setF32,
setF64,
getI8,
getI16,
getI32,
getI64,
getU8,
getU16,
getU32,
getF32,
getF64,
};
export type MONOType = typeof MONO;
const BINDING: BINDING = <any>{
const BINDING = {
//current "public" BINDING API
mono_obj_array_new: cwraps.mono_wasm_obj_array_new,
mono_obj_array_set: cwraps.mono_wasm_obj_array_set,
@ -106,19 +123,10 @@ const BINDING: BINDING = <any>{
bind_static_method: mono_bind_static_method,
call_assembly_entry_point: mono_call_assembly_entry_point,
unbox_mono_obj,
// generated bindings closure `binding_support`
// todo use the methods directly in the closure, not via BINDING
_get_args_root_buffer_for_method_call,
_get_buffer_for_method_call,
invoke_method: cwraps.mono_wasm_invoke_method,
_handle_exception_for_call,
mono_wasm_try_unbox_primitive_and_get_type: cwraps.mono_wasm_try_unbox_primitive_and_get_type,
_unbox_mono_obj_root_with_known_nonprimitive_type,
_teardown_after_call,
};
export type BINDINGType = typeof BINDING;
let api: DotNetPublicAPI;
let exportedAPI: DotnetPublicAPI;
// this is executed early during load of emscripten runtime
// it exports methods to global objects MONO, BINDING and Module in backward compatible way
@ -126,8 +134,9 @@ let api: DotNetPublicAPI;
function initializeImportsAndExports(
imports: { isGlobal: boolean, isNode: boolean, isShell: boolean, isWeb: boolean, locateFile: Function },
exports: { mono: any, binding: any, internal: any, module: any },
): DotNetPublicAPI {
const module = exports.module as EmscriptenModuleMono;
replacements: { scriptDirectory: any, fetch: any, readAsync: any },
): DotnetPublicAPI {
const module = exports.module as DotnetModule;
const globalThisAny = globalThis as any;
// we want to have same instance of MONO, BINDING and Module in dotnet iffe
@ -138,7 +147,7 @@ function initializeImportsAndExports(
Object.assign(exports.binding, BINDING);
Object.assign(exports.internal, INTERNAL);
api = <any>{
exportedAPI = <any>{
MONO: exports.mono,
BINDING: exports.binding,
INTERNAL: exports.internal,
@ -149,28 +158,52 @@ function initializeImportsAndExports(
}
};
if (module.configSrc) {
// this could be overriden on Module
// these could be overriden on DotnetModuleConfig
if (!module.preInit) {
module.preInit = [];
} else if (typeof module.preInit === "function") {
module.preInit = [module.preInit];
}
module.preInit.unshift(mono_wasm_pre_init);
}
// this could be overriden on Module
if (!module.onRuntimeInitialized) {
module.onRuntimeInitialized = mono_wasm_on_runtime_initialized;
if (!module.preRun) {
module.preRun = [];
} else if (typeof module.preRun === "function") {
module.preRun = [module.preRun];
}
if (!module.print) {
module.print = console.log;
}
if (!module.printErr) {
module.printErr = console.error;
}
module.imports = module.imports || <DotnetModuleConfigImports>{};
if (!module.imports.require) {
module.imports.require = globalThis.require;
}
if (!module.imports.require) {
module.imports.require = (name) => {
const resolve = (<any>module.imports)[name];
if (!resolve)
throw new Error(`Please provide Module.imports.${name}`);
return resolve;
};
}
if (imports.isGlobal || !module.disableDotNet6Compatibility) {
Object.assign(module, api);
if (module.imports.fetch) {
runtimeHelpers.fetch = module.imports.fetch;
}
else {
runtimeHelpers.fetch = fetch_like;
}
if (module.scriptDirectory) {
replacements.scriptDirectory = module.scriptDirectory;
}
replacements.fetch = runtimeHelpers.fetch;
replacements.readAsync = readAsync_like;
// here we expose objects global namespace for tests and backward compatibility
if (imports.isGlobal || !module.disableDotnet6Compatibility) {
Object.assign(module, exportedAPI);
// backward compatibility
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@ -180,7 +213,6 @@ function initializeImportsAndExports(
return mono_bind_static_method(fqn, signature);
};
// here we expose objects used in tests to global namespace
const warnWrap = (name: string, provider: () => any) => {
if (typeof globalThisAny[name] !== "undefined") {
// it already exists in the global namespace
@ -211,6 +243,35 @@ function initializeImportsAndExports(
warnWrap("addRunDependency", () => module.addRunDependency);
warnWrap("removeRunDependency", () => module.removeRunDependency);
}
// this is registration of the runtime pre_init, when user set configSrc
if (module.configSrc) {
module.preInit.push(async () => {
module.addRunDependency("mono_wasm_pre_init");
// execution order == [0] ==
await mono_wasm_pre_init();
module.removeRunDependency("mono_wasm_pre_init");
});
}
// if onRuntimeInitialized is set it's probably Blazor, we let them to do their own init sequence
if (!module.onRuntimeInitialized) {
// note this would keep running in async-parallel with emscripten's `run()` and `postRun()`
// because it's loading files asynchronously and the emscripten is not awaiting onRuntimeInitialized
// execution order == [1] ==
module.onRuntimeInitialized = () => mono_wasm_on_runtime_initialized();
module.ready = module.ready.then(async () => {
// mono_wasm_runtime_is_initialized is set when finalize_startup is done
await mono_wasm_runtime_is_initialized;
// TODO we could take over Module.postRun and call it from here if necessary
// execution order == [2] ==
return exportedAPI;
});
}
// this code makes it possible to find dotnet runtime on a page via global namespace, even when there are multiple runtimes at the same time
let list: RuntimeList;
if (!globalThisAny.getDotnetRuntime) {
globalThisAny.getDotnetRuntime = (runtimeId: string) => globalThisAny.getDotnetRuntime.__list.getRuntime(runtimeId);
@ -219,15 +280,15 @@ function initializeImportsAndExports(
else {
list = globalThisAny.getDotnetRuntime.__list;
}
list.registerRuntime(api);
list.registerRuntime(exportedAPI);
return api;
return exportedAPI;
}
export const __initializeImportsAndExports: any = initializeImportsAndExports; // don't want to export the type
// the methods would be visible to EMCC linker
// --- keep in sync with dotnet.lib.js ---
// --- keep in sync with dotnet.cjs.lib.js ---
export const __linker_exports: any = {
// mini-wasm.c
mono_set_timeout,
@ -305,68 +366,16 @@ const INTERNAL: any = {
mono_wasm_detach_debugger,
mono_wasm_raise_debug_event,
mono_wasm_runtime_is_ready: runtimeHelpers.mono_wasm_runtime_is_ready,
// memory accessors
setI8,
setI16,
setI32,
setI64,
setU8,
setU16,
setU32,
setF32,
setF64,
getI8,
getI16,
getI32,
getI64,
getU8,
getU16,
getU32,
getF32,
getF64,
};
// this represents visibility in the javascript
// like https://github.com/dotnet/aspnetcore/blob/main/src/Components/Web.JS/src/Platform/Mono/MonoTypes.ts
interface MONO {
mono_wasm_runtime_ready: typeof mono_wasm_runtime_ready
mono_wasm_setenv: typeof mono_wasm_setenv
mono_wasm_load_data_archive: typeof mono_wasm_load_data_archive;
mono_wasm_load_bytes_into_heap: typeof mono_wasm_load_bytes_into_heap;
mono_wasm_load_icu_data: typeof mono_wasm_load_icu_data;
mono_wasm_load_config: typeof mono_wasm_load_config;
mono_load_runtime_and_bcl_args: typeof mono_load_runtime_and_bcl_args;
mono_wasm_new_root_buffer: typeof mono_wasm_new_root_buffer;
mono_wasm_new_root: typeof mono_wasm_new_root;
mono_wasm_release_roots: typeof mono_wasm_release_roots;
// for Blazor's future!
mono_wasm_add_assembly: (name: string, data: VoidPtr, size: number) => number,
mono_wasm_load_runtime: (unused: string, debug_level: number) => void,
loaded_files: string[];
config: MonoConfig | MonoConfigError,
}
// this represents visibility in the javascript
// like https://github.com/dotnet/aspnetcore/blob/main/src/Components/Web.JS/src/Platform/Mono/MonoTypes.ts
interface BINDING {
mono_obj_array_new: (size: number) => MonoArray,
mono_obj_array_set: (array: MonoArray, idx: number, obj: MonoObject) => void,
js_string_to_mono_string: typeof js_string_to_mono_string,
js_typed_array_to_array: typeof js_typed_array_to_array,
js_to_mono_obj: typeof js_to_mono_obj,
mono_array_to_js_array: typeof mono_array_to_js_array,
conv_string: typeof conv_string,
bind_static_method: typeof mono_bind_static_method,
call_assembly_entry_point: typeof mono_call_assembly_entry_point,
unbox_mono_obj: typeof unbox_mono_obj
}
export interface DotNetPublicAPI {
MONO: MONO,
BINDING: BINDING,
Module: any,
export interface DotnetPublicAPI {
MONO: typeof MONO,
BINDING: typeof BINDING,
INTERNAL: any,
Module: EmscriptenModule,
RuntimeId: number,
RuntimeBuildInfo: {
ProductVersion: string,
@ -375,15 +384,15 @@ export interface DotNetPublicAPI {
}
class RuntimeList {
private list: { [runtimeId: number]: WeakRef<DotNetPublicAPI> } = {};
private list: { [runtimeId: number]: WeakRef<DotnetPublicAPI> } = {};
public registerRuntime(api: DotNetPublicAPI): number {
public registerRuntime(api: DotnetPublicAPI): number {
api.RuntimeId = Object.keys(this.list).length;
this.list[api.RuntimeId] = create_weak_ref(api);
return api.RuntimeId;
}
public getRuntime(runtimeId: number): DotNetPublicAPI | undefined {
public getRuntime(runtimeId: number): DotnetPublicAPI | undefined {
const wr = this.list[runtimeId];
return wr ? wr.deref() : undefined;
}

View File

@ -2,7 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
import cwraps from "./cwraps";
import { GlobalizationMode, VoidPtr } from "./types";
import { GlobalizationMode } from "./types";
import { VoidPtr } from "./types/emscripten";
let num_icu_assets_loaded_successfully = 0;
@ -27,7 +28,7 @@ export function mono_wasm_get_icudt_name(culture: string): string {
// @globalization_mode is one of "icu", "invariant", or "auto".
// "auto" will use "icu" if any ICU data archives have been loaded,
// otherwise "invariant".
export function mono_wasm_globalization_init(globalization_mode: GlobalizationMode): void {
export function mono_wasm_globalization_init(globalization_mode: GlobalizationMode, tracing: boolean): void {
let invariantMode = false;
if (globalization_mode === "invariant")
@ -35,9 +36,13 @@ export function mono_wasm_globalization_init(globalization_mode: GlobalizationMo
if (!invariantMode) {
if (num_icu_assets_loaded_successfully > 0) {
if (tracing) {
console.debug("MONO_WASM: ICU data archive(s) loaded, disabling invariant mode");
}
} else if (globalization_mode !== "icu") {
if (tracing) {
console.debug("MONO_WASM: ICU data archive(s) not loaded, using invariant globalization mode");
}
invariantMode = true;
} else {
const msg = "invariant globalization mode is inactive and no ICU data archives were loaded";

View File

@ -2,13 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.
/* eslint-disable @typescript-eslint/triple-slash-reference */
/// <reference path="./types/emscripten.d.ts" />
/// <reference path="./types/v8.d.ts" />
import { EmscriptenModuleMono, MonoConfig, RuntimeHelpers } from "./types";
import { DotnetModule, MonoConfig, RuntimeHelpers } from "./types";
import { EmscriptenModule } from "./types/emscripten";
// these are our public API (except internal)
export let Module: EmscriptenModule & EmscriptenModuleMono;
export let Module: EmscriptenModule & DotnetModule;
export let MONO: any;
export let BINDING: any;
export let INTERNAL: any;
@ -24,7 +24,7 @@ export let locateFile: Function;
export function setImportsAndExports(
imports: { isGlobal: boolean, isNode: boolean, isShell: boolean, isWeb: boolean, locateFile: Function },
exports: { mono: any, binding: any, internal: any, module: any },
) {
): void {
MONO = exports.mono;
BINDING = exports.binding;
INTERNAL = exports.internal;
@ -57,4 +57,5 @@ export const runtimeHelpers: RuntimeHelpers = <any>{
MONO.config = value;
Module.config = value;
},
fetch: null
};

View File

@ -14,8 +14,9 @@ import { wrap_error } from "./method-calls";
import { js_string_to_mono_string, js_string_to_mono_string_interned } from "./strings";
import { isThenable } from "./cancelable-promise";
import { has_backing_array_buffer } from "./buffers";
import { Int32Ptr, JSHandle, MonoArray, MonoMethod, MonoObject, MonoObjectNull, MonoString, wasm_type_symbol } from "./types";
import { JSHandle, MonoArray, MonoMethod, MonoObject, MonoObjectNull, MonoString, wasm_type_symbol } from "./types";
import { setI32, setU32, setF64 } from "./memory";
import { Int32Ptr, TypedArray } from "./types/emscripten";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function _js_to_mono_uri(should_add_in_flight: boolean, js_obj: any): MonoObject {

View File

@ -1,4 +1,5 @@
import { Module } from "./imports";
import { VoidPtr, NativePointer } from "./types/emscripten";
const _temp_mallocs: Array<Array<VoidPtr> | null> = [];

View File

@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
import { WasmRoot, WasmRootBuffer, mono_wasm_new_root } from "./roots";
import { MonoClass, MonoMethod, MonoObject, coerceNull, VoidPtrNull, VoidPtr, MonoType } from "./types";
import { MonoClass, MonoMethod, MonoObject, coerceNull, VoidPtrNull, MonoType } from "./types";
import { BINDING, Module, runtimeHelpers } from "./imports";
import { js_to_mono_enum, _js_to_mono_obj, _js_to_mono_uri } from "./js-to-cs";
import { js_string_to_mono_string, js_string_to_mono_string_interned } from "./strings";
@ -17,6 +17,7 @@ import {
_handle_exception_for_call, _teardown_after_call
} from "./method-calls";
import cwraps from "./cwraps";
import { VoidPtr } from "./types/emscripten";
const primitiveConverters = new Map<string, Converter>();
const _signature_converters = new Map<string, Converter>();

View File

@ -5,7 +5,7 @@ import { mono_wasm_new_root, mono_wasm_new_root_buffer, WasmRoot, WasmRootBuffer
import {
JSHandle, MonoArray, MonoMethod, MonoObject,
MonoObjectNull, MonoString, coerceNull as coerceNull,
VoidPtr, VoidPtrNull, Int32Ptr, MonoStringNull
VoidPtrNull, MonoStringNull
} from "./types";
import { BINDING, INTERNAL, Module, MONO, runtimeHelpers } from "./imports";
import { _mono_array_root_to_js_array, _unbox_mono_obj_root } from "./cs-to-js";
@ -21,6 +21,7 @@ import { conv_string, js_string_to_mono_string } from "./strings";
import cwraps from "./cwraps";
import { bindings_lazy_init } from "./startup";
import { _create_temp_frame, _release_temp_frame } from "./memory";
import { VoidPtr, Int32Ptr, EmscriptenModule } from "./types/emscripten";
function _verify_args_for_method_call(args_marshal: ArgsMarshalString, args: any) {
const has_args = args && (typeof args === "object") && args.length > 0;
@ -249,7 +250,7 @@ export function call_static_method(fqn: string, args: any[], signature: ArgsMars
return call_method(method, undefined, signature, args);
}
export function mono_bind_static_method(fqn: string, signature: ArgsMarshalString): Function {
export function mono_bind_static_method(fqn: string, signature?: ArgsMarshalString): Function {
bindings_lazy_init();// TODO remove this once Blazor does better startup
const method = mono_method_resolve(fqn);

View File

@ -0,0 +1,94 @@
# Linked javascript files
They are emcc way how to extend the dotnet.js script during linking, by appending the scripts.
See https://emscripten.org/docs/tools_reference/emcc.html#emcc-pre-js
There are `-extern-pre-js`,`-pre-js`, `-post-js`, `-extern-post-js`.
In `src\mono\wasm\build\WasmApp.Native.targets` we apply them by file naming convention as: `*.extpre.js`,`*.pre.js`, `*.post.js`, `*.extpost.js`
- For ES6 with `WasmEnableES6 == true` from `src/es6`folder
- For CommonJS with `WasmEnableES6 == false` from `src/cjs`folder
In `src\mono\wasm\runtime\CMakeLists.txt` which links only in-tree, we use same mapping explicitly. Right now CommonJS is default.
# dotnet.cjs.extpost.js
- Is at the end of file but is executed first (1)
- Applied only when linking CommonJS
- If `globalThis.Module` exist it takes it and start runtime with it.
- Otherwise user could still use the `createDotnetRuntime` export or `globalThis.createDotnetRuntime` if it was loaded into global namespace.
# dotnet.cjs.pre.js
- Executed second (2)
- Applied only when linking CommonJS
- Will try to see if it was executed with `globalThis.Module` and if so, it would use it's instance as `Module`. It would preserve emscripten's `Module.ready`
- Otherwise it would load it would assume it was called via `createDotnetRuntime` export same as described for `dotnet.es6.pre.js` below.
# dotnet.es6.pre.js
- Executed second (2)
- Applied only when linking ES6
- Will check that it was passed `moduleFactory` callback. Because of emscripten reasons it has confusing `createDotnetRuntime` name here.
- Will validate `Module.ready` is left un-overriden.
# runtime.*.iffe.js
- Executed third (3)
- this is produced from `*.ts` files in this directory by rollupJS.
# dotnet.*.post.js
- Executed last (4)
- When `onRuntimeInitialized` is overriden it would wait for emscriptens `Module.ready`
- Otherwise it would wait for for MonoVM to load all assets and assemblies.
- It would pass on the API exports
# About new API
The signature is
```
function createDotnetRuntime(moduleFactory: (api: DotnetPublicAPI) => DotnetModuleConfig): Promise<DotNetExports>
```
Simplest intended usage looks like this in ES6:
```
import createDotnetRuntime from './dotnet.js'
await createDotnetRuntime(() => ({
configSrc: "./mono-config.json",
}));
```
More complex scenario with using APIs, commented
```
import createDotnetRuntime from './dotnet.js'
export const { MONO, BINDING } = await createDotnetRuntime(({ MONO, BINDING, Module }) =>
// this is callback with no statement, the APIs are only empty shells here and are populated later.
({
disableDotnet6Compatibility: true,
configSrc: "./mono-config.json",
onConfigLoaded: () => {
// This is called during emscripten `preInit` event, after we fetched config.
// Module.config is loaded and could be tweaked before application
Module.config.environment_variables["MONO_LOG_LEVEL"]="debug"
// here we could use API passed into this callback
// call some early available functions
MONO.mono_wasm_setenv("HELLO", "WORLD);
}
onDotnetReady: () => {
// Only when there is no `onRuntimeInitialized` override.
// This is called after all assets are loaded , mapping to legacy `config.loaded_cb`.
// It happens during emscripten `onRuntimeInitialized` after monoVm init + globalization + assemblies.
// This also matches when the top level promise is resolved.
// The original emscripten `Module.ready` promise is replaced with this.
// at this point both emscripten and monoVM are fully initialized.
Module.FS.chdir(processedArguments.working_dir);
},
onAbort: (error) => {
set_exit_code(1, error);
},
}));
// at this point both emscripten and monoVM are fully initialized.
// we could use the APIs returned and resolved from createDotnetRuntime promise
// both API exports are receiving the same API object instances, i.e. same `MONO` instance.
const run_all = BINDING.bind_static_method ("[debugger-test] DebuggerTest:run_all");
run_all();
```

View File

@ -6,6 +6,8 @@
"url": "git@github.com:dotnet/runtime.git"
},
"version": "1.0.0",
"main": "dotnet.js",
"types": "dotnet.d.ts",
"scripts": {
"rollup": "rollup -c",
"lint": "eslint --no-color --max-warnings=0 ./**/*.ts ./*.js"

View File

@ -0,0 +1,50 @@
import { ENVIRONMENT_IS_NODE, Module } from "./imports";
export async function fetch_like(url: string): Promise<Response> {
try {
if (typeof (globalThis.fetch) === "function") {
return globalThis.fetch(url, { credentials: "same-origin" });
}
else if (ENVIRONMENT_IS_NODE) {
const node_fs = Module.imports!.require!("fs");
const node_url = Module.imports!.require!("url");
if (url.startsWith("file://")) {
url = node_url.fileURLToPath(url);
}
const arrayBuffer = await node_fs.promises.readFile(url);
return <Response><any>{
ok: true,
url,
arrayBuffer: () => arrayBuffer,
json: () => JSON.parse(arrayBuffer)
};
}
else if (typeof (read) === "function") {
const arrayBuffer = new Uint8Array(read(url, "binary"));
return <Response><any>{
ok: true,
url,
arrayBuffer: () => arrayBuffer,
json: () => JSON.parse(Module.UTF8ArrayToString(arrayBuffer, 0, arrayBuffer.length))
};
}
}
catch (e: any) {
return <Response><any>{
ok: false,
url,
arrayBuffer: () => { throw e; },
json: () => { throw e; }
};
}
throw new Error("No fetch implementation available");
}
export function readAsync_like(url: string, onload: Function, onerror: Function): void {
fetch_like(url).then((res: Response) => {
onload(res.arrayBuffer());
}).catch((err) => {
onerror(err);
});
}

View File

@ -3,11 +3,11 @@ import typescript from "@rollup/plugin-typescript";
import { terser } from "rollup-plugin-terser";
import { readFile, writeFile, mkdir } from "fs/promises";
import * as fs from "fs";
import * as path from "path";
import { createHash } from "crypto";
import dts from "rollup-plugin-dts";
import consts from "rollup-plugin-consts";
const outputFileName = "runtime.iffe.js";
const configuration = process.env.Configuration;
const isDebug = configuration !== "Release";
const productVersion = process.env.ProductVersion || "7.0.0-dev";
@ -40,34 +40,59 @@ const terserConfig = {
};
const plugins = isDebug ? [writeOnChangePlugin()] : [terser(terserConfig), writeOnChangePlugin()];
const banner = "//! Licensed to the .NET Foundation under one or more agreements.\n//! The .NET Foundation licenses this file to you under the MIT license.\n";
const banner_generated = banner + "//! \n//! This is generated file, see src/mono/wasm/runtime/rollup.config.js \n";
// emcc doesn't know how to load ES6 module, that's why we need the whole rollup.js
const format = "iife";
const name = "__dotnet_runtime";
export default defineConfig([
{
const iffeConfig = {
treeshake: !isDebug,
input: "exports.ts",
output: [{
file: nativeBinDir + "/src/" + outputFileName,
output: [
{
file: nativeBinDir + "/src/cjs/runtime.cjs.iffe.js",
name,
banner,
format,
plugins,
}],
plugins: [consts({ productVersion, configuration }), typescript()]
},
{
file: nativeBinDir + "/src/es6/runtime.es6.iffe.js",
name,
banner,
format,
plugins,
}
],
plugins: [consts({ productVersion, configuration }), typescript()]
};
const typesConfig = {
input: "./export-types.ts",
output: [
// dotnet.d.ts
{
format: "es",
file: nativeBinDir + "/src/" + "dotnet.d.ts",
file: nativeBinDir + "/dotnet.d.ts",
banner: banner_generated,
plugins: [writeOnChangePlugin()],
}
],
plugins: [dts()],
}
};
if (isDebug) {
// export types also into the source code and commit to git
// so that we could notice that the API changed and review it
typesConfig.output.push({
format: "es",
file: "./dotnet.d.ts",
banner: banner_generated,
plugins: [writeOnChangePlugin()],
});
}
export default defineConfig([
iffeConfig,
typesConfig
]);
// this would create .sha256 file next to the output file, so that we do not touch datetime of the file if it's same -> faster incremental build.
@ -80,7 +105,8 @@ function writeOnChangePlugin() {
async function writeWhenChanged(options, bundle) {
try {
const asset = bundle[outputFileName];
const name = Object.keys(bundle)[0];
const asset = bundle[name];
const code = asset.code;
const hashFileName = options.file + ".sha256";
const oldHashExists = await checkFileExists(hashFileName);
@ -94,13 +120,14 @@ async function writeWhenChanged(options, bundle) {
isOutputChanged = oldHash !== newHash;
}
if (isOutputChanged) {
if (!await checkFileExists(hashFileName)) {
await mkdir(nativeBinDir + "/src", { recursive: true });
const dir = path.dirname(options.file);
if (!await checkFileExists(dir)) {
await mkdir(dir, { recursive: true });
}
await writeFile(hashFileName, newHash);
} else {
// this.warn('No change in ' + options.file)
delete bundle[outputFileName];
delete bundle[name];
}
} catch (ex) {
this.warn(ex.toString());

View File

@ -3,7 +3,7 @@
import cwraps from "./cwraps";
import { Module } from "./imports";
import { VoidPtr, ManagedPointer, NativePointer } from "./types";
import { VoidPtr, ManagedPointer, NativePointer } from "./types/emscripten";
const maxScratchRoots = 8192;
let _scratch_root_buffer: WasmRootBuffer | null = null;

View File

@ -1,7 +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 { AllAssetEntryTypes, AssetEntry, CharPtr, CharPtrNull, EmscriptenModuleMono, GlobalizationMode, MonoConfig, VoidPtr, wasm_type_symbol } from "./types";
import { AllAssetEntryTypes, AssetEntry, CharPtrNull, DotnetModule, GlobalizationMode, MonoConfig, MonoConfigError, wasm_type_symbol } from "./types";
import { ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, INTERNAL, locateFile, Module, MONO, runtimeHelpers } from "./imports";
import cwraps from "./cwraps";
import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug";
@ -11,6 +11,7 @@ import { mono_wasm_init_aot_profiler, mono_wasm_init_coverage_profiler } from ".
import { mono_wasm_load_bytes_into_heap } from "./buffers";
import { bind_runtime_method, get_method, _create_primitive_converters } from "./method-binding";
import { find_corlib_class } from "./class-loader";
import { VoidPtr, CharPtr } from "./types/emscripten";
export let runtime_is_initialized_resolve: Function;
export let runtime_is_initialized_reject: Function;
@ -21,8 +22,11 @@ export const mono_wasm_runtime_is_initialized = new Promise((resolve, reject) =>
export async function mono_wasm_pre_init(): Promise<void> {
const moduleExt = Module as EmscriptenModuleMono;
if (moduleExt.configSrc) {
const moduleExt = Module as DotnetModule;
if (!moduleExt.configSrc) {
return;
}
try {
// sets MONO.config implicitly
await mono_wasm_load_config(moduleExt.configSrc);
@ -44,17 +48,18 @@ export async function mono_wasm_pre_init(): Promise<void> {
throw err;
}
}
}
}
export function mono_wasm_on_runtime_initialized(): void {
const moduleExt = Module as EmscriptenModuleMono;
if (!moduleExt.config || moduleExt.config.isError) {
export async function mono_wasm_on_runtime_initialized(): Promise<void> {
if (!Module.config || Module.config.isError) {
return;
}
mono_load_runtime_and_bcl_args(moduleExt.config);
await mono_load_runtime_and_bcl_args(Module.config);
finalize_startup(Module.config);
}
// Set environment variable NAME to VALUE
// Should be called before mono_load_runtime_and_bcl () in most cases
export function mono_wasm_setenv(name: string, value: string): void {
@ -71,44 +76,6 @@ export function mono_wasm_set_runtime_options(options: string[]): void {
cwraps.mono_wasm_parse_runtime_options(options.length, argv);
}
async function _fetch_asset(url: string): Promise<Response> {
try {
if (typeof (fetch) === "function") {
return fetch(url, { credentials: "same-origin" });
}
else if (ENVIRONMENT_IS_NODE) {
//const fs = (<any>globalThis).require("fs");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const fs = require("fs");
const arrayBuffer = await fs.promises.readFile(url);
return <Response><any>{
ok: true,
url,
arrayBuffer: () => arrayBuffer,
json: () => JSON.parse(arrayBuffer)
};
}
else if (typeof (read) === "function") {
const arrayBuffer = new Uint8Array(read(url, "binary"));
return <Response><any>{
ok: true,
url,
arrayBuffer: () => arrayBuffer,
json: () => JSON.parse(Module.UTF8ArrayToString(arrayBuffer, 0, arrayBuffer.length))
};
}
}
catch (e: any) {
return <Response><any>{
ok: false,
url,
arrayBuffer: () => { throw e; },
json: () => { throw e; }
};
}
throw new Error("No fetch implementation available");
}
function _handle_fetched_asset(ctx: MonoInitContext, asset: AssetEntry, url: string, blob: ArrayBuffer) {
const bytes = new Uint8Array(blob);
if (ctx.tracing)
@ -182,47 +149,46 @@ function _handle_fetched_asset(ctx: MonoInitContext, asset: AssetEntry, url: str
}
}
function _apply_configuration_from_args(args: MonoConfig) {
for (const k in (args.environment_variables || {}))
mono_wasm_setenv(k, args.environment_variables![k]);
function _apply_configuration_from_args(config: MonoConfig) {
for (const k in (config.environment_variables || {}))
mono_wasm_setenv(k, config.environment_variables![k]);
if (args.runtime_options)
mono_wasm_set_runtime_options(args.runtime_options);
if (config.runtime_options)
mono_wasm_set_runtime_options(config.runtime_options);
if (args.aot_profiler_options)
mono_wasm_init_aot_profiler(args.aot_profiler_options);
if (config.aot_profiler_options)
mono_wasm_init_aot_profiler(config.aot_profiler_options);
if (args.coverage_profiler_options)
mono_wasm_init_coverage_profiler(args.coverage_profiler_options);
if (config.coverage_profiler_options)
mono_wasm_init_coverage_profiler(config.coverage_profiler_options);
}
function _finalize_startup(args: MonoConfig, ctx: MonoInitContext) {
const moduleExt = Module as EmscriptenModuleMono;
ctx.loaded_files.forEach(value => MONO.loaded_files.push(value.url));
if (ctx.tracing) {
console.log("MONO_WASM: loaded_assets: " + JSON.stringify(ctx.loaded_assets));
console.log("MONO_WASM: loaded_files: " + JSON.stringify(ctx.loaded_files));
function finalize_startup(config: MonoConfig | MonoConfigError | undefined): void {
try {
if (!config || config.isError) {
return;
}
if (config.diagnostic_tracing) {
console.debug("MONO_WASM: Initializing mono runtime");
}
console.debug("MONO_WASM: Initializing mono runtime");
const moduleExt = Module as DotnetModule;
mono_wasm_globalization_init(args.globalization_mode!);
if (ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) {
try {
cwraps.mono_wasm_load_runtime("unused", args.debug_level || 0);
_apply_configuration_from_args(config);
mono_wasm_globalization_init(config.globalization_mode!, config.diagnostic_tracing!);
cwraps.mono_wasm_load_runtime("unused", config.debug_level || 0);
} catch (err: any) {
Module.printErr("MONO_WASM: mono_wasm_load_runtime () failed: " + err);
Module.printErr("MONO_WASM: Stacktrace: \n");
Module.printErr(err.stack);
runtime_is_initialized_reject(err);
if (ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) {
const wasm_exit = cwraps.mono_wasm_exit;
wasm_exit(1);
}
} else {
cwraps.mono_wasm_load_runtime("unused", args.debug_level || 0);
}
bindings_lazy_init();
@ -237,7 +203,7 @@ function _finalize_startup(args: MonoConfig, ctx: MonoInitContext) {
mono_wasm_runtime_ready();
//legacy config loading
const argsAny: any = args;
const argsAny: any = config;
if (argsAny.loaded_cb) {
try {
argsAny.loaded_cb();
@ -251,12 +217,12 @@ function _finalize_startup(args: MonoConfig, ctx: MonoInitContext) {
}
}
if (moduleExt.onDotNetReady) {
if (moduleExt.onDotnetReady) {
try {
moduleExt.onDotNetReady();
moduleExt.onDotnetReady();
}
catch (err: any) {
Module.printErr("MONO_WASM: onDotNetReady () failed: " + err);
Module.printErr("MONO_WASM: onDotnetReady () failed: " + err);
Module.printErr("MONO_WASM: Stacktrace: \n");
Module.printErr(err.stack);
runtime_is_initialized_reject(err);
@ -265,6 +231,11 @@ function _finalize_startup(args: MonoConfig, ctx: MonoInitContext) {
}
runtime_is_initialized_resolve();
} catch (err: any) {
console.error("MONO_WASM: Error in finalize_startup:", err);
runtime_is_initialized_reject(err);
throw err;
}
}
export function bindings_lazy_init(): void {
@ -330,14 +301,20 @@ export function bindings_lazy_init(): void {
}
// Initializes the runtime and loads assemblies, debug information, and other files.
export async function mono_load_runtime_and_bcl_args(args: MonoConfig): Promise<void> {
try {
if (args.enable_debugging)
args.debug_level = args.enable_debugging;
export async function mono_load_runtime_and_bcl_args(config: MonoConfig | MonoConfigError | undefined): Promise<void> {
if (!config || config.isError) {
return;
}
try {
if (config.enable_debugging)
config.debug_level = config.enable_debugging;
config.diagnostic_tracing = config.diagnostic_tracing || false;
const ctx: MonoInitContext = {
tracing: args.diagnostic_tracing || false,
pending_count: args.assets.length,
tracing: config.diagnostic_tracing,
pending_count: config.assets.length,
loaded_assets: Object.create(null),
// dlls and pdbs, used by blazor and the debugger
loaded_files: [],
@ -345,14 +322,15 @@ export async function mono_load_runtime_and_bcl_args(args: MonoConfig): Promise<
createDataFile: Module.FS_createDataFile
};
_apply_configuration_from_args(args);
// fetch_file_cb is legacy do we really want to support it ?
if (!Module.imports!.fetch && typeof ((<any>config).fetch_file_cb) === "function") {
runtimeHelpers.fetch = (<any>config).fetch_file_cb;
}
const local_fetch = typeof (args.fetch_file_cb) === "function" ? args.fetch_file_cb : _fetch_asset;
const load_asset = async (config: MonoConfig, asset: AllAssetEntryTypes): Promise<void> => {
// TODO Module.addRunDependency(asset.name);
const load_asset = async (asset: AllAssetEntryTypes): Promise<void> => {
//TODO we could do module.addRunDependency(asset.name) and delay emscripten run() after all assets are loaded
const sourcesList = asset.load_remote ? args.remote_sources! : [""];
const sourcesList = asset.load_remote ? config.remote_sources! : [""];
let error = undefined;
for (let sourcePrefix of sourcesList) {
// HACK: Special-case because MSBuild doesn't allow "" as an attribute
@ -362,10 +340,10 @@ export async function mono_load_runtime_and_bcl_args(args: MonoConfig): Promise<
let attemptUrl;
if (sourcePrefix.trim() === "") {
if (asset.behavior === "assembly")
attemptUrl = locateFile(args.assembly_root + "/" + asset.name);
attemptUrl = locateFile(config.assembly_root + "/" + asset.name);
else if (asset.behavior === "resource") {
const path = asset.culture !== "" ? `${asset.culture}/${asset.name}` : asset.name;
attemptUrl = locateFile(args.assembly_root + "/" + path);
attemptUrl = locateFile(config.assembly_root + "/" + path);
}
else
attemptUrl = asset.name;
@ -380,7 +358,7 @@ export async function mono_load_runtime_and_bcl_args(args: MonoConfig): Promise<
console.log(`MONO_WASM: Attempting to fetch '${attemptUrl}' for ${asset.name}`);
}
try {
const response = await local_fetch(attemptUrl);
const response = await runtimeHelpers.fetch(attemptUrl);
if (!response.ok) {
error = new Error(`MONO_WASM: Fetch '${attemptUrl}' for ${asset.name} failed ${response.status} ${response.statusText}`);
continue;// next source
@ -397,25 +375,29 @@ export async function mono_load_runtime_and_bcl_args(args: MonoConfig): Promise<
}
if (!error) {
//TODO Module.removeRunDependency(configFilePath);
break; // this source worked, stop searching
}
}
if (error) {
const isOkToFail = asset.is_optional || (asset.name.match(/\.pdb$/) && args.ignore_pdb_load_errors);
const isOkToFail = asset.is_optional || (asset.name.match(/\.pdb$/) && config.ignore_pdb_load_errors);
if (!isOkToFail)
throw error;
}
// TODO Module.removeRunDependency(asset.name);
};
const fetch_promises: Promise<void>[] = [];
// start fetching all assets in parallel
for (const asset of args.assets) {
fetch_promises.push(load_asset(asset));
for (const asset of config.assets) {
fetch_promises.push(load_asset(config, asset));
}
await Promise.all(fetch_promises);
_finalize_startup(args, ctx);
ctx.loaded_files.forEach(value => MONO.loaded_files.push(value.url));
if (ctx.tracing) {
console.log("MONO_WASM: loaded_assets: " + JSON.stringify(ctx.loaded_assets));
console.log("MONO_WASM: loaded_files: " + JSON.stringify(ctx.loaded_files));
}
} catch (err: any) {
console.error("MONO_WASM: Error in mono_load_runtime_and_bcl_args:", err);
runtime_is_initialized_reject(err);
@ -489,7 +471,7 @@ export async function mono_wasm_load_config(configFilePath: string): Promise<voi
module.addRunDependency(configFilePath);
try {
// NOTE: when we add nodejs make sure to include the nodejs fetch package
const configRaw = await _fetch_asset(configFilePath);
const configRaw = await runtimeHelpers.fetch(configFilePath);
const config = await configRaw.json();
runtimeHelpers.config = config;
@ -545,7 +527,7 @@ export function mono_wasm_set_main_args(name: string, allRuntimeArguments: strin
cwraps.mono_wasm_set_main_args(main_argc, main_argv);
}
type MonoInitContext = {
export type MonoInitContext = {
tracing: boolean,
pending_count: number,
loaded_files: { url: string, file: string }[],

View File

@ -2,11 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
import { mono_wasm_new_root_buffer, WasmRootBuffer } from "./roots";
import { CharPtr, MonoString, MonoStringNull, NativePointer } from "./types";
import { MonoString, MonoStringNull, } from "./types";
import { Module } from "./imports";
import cwraps from "./cwraps";
import { mono_wasm_new_root } from "./roots";
import { getI32 } from "./memory";
import { NativePointer, CharPtr } from "./types/emscripten";
export class StringDecoder {

View File

@ -2,32 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
import { bind_runtime_method } from "./method-binding";
export type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array;
export interface ManagedPointer {
__brandManagedPointer: "ManagedPointer"
}
export interface NativePointer {
__brandNativePointer: "NativePointer"
}
export interface VoidPtr extends NativePointer {
__brand: "VoidPtr"
}
export interface CharPtr extends NativePointer {
__brand: "CharPtr"
}
export interface Int32Ptr extends NativePointer {
__brand: "Int32Ptr"
}
export interface CharPtrPtr extends NativePointer {
__brand: "CharPtrPtr"
}
import { CharPtr, EmscriptenModule, ManagedPointer, NativePointer, VoidPtr } from "./types/emscripten";
export type GCHandle = {
__brand: "GCHandle"
@ -78,7 +53,6 @@ export type MonoConfig = {
assets: AllAssetEntryTypes[], // a list of assets to load along with the runtime. each asset is a dictionary-style Object with the following properties:
debug_level?: number, // Either this or the next one needs to be set
enable_debugging?: number, // Either this or the previous one needs to be set
fetch_file_cb?: Request, // a function (string) invoked to fetch a given file. If no callback is provided a default implementation appropriate for the current environment will be selected (readFileSync in node, fetch elsewhere). If no default implementation is available this call will fail.
globalization_mode: GlobalizationMode, // configures the runtime's globalization mode
diagnostic_tracing?: boolean // enables diagnostic log messages during startup
remote_sources?: string[], // additional search locations for assets. Sources will be checked in sequential order until the asset is found. The string "./" indicates to load from the application directory (as with the files in assembly_list), and a fully-qualified URL like "https://example.com/" indicates that asset loads can be attempted from a remote server. Sources must end with a "/".
@ -158,6 +132,7 @@ export type RuntimeHelpers = {
loaded_files: string[];
config: MonoConfig | MonoConfigError;
fetch: (url: string) => Promise<Response>;
}
export const wasm_type_symbol = Symbol.for("wasm type");
@ -179,19 +154,36 @@ export type CoverageProfilerOptions = {
}
// how we extended emscripten Module
export type EmscriptenModuleMono = EmscriptenModule & EmscriptenModuleConfig;
export type DotnetModule = EmscriptenModule & DotnetModuleConfig;
export type EmscriptenModuleConfig = {
disableDotNet6Compatibility?: boolean,
export type DotnetModuleConfig = {
disableDotnet6Compatibility?: boolean,
// backward compatibility
config?: MonoConfig | MonoConfigError,
configSrc?: string,
scriptDirectory?: string,
onConfigLoaded?: () => void;
onDotNetReady?: () => void;
onDotnetReady?: () => void;
/**
* @deprecated DEPRECATED! backward compatibility https://github.com/search?q=mono_bind_static_method&type=Code
*/
mono_bind_static_method: (fqn: string, signature: string) => Function,
imports?: DotnetModuleConfigImports;
} & EmscriptenModule
export type DotnetModuleConfigImports = {
require?: (name: string) => any;
fetch?: (url: string) => Promise<Response>;
fs?: {
promises?: {
readFile?: (path: string) => Promise<string | Buffer>,
}
readFileSync?: (path: string, options: any | undefined) => string,
};
crypto?: {
randomBytes?: (size: number) => Buffer
};
ws?: WebSocket & { Server: any };
path?: {
normalize?: (path: string) => string,
dirname?: (path: string) => string,
};
url?: any;
}

View File

@ -1,28 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
declare interface ManagedPointer {
export declare interface ManagedPointer {
__brandManagedPointer: "ManagedPointer"
}
declare interface NativePointer {
export declare interface NativePointer {
__brandNativePointer: "NativePointer"
}
declare interface VoidPtr extends NativePointer {
export declare interface VoidPtr extends NativePointer {
__brand: "VoidPtr"
}
declare interface CharPtr extends NativePointer {
export declare interface CharPtr extends NativePointer {
__brand: "CharPtr"
}
declare interface Int32Ptr extends NativePointer {
export declare interface Int32Ptr extends NativePointer {
__brand: "Int32Ptr"
}
declare interface CharPtrPtr extends NativePointer {
export declare interface CharPtrPtr extends NativePointer {
__brand: "CharPtrPtr"
}
declare interface EmscriptenModule {
export declare interface EmscriptenModule {
HEAP8: Int8Array,
HEAP16: Int16Array;
HEAP32: Int32Array;
@ -52,8 +52,12 @@ declare interface EmscriptenModule {
removeRunDependency(id: string): void;
addRunDependency(id: string): void;
preInit?: (() => Promise<void>)[];
onRuntimeInitialized?: () => void;
ready: Promise<unknown>;
preInit?: (() => any)[];
preRun?: (() => any)[];
postRun?: (() => any)[];
onRuntimeInitialized?: () => any;
instantiateWasm: (imports: any, successCallback: Function) => any;
}
declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array;
export declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array;

View File

@ -10,8 +10,9 @@ import { mono_wasm_get_jsobj_from_js_handle, mono_wasm_get_js_handle } from "./g
import { _wrap_js_thenable_as_task } from "./js-to-cs";
import { wrap_error } from "./method-calls";
import { conv_string } from "./strings";
import { Int32Ptr, JSHandle, MonoArray, MonoObject, MonoObjectNull, MonoString } from "./types";
import { JSHandle, MonoArray, MonoObject, MonoObjectNull, MonoString } from "./types";
import { Module } from "./imports";
import { Int32Ptr } from "./types/emscripten";
const wasm_ws_pending_send_buffer = Symbol.for("wasm ws_pending_send_buffer");
const wasm_ws_pending_send_buffer_offset = Symbol.for("wasm ws_pending_send_buffer_offset");

View File

@ -67,33 +67,34 @@ for (let m of methods) {
}
}
function proxyJson(func) {
for (let m of ["log", ...methods])
console[m] = proxyMethod(`console.${m}`, func, true);
}
let consoleWebSocket;
if (is_browser) {
const consoleUrl = `${window.location.origin}/console`.replace('http://', 'ws://');
consoleWebSocket = new WebSocket(consoleUrl);
// redirect output so that when emscripten starts it's already redirected
proxyJson(function (msg) {
if (consoleWebSocket.readyState === WebSocket.OPEN) {
consoleWebSocket.send(msg);
}
else {
originalConsole.log(msg);
}
});
consoleWebSocket.onopen = function (event) {
originalConsole.log("browser: Console websocket connected.");
};
consoleWebSocket.onerror = function (event) {
originalConsole.error(`websocket error: ${event}`);
};
consoleWebSocket.onclose = function (event) {
originalConsole.error(`websocket closed: ${event}`);
};
const send = (msg) => {
if (consoleWebSocket.readyState === WebSocket.OPEN) {
consoleWebSocket.send(msg);
}
else {
originalConsole.log(msg);
}
}
// redirect output early, so that when emscripten starts it's already redirected
for (let m of ["log", ...methods])
console[m] = proxyMethod(`console.${m}`, send, true);
}
if (typeof globalThis.crypto === 'undefined') {
@ -121,7 +122,9 @@ if (typeof globalThis.performance === 'undefined') {
}
}
}
var Module = {
loadDotnet("./dotnet.js").then((createDotnetRuntime) => {
return createDotnetRuntime(({ MONO, INTERNAL, BINDING, Module }) => ({
disableDotnet6Compatibility: true,
config: null,
configSrc: "./mono-config.json",
onConfigLoaded: () => {
@ -139,7 +142,7 @@ var Module = {
INTERNAL.mono_wasm_enable_on_demand_gc(0);
}
},
onDotNetReady: () => {
onDotnetReady: () => {
let wds = Module.FS.stat(processedArguments.working_dir);
if (wds === undefined || !Module.FS.isDir(wds.mode)) {
set_exit_code(1, `Could not find working directory ${processedArguments.working_dir}`);
@ -148,7 +151,7 @@ var Module = {
Module.FS.chdir(processedArguments.working_dir);
App.init();
App.init({ MONO, INTERNAL, BINDING, Module });
},
onAbort: (error) => {
console.log("ABORT: " + error);
@ -157,12 +160,18 @@ var Module = {
console.error(err.stack);
set_exit_code(1, error);
},
};
}))
}).catch(function (err) {
console.error(err);
set_exit_code(1, "failed to load the dotnet.js file");
throw err;
});
const App = {
init: async function () {
init: async function ({ MONO, INTERNAL, BINDING, Module }) {
console.info("Initializing.....");
Object.assign(App, { MONO, INTERNAL, BINDING, Module });
for (let i = 0; i < processedArguments.profilers.length; ++i) {
const init = Module.cwrap('mono_wasm_load_profiler_' + processedArguments.profilers[i], 'void', ['string']);
init("");
@ -205,7 +214,7 @@ const App = {
const main_assembly_name = processedArguments.applicationArgs[1];
const app_args = processedArguments.applicationArgs.slice(2);
INTERNAL.mono_wasm_set_main_args(processedArguments.applicationArgs[1], app_args);
INTERNAL.mono_wasm_set_main_args(main_assembly_name, app_args);
// Automatic signature isn't working correctly
const result = await BINDING.call_assembly_entry_point(main_assembly_name, [app_args], "m");
@ -228,7 +237,7 @@ const App = {
const fqn = "[System.Private.Runtime.InteropServices.JavaScript.Tests]System.Runtime.InteropServices.JavaScript.Tests.HelperMarshal:" + method_name;
try {
return INTERNAL.call_static_method(fqn, args || [], signature);
return App.INTERNAL.call_static_method(fqn, args || [], signature);
} catch (exc) {
console.error("exception thrown in", fqn);
throw exc;
@ -244,12 +253,12 @@ function set_exit_code(exit_code, reason) {
console.error(reason.stack);
}
}
if (is_browser) {
const stack = (new Error()).stack.replace(/\n/g, "").replace(/[ ]*at/g, " at").replace(/https?:\/\/[0-9.:]*/g, "").replace("Error", "");
const messsage = `Exit called with ${exit_code} when isXUnitDoneCheck=${isXUnitDoneCheck} isXmlDoneCheck=${isXmlDoneCheck} WS.bufferedAmount=${consoleWebSocket.bufferedAmount} ${stack}.`;
if (is_browser) {
if (App.Module) {
// Notify the selenium script
Module.exit_code = exit_code;
App.Module.exit_code = exit_code;
}
//Tell xharness WasmBrowserTestRunner what was the exit code
const tests_done_elem = document.createElement("label");
@ -257,8 +266,6 @@ function set_exit_code(exit_code, reason) {
tests_done_elem.innerHTML = exit_code.toString();
document.body.appendChild(tests_done_elem);
console.log('WS: ' + messsage);
originalConsole.log('CDP: ' + messsage);
const stop_when_ws_buffer_empty = () => {
if (consoleWebSocket.bufferedAmount == 0) {
// tell xharness WasmTestMessagesProcessor we are done.
@ -271,8 +278,8 @@ function set_exit_code(exit_code, reason) {
};
stop_when_ws_buffer_empty();
} else if (INTERNAL) {
INTERNAL.mono_wasm_exit(exit_code);
} else if (App && App.INTERNAL) {
App.INTERNAL.mono_wasm_exit(exit_code);
}
}
@ -354,7 +361,7 @@ async function loadDotnet(file) {
if (typeof WScript !== "undefined") { // Chakra
loadScript = function (file) {
WScript.LoadScriptFile(file);
return globalThis.Module;
return globalThis.createDotnetRuntime;
};
} else if (is_node) { // NodeJS
loadScript = async function (file) {
@ -368,8 +375,8 @@ async function loadDotnet(file) {
let timeout = 100;
// bysy spin waiting for script to load into global namespace
while (timeout > 0) {
if (globalThis.Module) {
return globalThis.Module;
if (globalThis.createDotnetRuntime) {
return globalThis.createDotnetRuntime;
}
// delay 10ms
await new Promise(resolve => setTimeout(resolve, 10));
@ -381,7 +388,7 @@ async function loadDotnet(file) {
else if (typeof globalThis.load !== 'undefined') {
loadScript = async function (file) {
globalThis.load(file)
return globalThis.Module;
return globalThis.createDotnetRuntime;
}
}
else {
@ -390,9 +397,3 @@ async function loadDotnet(file) {
return loadScript(file);
}
loadDotnet("./dotnet.js").catch(function (err) {
console.error(err);
set_exit_code(1, "failed to load the dotnet.js file");
throw err;
});

View File

@ -66,13 +66,14 @@
<_EmccCommonFlags Include="-s ALLOW_MEMORY_GROWTH=1" />
<_EmccCommonFlags Include="-s NO_EXIT_RUNTIME=1" />
<_EmccCommonFlags Include="-s FORCE_FILESYSTEM=1" />
<_EmccCommonFlags Include="-s EXPORTED_RUNTIME_METHODS=&quot;['DOTNET','MONO','BINDING','INTERNAL','FS','print','ccall','cwrap','setValue','getValue','UTF8ToString','UTF8ArrayToString','FS_createPath','FS_createDataFile','removeRunDependency','addRunDependency']&quot;" />
<_EmccCommonFlags Include="-s EXPORTED_RUNTIME_METHODS=&quot;['FS','print','ccall','cwrap','setValue','getValue','UTF8ToString','UTF8ArrayToString','FS_createPath','FS_createDataFile','removeRunDependency','addRunDependency']&quot;" />
<_EmccCommonFlags Include="-s EXPORTED_FUNCTIONS=&quot;['_free','_malloc']&quot;" />
<_EmccCommonFlags Include="--source-map-base http://example.com" />
<_EmccCommonFlags Include="-s STRICT_JS=1" />
<_EmccCommonFlags Include="-s MODULARIZE=1" Condition="'$(WasmEnableES6)' != 'false'" />
<_EmccCommonFlags Include="-s EXPORT_ES6=1" Condition="'$(WasmEnableES6)' != 'false'" />
<_EmccCommonFlags Include="-s EXPORT_NAME=&quot;'createDotnetRuntime'&quot;" />
<_EmccCommonFlags Include="-s MODULARIZE=1"/>
<_EmccCommonFlags Include="-s EXPORT_ES6=1" Condition="'$(WasmEnableES6)' == 'true'" />
</ItemGroup>
<ItemGroup Condition="'$(OS)' != 'Windows_NT'">
@ -152,7 +153,7 @@
<CMakeConfigurationLinkFlags Condition="'$(Configuration)' == 'Debug'">$(CMakeConfigurationEmccFlags)</CMakeConfigurationLinkFlags>
<CMakeConfigurationLinkFlags Condition="'$(Configuration)' == 'Release'">-O2</CMakeConfigurationLinkFlags>
<CMakeConfigurationEmsdkPath Condition="'$(Configuration)' == 'Release'"> -DEMSDK_PATH=&quot;$(EMSDK_PATH.TrimEnd('\/'))&quot;</CMakeConfigurationEmsdkPath>
<CMakeBuildRuntimeConfigureCmd>emcmake cmake $(MSBuildThisFileDirectory)runtime -DCMAKE_BUILD_TYPE=$(Configuration) -DCONFIGURATION_EMCC_FLAGS=&quot;$(CMakeConfigurationEmccFlags)&quot; -DCONFIGURATION_LINK_FLAGS=&quot;$(CMakeConfigurationLinkFlags)&quot; -DMONO_INCLUDES=&quot;$(MonoArtifactsPath)include/mono-2.0&quot; -DMONO_OBJ_INCLUDES=&quot;$(MonoObjDir.TrimEnd('\/'))&quot; -DICU_LIB_DIR=&quot;$(ICULibDir.TrimEnd('\/'))&quot; -DMONO_ARTIFACTS_DIR=&quot;$(MonoArtifactsPath.TrimEnd('\/'))&quot; -DNATIVE_BIN_DIR=&quot;$(NativeBinDir.TrimEnd('\/'))&quot; -DSYSTEM_NATIVE_DIR=&quot;$(RepoRoot)src/native/libs/System.Native&quot; -DSOURCE_DIR=&quot;$(MSBuildThisFileDirectory.TrimEnd('\/'))/runtime&quot;$(CMakeConfigurationEmsdkPath)</CMakeBuildRuntimeConfigureCmd>
<CMakeBuildRuntimeConfigureCmd>emcmake cmake $(MSBuildThisFileDirectory)runtime -DCMAKE_BUILD_TYPE=$(Configuration) -DCONFIGURATION_EMCC_FLAGS=&quot;$(CMakeConfigurationEmccFlags)&quot; -DCONFIGURATION_LINK_FLAGS=&quot;$(CMakeConfigurationLinkFlags)&quot; -DMONO_INCLUDES=&quot;$(MonoArtifactsPath)include/mono-2.0&quot; -DMONO_OBJ_INCLUDES=&quot;$(MonoObjDir.TrimEnd('\/'))&quot; -DICU_LIB_DIR=&quot;$(ICULibDir.TrimEnd('\/'))&quot; -DMONO_ARTIFACTS_DIR=&quot;$(MonoArtifactsPath.TrimEnd('\/'))&quot; -DNATIVE_BIN_DIR=&quot;$(NativeBinDir.TrimEnd('\/'))&quot; $(CMakeConfigurationEmsdkPath)</CMakeBuildRuntimeConfigureCmd>
<CMakeBuildRuntimeConfigureCmd Condition="'$(OS)' == 'Windows_NT'">call &quot;$(RepositoryEngineeringDir)native\init-vs-env.cmd&quot; &amp;&amp; call &quot;$([MSBuild]::NormalizePath('$(EMSDK_PATH)', 'emsdk_env.bat'))&quot; &amp;&amp; $(CMakeBuildRuntimeConfigureCmd)</CMakeBuildRuntimeConfigureCmd>
<CMakeBuildRuntimeConfigureCmd Condition="'$(OS)' != 'Windows_NT'">bash -c 'source $(EMSDK_PATH)/emsdk_env.sh 2>&amp;1 &amp;&amp; $(CMakeBuildRuntimeConfigureCmd)'</CMakeBuildRuntimeConfigureCmd>
@ -180,16 +181,30 @@
<Copy SourceFiles="runtime/driver.c;
runtime/pinvoke.c;
runtime/corebindings.c;
runtime/library-dotnet.js;
$(SharedNativeRoot)libs\System.Native\pal_random.js;"
$(SharedNativeRoot)libs\System.Native\pal_random.lib.js;"
DestinationFolder="$(NativeBinDir)src"
SkipUnchangedFiles="true" />
<Copy SourceFiles="runtime/cjs/dotnet.cjs.pre.js;
runtime/cjs/dotnet.cjs.lib.js;
runtime/cjs/dotnet.cjs.post.js;
runtime/cjs/dotnet.cjs.extpost.js;"
DestinationFolder="$(NativeBinDir)src/cjs"
SkipUnchangedFiles="true" />
<Copy SourceFiles="runtime/es6/dotnet.es6.pre.js;
runtime/es6/dotnet.es6.lib.js;
runtime/es6/dotnet.es6.post.js;"
DestinationFolder="$(NativeBinDir)src/es6"
SkipUnchangedFiles="true" />
<Copy SourceFiles="runtime\pinvoke.h"
DestinationFolder="$(NativeBinDir)include\wasm"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(ICULibFiles);@(ICULibNativeFiles)"
<Copy SourceFiles="@(ICULibFiles);
@(ICULibNativeFiles);
runtime/package.json;"
DestinationFolder="$(NativeBinDir)"
SkipUnchangedFiles="true" />
@ -202,11 +217,14 @@
$(NativeBinDir)src\*.js;
$(_EmccDefaultsRspPath);
$(NativeBinDir)src\emcc-props.json" />
<WasmSrcFilesCjs Include="$(NativeBinDir)src\cjs\*.js;" />
<WasmSrcFilesEs6 Include="$(NativeBinDir)src\es6\*.js;" />
<WasmHeaderFiles Include="$(NativeBinDir)include\wasm\*.h" />
</ItemGroup>
<Copy SourceFiles="$(NativeBinDir)dotnet.js;
$(NativeBinDir)src\dotnet.d.ts;
$(NativeBinDir)dotnet.d.ts;
$(NativeBinDir)package.json;
$(NativeBinDir)dotnet.wasm;
$(NativeBinDir)dotnet.timezones.blat"
DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)"
@ -220,6 +238,14 @@
DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)src"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(WasmSrcFilesCjs)"
DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)src\cjs"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(WasmSrcFilesEs6)"
DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)src\es6"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(WasmHeaderFiles)"
DestinationFolder="$(MicrosoftNetCoreAppRuntimePackNativeDir)include\wasm"
SkipUnchangedFiles="true" />

View File

@ -8,7 +8,7 @@ var Module = {
onConfigLoaded: () => {
MONO.config.environment_variables["DOTNET_MODIFIABLE_ASSEMBLIES"] = "debug";
},
onDotNetReady: () => {
onDotnetReady: () => {
try {
App.init();
} catch (error) {

View File

@ -5,7 +5,7 @@
var Module = {
configSrc: "./mono-config.json",
onDotNetReady: () => {
onDotnetReady: () => {
try {
App.init();
} catch (error) {