|
|
|
|
@ -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;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|