diff --git a/Blazor.sln b/Blazor.sln
index f1caf90170..9e90f27bfe 100644
--- a/Blazor.sln
+++ b/Blazor.sln
@@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Blazor.Mono.Test"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Blazor.E2ETest", "test\Microsoft.Blazor.E2ETest\Microsoft.Blazor.E2ETest.csproj", "{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoSanityClient", "samples\MonoSanityClient\MonoSanityClient.csproj", "{06AAAE9E-96DE-4574-97DA-9C4C7D9FE990}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -75,6 +77,10 @@ Global
{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {06AAAE9E-96DE-4574-97DA-9C4C7D9FE990}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {06AAAE9E-96DE-4574-97DA-9C4C7D9FE990}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {06AAAE9E-96DE-4574-97DA-9C4C7D9FE990}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {06AAAE9E-96DE-4574-97DA-9C4C7D9FE990}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -87,6 +93,7 @@ Global
{5A694793-3257-4D37-BB74-4A41B3894685} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
{118484D3-3993-45CE-97C1-6F28A517529B} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}
{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}
+ {06AAAE9E-96DE-4574-97DA-9C4C7D9FE990} = {F5FDD4E5-6A52-4A86-BE5E-5E42CB1DC8DA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {504DA352-6788-4DC0-8705-82167E72A4D3}
diff --git a/runtime/Microsoft.Blazor.Server/BlazorAppBuilderExtensions.cs b/runtime/Microsoft.Blazor.Server/BlazorAppBuilderExtensions.cs
index 9c23044bcf..2dff0e53df 100644
--- a/runtime/Microsoft.Blazor.Server/BlazorAppBuilderExtensions.cs
+++ b/runtime/Microsoft.Blazor.Server/BlazorAppBuilderExtensions.cs
@@ -26,7 +26,8 @@ namespace Microsoft.AspNetCore.Builder
{
{ ".dll", MediaTypeNames.Application.Octet },
{ ".js", "application/javascript" },
- { ".wasm", MediaTypeNames.Application.Octet }
+ { ".mem", MediaTypeNames.Application.Octet },
+ { ".wasm", MediaTypeNames.Application.Octet },
});
}
}
diff --git a/samples/MonoSanity/MonoSanity.csproj b/samples/MonoSanity/MonoSanity.csproj
index c800d91356..6cf5b5d1c1 100644
--- a/samples/MonoSanity/MonoSanity.csproj
+++ b/samples/MonoSanity/MonoSanity.csproj
@@ -10,6 +10,7 @@
Simple sanity check to ensure the Mono runtime works in basic cases.
+ + + + + + + +Loading...
+ + + diff --git a/samples/MonoSanity/wwwroot/loader.js b/samples/MonoSanity/wwwroot/loader.js new file mode 100644 index 0000000000..d391c30bbb --- /dev/null +++ b/samples/MonoSanity/wwwroot/loader.js @@ -0,0 +1,164 @@ +(function () { + window.initMono = function initMono(loadAssemblyUrls, onReadyCallback) { + window.Browser = { + init: function () { }, + asyncLoad: asyncLoad + }; + + window.Module = { + print: function (line) { console.log(line); }, + printEr: function (line) { console.error(line); }, + wasmBinaryFile: '/_framework/wasm/mono.wasm', + asmjsCodeFile: '/_framework/asmjs/mono.asm.js', + preloadPlugins: [], + preRun: [function () { + preloadAssemblies(loadAssemblyUrls); + }], + postRun: [function () { + var load_runtime = Module.cwrap('mono_wasm_load_runtime', null, ['string']); + load_runtime('appBinDir'); + onReadyCallback(); + }] + }; + + addScriptTagsToDocument(); + }; + + window.invokeMonoMethod = function invokeMonoMethod(assemblyName, namespace, typeName, methodName, args) { + var assembly_load = Module.cwrap('mono_wasm_assembly_load', 'number', ['string']); + var find_class = Module.cwrap('mono_wasm_assembly_find_class', 'number', ['number', 'string', 'string']); + var find_method = Module.cwrap('mono_wasm_assembly_find_method', 'number', ['number', 'string', 'number']); + + var assembly = assembly_load(assemblyName); + var type = find_class(assembly, namespace, typeName); + var method = find_method(type, methodName, -1); + + var stack = Module.Runtime.stackSave(); + try { + var resultPtr = callMethod(method, null, args); + return dotnetStringToJavaScriptString(resultPtr); + } + finally { + Module.Runtime.stackRestore(stack); + } + }; + + function preloadAssemblies(loadAssemblyUrls) { + var loadBclAssemblies = [ + 'mscorlib', + 'System', + 'System.Core', + 'Facades/netstandard', + 'Facades/System.Console', + 'Facades/System.Collections', + 'Facades/System.Diagnostics.Debug', + 'Facades/System.IO', + 'Facades/System.Linq', + 'Facades/System.Reflection', + 'Facades/System.Reflection.Extensions', + 'Facades/System.Runtime', + 'Facades/System.Runtime.Extensions', + 'Facades/System.Runtime.InteropServices', + 'Facades/System.Threading', + 'Facades/System.Threading.Tasks' + ]; + + var allAssemblyUrls = loadAssemblyUrls + .concat(loadBclAssemblies.map(function (name) { return '_framework/bcl/' + name + '.dll'; })); + + Module.FS_createPath('/', 'appBinDir', true, true); + allAssemblyUrls.forEach(function (url) { + FS.createPreloadedFile('appBinDir', getAssemblyNameFromUrl(url) + '.dll', url, true, false, null, function onError(err) { + throw err; + }); + }); + } + + function asyncLoad(url, onload, onerror) { + var xhr = new XMLHttpRequest; + xhr.open('GET', url, /* async: */ true); + xhr.responseType = 'arraybuffer'; + xhr.onload = function xhr_onload() { + if (xhr.status === 200 || xhr.status === 0 && xhr.response) { + var asm = new Uint8Array(xhr.response); + onload(asm); + } else { + onerror(xhr); + } + }; + xhr.onerror = onerror; + xhr.send(null); + } + + function dotnetStringToJavaScriptString(mono_obj) { + if (mono_obj === 0) + return null; + var mono_string_get_utf8 = Module.cwrap('mono_wasm_string_get_utf8', 'number', ['number']); + var raw = mono_string_get_utf8(mono_obj); + var res = Module.UTF8ToString(raw); + Module._free(raw); + return res; + } + + function callMethod(method, target, args) { + var stack = Module.Runtime.stackSave(); + var invoke_method = Module.cwrap('mono_wasm_invoke_method', 'number', ['number', 'number', 'number']); + var mono_string = Module.cwrap('mono_wasm_string_from_js', 'number', ['string']); + + try { + var argsBuffer = Module.Runtime.stackAlloc(args.length); + var exceptionFlagManagedInt = Module.Runtime.stackAlloc(4); + for (var i = 0; i < args.length; ++i) { + var argVal = args[i]; + if (typeof argVal === 'number') { + var managedInt = Module.Runtime.stackAlloc(4); + Module.setValue(managedInt, argVal, 'i32'); + Module.setValue(argsBuffer + i * 4, managedInt, 'i32'); + } else if (typeof argVal === 'string') { + var managedString = mono_string(argVal); + Module.setValue(argsBuffer + i * 4, managedString, 'i32'); + } else { + throw new Error('Unsupported arg type: ' + typeof argVal); + } + } + Module.setValue(exceptionFlagManagedInt, 0, 'i32'); + + var res = invoke_method(method, target, argsBuffer, exceptionFlagManagedInt); + + if (Module.getValue(exceptionFlagManagedInt, 'i32') !== 0) { + throw new Error(dotnetStringToJavaScriptString(res)); + } + + return res; + } finally { + Module.Runtime.stackRestore(stack); + } + } + + function addScriptTagsToDocument() { + // Load either the wasm or asm.js version of the Mono runtime + var browserSupportsNativeWebAssembly = typeof WebAssembly !== 'undefined' && WebAssembly.validate; + var monoRuntimeUrlBase = '/_framework/' + (browserSupportsNativeWebAssembly ? 'wasm' : 'asmjs'); + var monoRuntimeScriptUrl = monoRuntimeUrlBase + '/mono.js'; + + if (!browserSupportsNativeWebAssembly) { + // In the asmjs case, the initial memory structure is in a separate file we need to download + var meminitXHR = Module['memoryInitializerRequest'] = new XMLHttpRequest(); + meminitXHR.open('GET', monoRuntimeUrlBase + '/mono.js.mem'); + meminitXHR.responseType = 'arraybuffer'; + meminitXHR.send(null); + } + + var scriptElem = document.createElement('script'); + scriptElem.src = monoRuntimeScriptUrl; + document.body.appendChild(scriptElem); + } + + function getAssemblyNameFromUrl(url) { + var lastSegment = url.substring(url.lastIndexOf('/') + 1); + var queryStringStartPos = lastSegment.indexOf('?'); + var filename = queryStringStartPos < 0 ? lastSegment : lastSegment.substring(0, queryStringStartPos); + return filename.replace(/\.dll$/, ''); + } + +})(); \ No newline at end of file diff --git a/samples/MonoSanityClient/Examples.cs b/samples/MonoSanityClient/Examples.cs new file mode 100644 index 0000000000..24be61d54e --- /dev/null +++ b/samples/MonoSanityClient/Examples.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text; + +namespace MonoSanityClient +{ + public static class Examples + { + public static string AddNumbers(int a, int b) + => (a + b).ToString(); + + public static string RepeatString(string str, int count) + { + var result = new StringBuilder(); + + for (var i = 0; i < count; i++) + { + result.Append(str); + } + + return result.ToString(); + } + + public static void TriggerException(string message) + { + throw new InvalidOperationException(message); + } + } +} diff --git a/samples/MonoSanityClient/MonoSanityClient.csproj b/samples/MonoSanityClient/MonoSanityClient.csproj new file mode 100644 index 0000000000..5766db614c --- /dev/null +++ b/samples/MonoSanityClient/MonoSanityClient.csproj @@ -0,0 +1,7 @@ +