Remove old marshalled-interop API and update MonoSanity sample to use newer API

This commit is contained in:
Steve Sanderson 2017-12-15 19:16:15 +00:00
parent 99a08da321
commit 09eccb52c5
9 changed files with 144 additions and 51 deletions

View File

@ -104,6 +104,19 @@
function triggerJsException() {
throw new Error('This is a JavaScript exception.');
}
// Normally, applications would use the higher-level APIs for registering invocable
// functions, and for invoking them with automatic argument/result marshalling.
// But since this project is trying to test low-level Mono runtime capabilities,
// we implement our own marshalling here.
window.__blazorRegisteredFunctions = {
evaluateJsExpression: function (dotNetStringExpression) {
var result = eval(dotnetStringToJavaScriptString(dotNetStringExpression));
return result === null || result === undefined
? result // Pass through null/undefined so we can verify this is handled upstream
: javaScriptStringToDotNetString(result.toString());
}
};
</script>
</body>
</html>

View File

@ -43,6 +43,21 @@
}
};
window.dotnetStringToJavaScriptString = 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;
};
window.javaScriptStringToDotNetString = function dotnetStringToJavaScriptString(javaScriptString) {
var mono_string = Module.cwrap('mono_wasm_string_from_js', 'number', ['string']);
return mono_string(javaScriptString);
};
function preloadAssemblies(loadAssemblyUrls) {
var loadBclAssemblies = [
'mscorlib',
@ -77,21 +92,10 @@
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);
@ -103,7 +107,7 @@
Module.setValue(managedInt, argVal, 'i32');
Module.setValue(argsBuffer + i * 4, managedInt, 'i32');
} else if (typeof argVal === 'string') {
var managedString = mono_string(argVal);
var managedString = javaScriptStringToDotNetString(argVal);
Module.setValue(argsBuffer + i * 4, managedString, 'i32');
} else {
throw new Error('Unsupported arg type: ' + typeof argVal);

View File

@ -31,10 +31,10 @@ namespace MonoSanityClient
public static string EvaluateJavaScript(string expression)
{
var result = Runtime.InvokeJS(expression, out var resultIsException);
if (resultIsException != 0)
var result = Runtime.InvokeJS<string, object, object, string>(out var exceptionMessage, "evaluateJsExpression", expression, null, null);
if (exceptionMessage != null)
{
return $".NET got exception: {result}";
return $".NET got exception: {exceptionMessage}";
}
return $".NET received: {(result ?? "(NULL)")}";

View File

@ -7,4 +7,9 @@
<!-- Local alternative to <PackageReference Include="Microsoft.Blazor.Build" /> -->
<Import Project="..\..\src\Microsoft.Blazor.Build\ReferenceFromSource.props" />
<ItemGroup>
<!-- Share the WebAssembly.Runtime.cs source here so we get access to the same interop externs -->
<Compile Include="..\..\src\Microsoft.Blazor.Browser\Interop\WebAssembly.Runtime.cs" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
// 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.Runtime.CompilerServices;
namespace WebAssembly
{
internal static class Runtime
{
// The exact namespace, type, and method name must match the corresponding entry in
// driver.c in the Mono distribution
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern string InvokeJS(string str, out int resultIsException);
}
}

View File

@ -0,0 +1,101 @@
// 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 WebAssembly;
namespace Microsoft.Blazor.Browser.Interop
{
/// <summary>
/// Provides methods for invoking preregistered JavaScript functions from .NET code.
/// </summary>
public static class RegisteredFunction
{
/// <summary>
/// Invokes the JavaScript function registered with the specified identifier.
///
/// When using this overload, all arguments will be supplied as <see cref="System.Object" />
/// references, meaning that any reference types will be boxed. If you are passing
/// 3 or fewer arguments, it is preferable to instead call the overload that
/// specifies generic type arguments for each argument.
/// </summary>
/// <typeparam name="TRes">The .NET type corresponding to the function's return value type.</typeparam>
/// <param name="identifier">The identifier used when registering the target function.</param>
/// <param name="args">The arguments to pass, each of which will be supplied as a <see cref="System.Object" /> instance.</param>
/// <returns>The result of the function invocation.</returns>
public static TRes Invoke<TRes>(string identifier, params object[] args)
{
var result = Runtime.InvokeJSArray<TRes>(out var exception, identifier, args);
return exception != null
? throw new JavaScriptException(exception)
: result;
}
/// <summary>
/// Invokes the JavaScript function registered with the specified identifier.
/// </summary>
/// <typeparam name="TRes">The .NET type corresponding to the function's return value type.</typeparam>
/// <param name="identifier">The identifier used when registering the target function.</param>
/// <returns>The result of the function invocation.</returns>
public static TRes Invoke<T0, TRes>(string identifier)
{
var result = Runtime.InvokeJS<object, object, object, TRes>(out var exception, identifier, null, null, null);
return exception != null
? throw new JavaScriptException(exception)
: result;
}
/// <summary>
/// Invokes the JavaScript function registered with the specified identifier.
/// </summary>
/// <typeparam name="T0">The type of the first argument.</typeparam>
/// <typeparam name="TRes">The .NET type corresponding to the function's return value type.</typeparam>
/// <param name="identifier">The identifier used when registering the target function.</param>
/// <param name="arg0">The first argument.</param>
/// <returns>The result of the function invocation.</returns>
public static TRes Invoke<T0, TRes>(string identifier, T0 arg0)
{
var result = Runtime.InvokeJS<T0, object, object, TRes>(out var exception, identifier, arg0, null, null);
return exception != null
? throw new JavaScriptException(exception)
: result;
}
/// <summary>
/// Invokes the JavaScript function registered with the specified identifier.
/// </summary>
/// <typeparam name="T0">The type of the first argument.</typeparam>
/// <typeparam name="T1">The type of the second argument.</typeparam>
/// <typeparam name="TRes">The .NET type corresponding to the function's return value type.</typeparam>
/// <param name="identifier">The identifier used when registering the target function.</param>
/// <param name="arg0">The first argument.</param>
/// <param name="arg1">The second argument.</param>
/// <returns>The result of the function invocation.</returns>
public static TRes Invoke<T0, T1, TRes>(string identifier, T0 arg0, T1 arg1)
{
var result = Runtime.InvokeJS<T0, T1, object, TRes>(out var exception, identifier, arg0, arg1, null);
return exception != null
? throw new JavaScriptException(exception)
: result;
}
/// <summary>
/// Invokes the JavaScript function registered with the specified identifier.
/// </summary>
/// <typeparam name="T0">The type of the first argument.</typeparam>
/// <typeparam name="T1">The type of the second argument.</typeparam>
/// <typeparam name="T2">The type of the third argument.</typeparam>
/// <typeparam name="TRes">The .NET type corresponding to the function's return value type.</typeparam>
/// <param name="identifier">The identifier used when registering the target function.</param>
/// <param name="arg0">The first argument.</param>
/// <param name="arg1">The second argument.</param>
/// <param name="arg2">The third argument.</param>
/// <returns>The result of the function invocation.</returns>
public static TRes Invoke<T0, T1, T2, TRes>(string identifier, T0 arg0, T1 arg1, T2 arg2)
{
var result = Runtime.InvokeJS<T0, T1, T2, TRes>(out var exception, identifier, arg0, arg1, arg2);
return exception != null
? throw new JavaScriptException(exception)
: result;
}
}
}

View File

@ -1,33 +1,19 @@
// 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 Microsoft.Blazor.Browser.Interop;
using System.Runtime.CompilerServices;
namespace WebAssembly
{
internal static class Runtime
{
public static string EvaluateJavaScript(string expression)
{
var result = InvokeJS(expression, out var resultIsException);
if (resultIsException != 0)
{
throw new JavaScriptException(result);
}
return result;
}
// The exact namespace, type, and method name must match the corresponding entry in
// The exact namespace, type, and method names must match the corresponding entry in
// driver.c in the Mono distribution
[MethodImpl(MethodImplOptions.InternalCall)]
static extern string InvokeJS(string str, out int resultIsException);
// The exact namespace, type, and method name must match the corresponding entry in
// driver.c in the Mono distribution
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern object InvokeJSUnmarshalled(string funcExpression, object[] args, out int resultIsException);
public static extern TRes InvokeJS<T0, T1, T2, TRes>(out string exception, string funcName, T0 arg0, T1 arg1, T2 arg2);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern TRes InvokeJSArray<TRes>(out string exception, string funcName, object[] args);
}
}

View File

@ -7,7 +7,6 @@ namespace Microsoft.Blazor.Browser
{
public Renderer()
{
WebAssembly.Runtime.EvaluateJavaScript("console.log('Renderer')");
}
}
}

View File

@ -75,7 +75,7 @@ namespace Microsoft.Blazor.E2ETest.Tests
SetValue(Browser, "callJsEvalExpression", "triggerJsException()");
Browser.FindElement(By.CssSelector("#callJs button")).Click();
var result = GetValue(Browser, "callJsResult");
Assert.StartsWith(".NET got exception: Error: This is a JavaScript exception.", result);
Assert.StartsWith(".NET got exception: This is a JavaScript exception.", result);
// Also verify we got a stack trace
Assert.Contains("at triggerJsException", result);