import { MethodHandle , System_Object , System_String , System_Array , Pointer , Platform } from '../Platform' ; import { getFileNameFromUrl } from '../Url' ; import { attachDebuggerHotkey , hasDebuggingEnabled } from './MonoDebugger' ; const assemblyHandleCache = { } ; const typeHandleCache = { } ; const methodHandleCache = { } ; let assembly_load ; let find_class ; let find_method ; let invoke_method ; let mono_string_get_utf8 ; let mono_string ; const appBinDirName = 'appBinDir' ; const uint64HighOrderShift = Math . pow ( 2 , 32 ) ; const maxSafeNumberHighPart = Math . pow ( 2 , 21 ) - 1 ; export const monoPlatform = { start : function start ( loadAssemblyUrls ) { return new Promise < void > ( ( resolve , reject ) => { attachDebuggerHotkey ( loadAssemblyUrls ) ; window [ 'Browser' ] = { init : ( ) => { } , } ; window [ 'Module' ] = createEmscriptenModuleInstance ( loadAssemblyUrls , resolve , reject ) ; addScriptTagsToDocument ( ) ; } ) ; } , findMethod : findMethod , callEntryPoint : function callEntryPoint ( assemblyName , entrypointMethod , args ) : void { const entrypointSegments = entrypointMethod . split ( '::' ) ; if ( entrypointSegments . length != 2 ) { throw new Error ( 'Malformed entry point method name; could not resolve class name and method name.' ) ; } const typeFullName = entrypointSegments [ 0 ] ; const methodName = entrypointSegments [ 1 ] ; const lastDot = typeFullName . lastIndexOf ( '.' ) ; const namespace = lastDot > - 1 ? typeFullName . substring ( 0 , lastDot ) : '' ; const typeShortName = lastDot > - 1 ? typeFullName . substring ( lastDot + 1 ) : typeFullName ; const entryPointMethodHandle = monoPlatform . findMethod ( assemblyName , namespace , typeShortName , methodName ) ; monoPlatform . callMethod ( entryPointMethodHandle , null , args ) ; } , callMethod : function callMethod ( method , target , args ) : System_Object { if ( args . length > 4 ) { 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 ) { throw new Error ( monoPlatform . toJavaScriptString ( < System_String > res ) ) ; } return res ; } finally { Module . stackRestore ( stack ) ; } } , toJavaScriptString : function toJavaScriptString ( managedString ) { const utf8 = mono_string_get_utf8 ( managedString ) ; const res = Module . UTF8ToString ( utf8 ) ; Module . _free ( utf8 as any ) ; return res ; } , toDotNetString : function toDotNetString ( jsString ) : System_String { return mono_string ( jsString ) ; } , toUint8Array : function toUint8Array ( array ) : Uint8Array { const dataPtr = getArrayDataPointer ( array ) ; const length = Module . getValue ( dataPtr , 'i32' ) ; return new Uint8Array ( Module . HEAPU8 . buffer , dataPtr + 4 , length ) ; } , getArrayLength : function getArrayLength ( array ) : number { return Module . getValue ( getArrayDataPointer ( array ) , 'i32' ) ; } , getArrayEntryPtr : function getArrayEntryPtr < TPtr extends Pointer > ( array , index , itemSize ) : TPtr { const address = getArrayDataPointer ( array ) + 4 + index * itemSize ; return address as any as TPtr ; } , getObjectFieldsBaseAddress : function getObjectFieldsBaseAddress ( referenceTypedObject ) : Pointer { return ( referenceTypedObject as any as number + 8 ) as any as Pointer ; } , readInt16Field : function readHeapInt16 ( baseAddress , fieldOffset ? ) : number { return Module . getValue ( ( baseAddress as any as number ) + ( fieldOffset || 0 ) , 'i16' ) ; } , readInt32Field : function readHeapInt32 ( baseAddress , fieldOffset ? ) : number { return Module . getValue ( ( baseAddress as any as number ) + ( fieldOffset || 0 ) , 'i32' ) ; } , readUint64Field : function readHeapUint64 ( baseAddress , fieldOffset ? ) : number { const address = ( baseAddress as any as number ) + ( fieldOffset || 0 ) ; const heapU32Index = address >> 2 ; const highPart = Module . HEAPU32 [ heapU32Index + 1 ] ; if ( highPart > maxSafeNumberHighPart ) { throw new Error ( `Cannot read uint64 with high order part ${ highPart }, because the result would exceed Number.MAX_SAFE_INTEGER.` ) ; } return ( highPart * uint64HighOrderShift ) + Module . HEAPU32 [ heapU32Index ] ; } , readFloatField : function readHeapFloat ( baseAddress , fieldOffset ? ) : number { return Module . getValue ( ( baseAddress as any as number ) + ( fieldOffset || 0 ) , 'float' ) ; } , readObjectField : function readHeapObject < T extends System_Object > ( baseAddress , fieldOffset ? ) : T { return Module . getValue ( ( baseAddress as any as number ) + ( fieldOffset || 0 ) , 'i32' ) as any as T ; } , readStringField : function readHeapObject ( baseAddress , fieldOffset ? ) : string | null { const fieldValue = Module . getValue ( ( baseAddress as any as number ) + ( fieldOffset || 0 ) , 'i32' ) ; return fieldValue === 0 ? null : monoPlatform . toJavaScriptString ( fieldValue as any as System_String ) ; } , readStructField : function readStructField < T extends Pointer > ( baseAddress , fieldOffset ? ) : T { return ( ( baseAddress as any as number ) + ( fieldOffset || 0 ) ) as any as T ; } , } ; function findAssembly ( assemblyName ) { 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 , namespace , className ) { 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 , namespace , className , methodName ) { 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 ) { throw new Error ( 'This browser does not support WebAssembly.' ) ; } const scriptElem = document . createElement ( 'script' ) ; scriptElem . src = '_framework/wasm/mono.js' ; scriptElem . defer = true ; document . body . appendChild ( scriptElem ) ; } function createEmscriptenModuleInstance ( loadAssemblyUrls , onReady , onError ) { const module = { } as typeof Module ; const wasmBinaryFile = '_framework/wasm/mono.wasm' ; const suppressMessages = [ 'DEBUGGING ENABLED' ] ; module . print = line => ( suppressMessages . indexOf ( line ) < 0 && console . log ( `WASM: ${ line }` ) ) ; module . printErr = line => console . error ( `WASM: ${ line }` ) ; module . preRun = [ ] ; module . postRun = [ ] ; module . preloadPlugins = [ ] ; module . locateFile = fileName => { switch ( fileName ) { case 'mono.wasm' : return wasmBinaryFile ; default : return fileName ; } } ; module . preRun . push ( ( ) => { const mono_wasm_add_assembly = Module . cwrap ( 'mono_wasm_add_assembly' , null , [ 'string' , '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 . loaded_files = [ ] ; loadAssemblyUrls . forEach ( url => { const filename = getFileNameFromUrl ( url ) ; const runDependencyId = `blazor:${ filename }` ; addRunDependency ( runDependencyId ) ; asyncLoad ( url ) . then ( data => { const heapAddress = Module . _malloc ( data . length ) ; const heapMemory = new Uint8Array ( Module . HEAPU8 . buffer , heapAddress , data . length ) ; heapMemory . set ( data ) ; mono_wasm_add_assembly ( filename , heapAddress , data . length ) ; MONO . loaded_files . push ( toAbsoluteUrl ( url ) ) ; removeRunDependency ( runDependencyId ) ; } , errorInfo => { const isPdb404 = errorInfo instanceof XMLHttpRequest && errorInfo . status === 404 && filename . match ( /\.pdb$/ ) ; if ( ! isPdb404 ) { onError ( errorInfo ) ; } removeRunDependency ( runDependencyId ) ; } ) ; } ) ; } ) ; module . postRun . push ( ( ) => { MONO . mono_wasm_setenv ( "MONO_URI_DOTNETRELATIVEORABSOLUTE" , "true" ) ; const load_runtime = Module . cwrap ( 'mono_wasm_load_runtime' , null , [ 'string' , 'number' ] ) ; load_runtime ( appBinDirName , hasDebuggingEnabled ( ) ? 1 : 0 ) ; MONO . mono_wasm_runtime_is_ready = true ; attachInteropInvoker ( ) ; onReady ( ) ; } ) ; return module ; } const anchorTagForAbsoluteUrlConversions = document . createElement ( 'a' ) ; function toAbsoluteUrl ( possiblyRelativeUrl ) { anchorTagForAbsoluteUrlConversions . href = possiblyRelativeUrl ; return anchorTagForAbsoluteUrlConversions . href ; } function asyncLoad ( url ) { return new Promise < Uint8Array > ( ( resolve , reject ) => { const xhr = new XMLHttpRequest ( ) ; xhr . open ( 'GET' , url , true ) ; xhr . responseType = 'arraybuffer' ; xhr . onload = function xhr_onload ( ) { if ( xhr . status == 200 || xhr . status == 0 && xhr . response ) { const asm = new Uint8Array ( xhr . response ) ; resolve ( asm ) ; } else { reject ( xhr ) ; } } ; xhr . onerror = reject ; xhr . send ( undefined ) ; } ) ; } function getArrayDataPointer < T > ( array ) { return < number > < any > array + 12 ; } function attachInteropInvoker ( ) { 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' ) ; DotNet . attachDispatcher ( { beginInvokeDotNetFromJS : ( callId , assemblyName , methodIdentifier , dotNetObjectId , argsJson ) : void => { if ( ! dotNetObjectId && ! assemblyName ) { throw new Error ( 'Either assemblyName or dotNetObjectId must have a non null value.' ) ; } const assemblyNameOrDotNetObjectId = dotNetObjectId ? dotNetObjectId . toString ( ) : assemblyName ; monoPlatform . callMethod ( dotNetDispatcherBeginInvokeMethodHandle , null , [ callId ? monoPlatform . toDotNetString ( callId . toString ( ) ) : null , monoPlatform . toDotNetString ( assemblyNameOrDotNetObjectId ) , monoPlatform . toDotNetString ( methodIdentifier ) , monoPlatform . toDotNetString ( argsJson ) , ] ) ; } , endInvokeJSFromDotNet : ( asyncHandle , succeeded , serializedArgs ) : void => { monoPlatform . callMethod ( dotNetDispatcherEndInvokeJSMethodHandle , null , [ monoPlatform . toDotNetString ( 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 ; } , } ) ; }