Make MonoSanity sample actually run some .NET code via Mono
This commit is contained in:
parent
c841016783
commit
32dae87b00
|
|
@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Blazor.Mono.Test"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Blazor.E2ETest", "test\Microsoft.Blazor.E2ETest\Microsoft.Blazor.E2ETest.csproj", "{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Blazor.E2ETest", "test\Microsoft.Blazor.E2ETest\Microsoft.Blazor.E2ETest.csproj", "{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoSanityClient", "samples\MonoSanityClient\MonoSanityClient.csproj", "{06AAAE9E-96DE-4574-97DA-9C4C7D9FE990}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -87,6 +93,7 @@ Global
|
||||||
{5A694793-3257-4D37-BB74-4A41B3894685} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
|
{5A694793-3257-4D37-BB74-4A41B3894685} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
|
||||||
{118484D3-3993-45CE-97C1-6F28A517529B} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}
|
{118484D3-3993-45CE-97C1-6F28A517529B} = {ADA3AE29-F6DE-49F6-8C7C-B321508CAE8E}
|
||||||
{5BC2A10D-B6CA-43AE-B73C-2A41AE1039F9} = {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
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {504DA352-6788-4DC0-8705-82167E72A4D3}
|
SolutionGuid = {504DA352-6788-4DC0-8705-82167E72A4D3}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,8 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
{
|
{
|
||||||
{ ".dll", MediaTypeNames.Application.Octet },
|
{ ".dll", MediaTypeNames.Application.Octet },
|
||||||
{ ".js", "application/javascript" },
|
{ ".js", "application/javascript" },
|
||||||
{ ".wasm", MediaTypeNames.Application.Octet }
|
{ ".mem", MediaTypeNames.Application.Octet },
|
||||||
|
{ ".wasm", MediaTypeNames.Application.Octet },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\runtime\Microsoft.Blazor.Server\Microsoft.Blazor.Server.csproj" />
|
<ProjectReference Include="..\..\runtime\Microsoft.Blazor.Server\Microsoft.Blazor.Server.csproj" />
|
||||||
|
<ProjectReference Include="..\MonoSanityClient\MonoSanityClient.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using MonoSanityClient;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Mime;
|
||||||
|
|
||||||
namespace MonoSanity
|
namespace MonoSanity
|
||||||
{
|
{
|
||||||
|
|
@ -13,6 +16,28 @@ namespace MonoSanity
|
||||||
app.UseDeveloperExceptionPage();
|
app.UseDeveloperExceptionPage();
|
||||||
app.UseFileServer();
|
app.UseFileServer();
|
||||||
app.UseBlazor();
|
app.UseBlazor();
|
||||||
|
|
||||||
|
ServeSingleStaticFile(app,
|
||||||
|
"/clientBin/MonoSanityClient.dll",
|
||||||
|
typeof(Examples).Assembly.Location);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ServeSingleStaticFile(IApplicationBuilder app, string url, string physicalPath)
|
||||||
|
{
|
||||||
|
// This is not implemented efficiently (e.g., doesn't support cache control headers)
|
||||||
|
// so don't use this in real applications. Use 'UseStaticFiles' or similar instead.
|
||||||
|
app.Use((context, next) =>
|
||||||
|
{
|
||||||
|
if (context.Request.Path == url)
|
||||||
|
{
|
||||||
|
context.Response.ContentType = MediaTypeNames.Application.Octet;
|
||||||
|
return File.OpenRead(physicalPath).CopyToAsync(context.Response.Body);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,83 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Mono sanity check</title>
|
<title>Mono sanity check</title>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Hello, world!</h1>
|
<p>Simple sanity check to ensure the Mono runtime works in basic cases.</p>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Add numbers</legend>
|
||||||
|
<form id="addNumbers">
|
||||||
|
<input id="addNumberA" value="123" /> +
|
||||||
|
<input id="addNumberB" value="456" /> =
|
||||||
|
<input id="addNumberResult" readonly />
|
||||||
|
<button type="submit" disabled>Go</button>
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Repeat string</legend>
|
||||||
|
<form id="repeatString">
|
||||||
|
<input id="repeatStringStr" value="Hello" /> *
|
||||||
|
<input id="repeatStringCount" value="3" type="number" /> =
|
||||||
|
<input id="repeatStringResult" readonly />
|
||||||
|
<button type="submit" disabled>Go</button>
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Trigger exception</legend>
|
||||||
|
<form id="triggerException">
|
||||||
|
<input id="triggerExceptionMessage" value="Your message here" />
|
||||||
|
<button type="submit" disabled>Go</button>
|
||||||
|
<div><textarea rows="5" cols="80" readonly id="triggerExceptionMessageStackTrace"></textarea></div>
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<p id="loadingIndicator">Loading...</p>
|
||||||
|
|
||||||
|
<script src="loader.js"></script>
|
||||||
|
<script>
|
||||||
|
initMono(['/clientBin/MonoSanityClient.dll'], function () {
|
||||||
|
var buttons = document.getElementsByTagName('button');
|
||||||
|
for (var i = 0; i < buttons.length; i++) {
|
||||||
|
buttons[i].disabled = false;
|
||||||
|
}
|
||||||
|
el('loadingIndicator').style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
el('addNumbers').onsubmit = function (evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var a = parseInt(el('addNumberA').value);
|
||||||
|
var b = parseInt(el('addNumberB').value);
|
||||||
|
var result = invokeMonoMethod('MonoSanityClient', 'MonoSanityClient', 'Examples', 'AddNumbers', [a, b]);
|
||||||
|
el('addNumberResult').value = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
el('repeatString').onsubmit = function (evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var str = el('repeatStringStr').value;
|
||||||
|
var count = parseInt(el('repeatStringCount').value);
|
||||||
|
var result = invokeMonoMethod('MonoSanityClient', 'MonoSanityClient', 'Examples', 'RepeatString', [str, count]);
|
||||||
|
el('repeatStringResult').value = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
el('triggerException').onsubmit = function (evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var message = el('triggerExceptionMessage').value;
|
||||||
|
try {
|
||||||
|
invokeMonoMethod('MonoSanityClient', 'MonoSanityClient', 'Examples', 'TriggerException', [message]);
|
||||||
|
el('triggerExceptionMessageStackTrace').value = 'WARNING: No exception occurred';
|
||||||
|
} catch (ex) {
|
||||||
|
el('triggerExceptionMessageStackTrace').value = ex.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function el(id) {
|
||||||
|
return document.getElementById(id);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -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$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -18,6 +18,7 @@ namespace Microsoft.Blazor.Mono.Test
|
||||||
var expectedFiles = new[]
|
var expectedFiles = new[]
|
||||||
{
|
{
|
||||||
"/asmjs/mono.asm.js",
|
"/asmjs/mono.asm.js",
|
||||||
|
"/asmjs/mono.js.mem",
|
||||||
"/wasm/mono.wasm",
|
"/wasm/mono.wasm",
|
||||||
"/bcl/mscorlib.dll",
|
"/bcl/mscorlib.dll",
|
||||||
"/bcl/Facades/System.Collections.dll",
|
"/bcl/Facades/System.Collections.dll",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue