Merge branch 'blazor-wasm'

This commit is contained in:
Brennan Conroy 2020-01-02 16:03:19 -08:00
commit a68fabb959
7 changed files with 37 additions and 139 deletions

View File

@ -9,9 +9,9 @@
-->
<Dependencies>
<ProductDependencies>
<Dependency Name="Microsoft.AspNetCore.Blazor.Mono" Version="3.1.0-preview4.19576.1">
<Dependency Name="Microsoft.AspNetCore.Blazor.Mono" Version="3.1.0-preview4.19578.1">
<Uri>https://github.com/aspnet/Blazor</Uri>
<Sha>d1f3a3db8a379cc887cd6dde0acf77dcd5e16c36</Sha>
<Sha>9ff01af4257317a90b64959fe1c87aef3da4a36f</Sha>
</Dependency>
<Dependency Name="Microsoft.AspNetCore.Razor.Language" Version="5.0.0-alpha.1.19630.1">
<Uri>https://github.com/aspnet/AspNetCore-Tooling</Uri>

View File

@ -95,7 +95,7 @@
<!-- Only listed explicitly to workaround https://github.com/dotnet/cli/issues/10528 -->
<MicrosoftNETCorePlatformsPackageVersion>5.0.0-alpha.1.19556.7</MicrosoftNETCorePlatformsPackageVersion>
<!-- Packages from aspnet/Blazor -->
<MicrosoftAspNetCoreBlazorMonoPackageVersion>3.1.0-preview4.19576.1</MicrosoftAspNetCoreBlazorMonoPackageVersion>
<MicrosoftAspNetCoreBlazorMonoPackageVersion>3.1.0-preview4.19578.1</MicrosoftAspNetCoreBlazorMonoPackageVersion>
<!-- Packages from aspnet/Extensions -->
<InternalAspNetCoreAnalyzersPackageVersion>5.0.0-alpha.1.19626.1</InternalAspNetCoreAnalyzersPackageVersion>
<MicrosoftAspNetCoreAnalyzerTestingPackageVersion>5.0.0-alpha.1.19626.1</MicrosoftAspNetCoreAnalyzerTestingPackageVersion>

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Blazor.Hosting
{
var assembly = Assembly.Load(assemblyName);
var entrypoint = FindUnderlyingEntrypoint(assembly);
var @params = entrypoint.GetParameters().Length == 1 ? new object[] { args } : new object[] { };
var @params = entrypoint.GetParameters().Length == 1 ? new object[] { args ?? Array.Empty<string>() } : new object[] { };
entrypointResult = entrypoint.Invoke(null, @params);
}
catch (Exception syncException)

File diff suppressed because one or more lines are too long

View File

@ -1,19 +1,9 @@
import { MethodHandle, System_Object, System_String, System_Array, Pointer, Platform } from '../Platform';
import { System_Object, System_String, System_Array, Pointer, Platform } from '../Platform';
import { getFileNameFromUrl } from '../Url';
import { attachDebuggerHotkey, hasDebuggingEnabled } from './MonoDebugger';
import { showErrorNotification } from '../../BootErrors';
const assemblyHandleCache: { [assemblyName: string]: number } = {};
const typeHandleCache: { [fullyQualifiedTypeName: string]: number } = {};
const methodHandleCache: { [fullyQualifiedMethodName: string]: MethodHandle } = {};
let assembly_load: (assemblyName: string) => number;
let find_class: (assemblyHandle: number, namespace: string, className: string) => number;
let find_method: (typeHandle: number, methodName: string, unknownArg: number) => MethodHandle;
let invoke_method: (method: MethodHandle, target: System_Object, argsArrayPtr: number, exceptionFlagIntPtr: number) => System_Object;
let mono_string_array_new: (length: number) => System_Array<System_String>;
let mono_string_get_utf8: (managedString: System_String) => Mono.Utf8Ptr;
let mono_string: (jsString: string) => System_String;
const appBinDirName = 'appBinDir';
const uint64HighOrderShift = Math.pow(2, 32);
const maxSafeNumberHighPart = Math.pow(2, 21) - 1; // The high-order int32 from Number.MAX_SAFE_INTEGER
@ -38,8 +28,6 @@ export const monoPlatform: Platform = {
});
},
findMethod: findMethod,
callEntryPoint: function callEntryPoint(assemblyName: string) {
// Instead of using Module.mono_call_assembly_entry_point, we have our own logic for invoking
// the entrypoint which adds support for async main.
@ -47,40 +35,9 @@ export const monoPlatform: Platform = {
// In the future, we might want Blazor.start to return a Promise<Promise<value>>, where the
// outer promise reflects the startup process, and the inner one reflects the possibly-async
// .NET entrypoint method.
const invokeEntrypoint = findMethod('Microsoft.AspNetCore.Blazor', 'Microsoft.AspNetCore.Blazor.Hosting', 'EntrypointInvoker', 'InvokeEntrypoint');
this.callMethod(invokeEntrypoint, null, [
this.toDotNetString(assemblyName),
mono_string_array_new(0) // In the future, we may have a way of supplying arg strings. For now, we always supply an empty string[].
]);
},
callMethod: function callMethod(method: MethodHandle, target: System_Object, args: System_Object[]): System_Object {
if (args.length > 4) {
// Hopefully this restriction can be eased soon, but for now make it clear what's going on
throw new Error(`Currently, MonoPlatform supports passing a maximum of 4 arguments from JS to .NET. You tried to pass ${args.length}.`);
}
const stack = Module.stackSave();
try {
const argsBuffer = Module.stackAlloc(args.length);
const exceptionFlagManagedInt = Module.stackAlloc(4);
for (let i = 0; i < args.length; ++i) {
Module.setValue(argsBuffer + i * 4, args[i], 'i32');
}
Module.setValue(exceptionFlagManagedInt, 0, 'i32');
const res = invoke_method(method, target, argsBuffer, exceptionFlagManagedInt);
if (Module.getValue(exceptionFlagManagedInt, 'i32') !== 0) {
// If the exception flag is set, the returned value is exception.ToString()
throw new Error(monoPlatform.toJavaScriptString(<System_String>res));
}
return res;
} finally {
Module.stackRestore(stack);
}
const invokeEntrypoint = bindStaticMethod('Microsoft.AspNetCore.Blazor', 'Microsoft.AspNetCore.Blazor.Hosting.EntrypointInvoker', 'InvokeEntrypoint');
// Note we're passing in null because passing arrays is problematic until https://github.com/mono/mono/issues/18245 is resolved.
invokeEntrypoint(assemblyName, null);
},
toJavaScriptString: function toJavaScriptString(managedString: System_String) {
@ -94,10 +51,6 @@ export const monoPlatform: Platform = {
return res;
},
toDotNetString: function toDotNetString(jsString: string): System_String {
return mono_string(jsString);
},
toUint8Array: function toUint8Array(array: System_Array<any>): Uint8Array {
const dataPtr = getArrayDataPointer(array);
const length = Module.getValue(dataPtr, 'i32');
@ -158,44 +111,6 @@ export const monoPlatform: Platform = {
},
};
function findAssembly(assemblyName: string): number {
let assemblyHandle = assemblyHandleCache[assemblyName];
if (!assemblyHandle) {
assemblyHandle = assembly_load(assemblyName);
if (!assemblyHandle) {
throw new Error(`Could not find assembly "${assemblyName}"`);
}
assemblyHandleCache[assemblyName] = assemblyHandle;
}
return assemblyHandle;
}
function findType(assemblyName: string, namespace: string, className: string): number {
const fullyQualifiedTypeName = `[${assemblyName}]${namespace}.${className}`;
let typeHandle = typeHandleCache[fullyQualifiedTypeName];
if (!typeHandle) {
typeHandle = find_class(findAssembly(assemblyName), namespace, className);
if (!typeHandle) {
throw new Error(`Could not find type "${className}" in namespace "${namespace}" in assembly "${assemblyName}"`);
}
typeHandleCache[fullyQualifiedTypeName] = typeHandle;
}
return typeHandle;
}
function findMethod(assemblyName: string, namespace: string, className: string, methodName: string): MethodHandle {
const fullyQualifiedMethodName = `[${assemblyName}]${namespace}.${className}::${methodName}`;
let methodHandle = methodHandleCache[fullyQualifiedMethodName];
if (!methodHandle) {
methodHandle = find_method(findType(assemblyName, namespace, className), methodName, -1);
if (!methodHandle) {
throw new Error(`Could not find method "${methodName}" on type "${namespace}.${className}"`);
}
methodHandleCache[fullyQualifiedMethodName] = methodHandle;
}
return methodHandle;
}
function addScriptTagsToDocument() {
const browserSupportsNativeWebAssembly = typeof WebAssembly !== 'undefined' && WebAssembly.validate;
if (!browserSupportsNativeWebAssembly) {
@ -254,26 +169,8 @@ function createEmscriptenModuleInstance(loadAssemblyUrls: string[], onReady: ()
'number',
'number',
]);
assembly_load = Module.cwrap('mono_wasm_assembly_load', 'number', ['string']);
find_class = Module.cwrap('mono_wasm_assembly_find_class', 'number', [
'number',
'string',
'string',
]);
find_method = Module.cwrap('mono_wasm_assembly_find_method', 'number', [
'number',
'string',
'number',
]);
invoke_method = Module.cwrap('mono_wasm_invoke_method', 'number', [
'number',
'number',
'number',
]);
mono_string_get_utf8 = Module.cwrap('mono_wasm_string_get_utf8', 'number', ['number']);
mono_string = Module.cwrap('mono_wasm_string_from_js', 'number', ['string']);
mono_string_array_new = Module.cwrap('mono_wasm_string_array_new', 'number', ['number']);
MONO.loaded_files = [];
@ -346,10 +243,16 @@ function getArrayDataPointer<T>(array: System_Array<T>): number {
return <number><any>array + 12; // First byte from here is length, then following bytes are entries
}
function bindStaticMethod(assembly: string, typeName: string, method: string) : (...args: any[]) => any {
// Fully qualified name looks like this: "[debugger-test] Math:IntAdd"
const fqn = `[${assembly}] ${typeName}:${method}`;
return Module.mono_bind_static_method(fqn);
}
function attachInteropInvoker(): void {
const dotNetDispatcherInvokeMethodHandle = findMethod('Mono.WebAssembly.Interop', 'Mono.WebAssembly.Interop', 'MonoWebAssemblyJSRuntime', 'InvokeDotNet');
const dotNetDispatcherBeginInvokeMethodHandle = findMethod('Mono.WebAssembly.Interop', 'Mono.WebAssembly.Interop', 'MonoWebAssemblyJSRuntime', 'BeginInvokeDotNet');
const dotNetDispatcherEndInvokeJSMethodHandle = findMethod('Mono.WebAssembly.Interop', 'Mono.WebAssembly.Interop', 'MonoWebAssemblyJSRuntime', 'EndInvokeJS');
const dotNetDispatcherInvokeMethodHandle = bindStaticMethod('Mono.WebAssembly.Interop', 'Mono.WebAssembly.Interop.MonoWebAssemblyJSRuntime', 'InvokeDotNet');
const dotNetDispatcherBeginInvokeMethodHandle = bindStaticMethod('Mono.WebAssembly.Interop', 'Mono.WebAssembly.Interop.MonoWebAssemblyJSRuntime', 'BeginInvokeDotNet');
const dotNetDispatcherEndInvokeJSMethodHandle = bindStaticMethod('Mono.WebAssembly.Interop', 'Mono.WebAssembly.Interop.MonoWebAssemblyJSRuntime', 'EndInvokeJS');
DotNet.attachDispatcher({
beginInvokeDotNetFromJS: (callId: number, assemblyName: string | null, methodIdentifier: string, dotNetObjectId: any | null, argsJson: string): void => {
@ -362,30 +265,25 @@ function attachInteropInvoker(): void {
? dotNetObjectId.toString()
: assemblyName;
monoPlatform.callMethod(dotNetDispatcherBeginInvokeMethodHandle, null, [
callId ? monoPlatform.toDotNetString(callId.toString()) : null,
monoPlatform.toDotNetString(assemblyNameOrDotNetObjectId),
monoPlatform.toDotNetString(methodIdentifier),
monoPlatform.toDotNetString(argsJson),
]);
dotNetDispatcherBeginInvokeMethodHandle(
callId ? callId.toString() : null,
assemblyNameOrDotNetObjectId,
methodIdentifier,
argsJson,
);
},
endInvokeJSFromDotNet: (asyncHandle, succeeded, serializedArgs): void => {
monoPlatform.callMethod(
dotNetDispatcherEndInvokeJSMethodHandle,
null,
[monoPlatform.toDotNetString(serializedArgs)]
dotNetDispatcherEndInvokeJSMethodHandle(
serializedArgs
);
},
invokeDotNetFromJS: (assemblyName, methodIdentifier, dotNetObjectId, argsJson) => {
const resultJsonStringPtr = monoPlatform.callMethod(dotNetDispatcherInvokeMethodHandle, null, [
assemblyName ? monoPlatform.toDotNetString(assemblyName) : null,
monoPlatform.toDotNetString(methodIdentifier),
dotNetObjectId ? monoPlatform.toDotNetString(dotNetObjectId.toString()) : null,
monoPlatform.toDotNetString(argsJson),
]) as System_String;
return resultJsonStringPtr
? monoPlatform.toJavaScriptString(resultJsonStringPtr)
: null;
return dotNetDispatcherInvokeMethodHandle(
assemblyName ? assemblyName : null,
methodIdentifier,
dotNetObjectId ? dotNetObjectId.toString() : null,
argsJson,
) as string;
},
});
}

View File

@ -10,7 +10,7 @@ declare namespace Module {
function FS_createPath(parent, path, canRead, canWrite);
function FS_createDataFile(parent, name, data, canRead, canWrite, canOwn);
function mono_call_assembly_entry_point(assemblyName: string, args: any[]): any;
function mono_bind_static_method(fqn: string): BoundStaticMethod;
}
// Emscripten declares these globals
@ -28,3 +28,7 @@ declare namespace MONO {
var mono_wasm_runtime_is_ready: boolean;
function mono_wasm_setenv (name: string, value: string): void;
}
// mono_bind_static_method allows arbitrary JS data types to be sent over the wire. However we are
// artifically limiting it to a subset of types that we actually use.
declare type BoundStaticMethod = (...args: (string | number | null)[]) => (string | number | null);

View File

@ -2,12 +2,8 @@ export interface Platform {
start(loadAssemblyUrls: string[]): Promise<void>;
callEntryPoint(assemblyName: string): void;
findMethod(assemblyName: string, namespace: string, className: string, methodName: string): MethodHandle;
callMethod(method: MethodHandle, target: System_Object | null, args: (System_Object | null)[]): System_Object;
toJavaScriptString(dotNetString: System_String): string;
toDotNetString(javaScriptString: string): System_String;
toUint8Array(array: System_Array<any>): Uint8Array;
getArrayLength(array: System_Array<any>): number;