using System.Collections.Generic; using System.Linq; namespace Facepunch.InteropGen; internal partial class ManagerWriter : BaseWriter { public ManagerWriter( Definition definitions, string targetName ) : base( definitions, targetName ) { } public override void Generate() { Header(); HelperFunctions(); Imports(); PointerStructs(); StartBlock( $"namespace {definitions.ManagedNamespace}" ); { Exports(); NativeInterop(); } EndBlock(); } private void Header() { WriteLine( "// " ); WriteLine( "// This file was generated by a tool." ); WriteLine( "// Changes to this file may cause incorrect behavior and will be lost if" ); WriteLine( "// the code is regenerated." ); WriteLine( "// " ); WriteLine( "" ); WriteLine( "using System;" ); WriteLine( "using System.Collections.Generic;" ); WriteLine( "using System.Runtime.CompilerServices;" ); WriteLine( "using System.Runtime.InteropServices;" ); WriteLine( "" ); } private void HelperFunctions() { StartBlock( $"internal static class {definitions.CustomNamespace}" ); { StartBlock( "public static int Convert( Type t )" ); { foreach ( Class c in definitions.Classes.OrderByDescending( x => x.ClassDepth ) ) { if ( c.Native ) { continue; } if ( c.Static ) { continue; } WriteLine( $"if ( typeof( global::{c.ManagedNameWithNamespace} ).IsAssignableFrom( t ) ) return {c.ClassIdent};" ); } WriteLine(); WriteLine( "throw new System.Exception( $\"Can't handle type {t}\" );" ); } EndBlock(); } EndBlock(); } private void NativeInterop() { IEnumerable exports = definitions.Classes.Where( x => x.Native == true ).SelectMany( x => x.Functions ); IEnumerable imports = definitions.Classes.Where( x => x.Native == false ).Where( x => !ShouldSkip( x ) ).SelectMany( x => x.Functions ); StartBlock( "internal unsafe static partial class NativeInterop" ); { WriteLine( "static IntPtr _nativeLibraryHandle;" ); WriteLine( "static bool _initialized;" ); WriteLine(); ErrorFunction(); WriteLine( "[UnmanagedFunctionPointer( CallingConvention.Cdecl )]" ); WriteLine( "delegate void NetCoreImportDelegate( int hash, void* imports, void* exports, int* structSizes );" ); WriteLine(); StartBlock( "internal static void Initialize()" ); { WriteLine( "if ( _initialized ) return;" ); WriteLine(); WriteLine( $"if ( !NativeLibrary.TryLoad( System.IO.Path.Combine( NetCore.NativeDllPath, \"{definitions.NativeDll}\" ), out var nativeDll ) )" ); WriteLine( $" Sandbox.Interop.NativeAssemblyLoadFailed( \"{definitions.NativeDll}\" );" ); WriteLine( "_nativeLibraryHandle = nativeDll;" ); WriteLine(); WriteLine( $"IntPtr nativeInitPtr = NativeLibrary.GetExport( nativeDll, \"igen_{definitions.Ident}\" );" ); WriteLine( $"if ( nativeInitPtr == IntPtr.Zero ) throw new System.Exception( \"Couldn't load from {definitions.NativeDll}\" );" ); WriteLine(); WriteLine( $"var nativeInit = Marshal.GetDelegateForFunctionPointer( nativeInitPtr );" ); WriteLine( $"if ( nativeInit == null ) throw new System.Exception( \"Couldn't load from {definitions.NativeDll}\" );" ); int i = 0; // // Managed Functions // { WriteLine(); WriteLine( $"var managedFunctions = new IntPtr[{imports.Count()}]" ); StartBlock( null ); foreach ( Function f in imports ) { Class c = f.Class; IEnumerable managedArgs = c.SelfArg( false, f.Static ).Concat( f.Parameters ).Concat( new[] { f.Return } ).Where( x => x.IsRealArgument ).Select( x => $"{x.GetManagedDelegateType( true )}" ); string managedArgss = $"{string.Join( ", ", managedArgs )}"; WriteLine( $"(IntPtr) (delegate* unmanaged<{managedArgss}>) &Exports.{f.MangledName}," ); } EndBlock( ";" ); } { int c = definitions.Classes.Where( x => x.Native == true ).Where( x => !ShouldSkip( x ) ).Sum( x => { int i = 0; Class bc = x.BaseClass; while ( bc != null ) { Class subclass = bc; i += 2; bc = bc.BaseClass; } i += x.Functions.Count; i += x.Variables.Count * 2; return i; } ); c += 1; WriteLine(); WriteLine( $"var nativeFunctions = new IntPtr[{c}];" ); } { int c = definitions.Classes.Where( x => x.Native == true ).Where( x => !ShouldSkip( x ) ).Count(); WriteLine(); WriteLine( $"var structSizes = new int[]" ); StartBlock( null ); foreach ( Struct s in definitions.Structs ) { if ( ShouldSkip( s ) ) { continue; } string size_of = $"sizeof( {s.ManagedNameWithNamespace} )"; WriteLine( $"{size_of}," ); i++; } EndBlock( ";" ); } WriteLine(); WriteLine(); WriteLine( "fixed ( void* m = managedFunctions )" ); WriteLine( "fixed ( void* n = nativeFunctions )" ); WriteLine( "fixed ( int* s = structSizes )" ); StartBlock( null ); WriteLine( $"nativeInit( {definitions.Hash}, m, n, s );" ); EndBlock(); WriteLine(); i = 1; WriteLine( $"var onError = Marshal.GetDelegateForFunctionPointer<_ErrorFunction>( nativeFunctions[0] );" ); WriteLine(); StartBlock( "try" ); i = 1; foreach ( Class c in definitions.Classes.Where( x => x.Native == true ) ) { if ( ShouldSkip( c ) ) { continue; } string namespc = $"{c.ManagedNamespace}.{c.ManagedName}".Trim( '.' ); Class bc = c.BaseClass; while ( bc != null ) { Class subclass = bc; WriteLine( $"{namespc}.{InternalNative}.From_{subclass.ManagedName}_To_{c.ManagedName} = (delegate* unmanaged[SuppressGCTransition]< IntPtr, IntPtr >) nativeFunctions[{i++}];" ); WriteLine( $"{namespc}.{InternalNative}.To_{subclass.ManagedName}_From_{c.ManagedName} = (delegate* unmanaged[SuppressGCTransition]< IntPtr, IntPtr >) nativeFunctions[{i++}];" ); bc = bc.BaseClass; } foreach ( Function f in c.Functions ) { IEnumerable managedArgs = c.SelfArg( false, f.Static ).Concat( f.Parameters ).Where( x => x.IsRealArgument ).Select( x => $"{x.GetManagedDelegateType( false )}" ).Concat( new[] { f.Return.GetManagedDelegateType( true ) } ); string managedArgss = $"{string.Join( ", ", managedArgs )}"; string nogc = ""; if ( f.IsNoGC ) { nogc = "[SuppressGCTransition]"; } WriteLine( $"{namespc}.{InternalNative}.{f.MangledName} = (delegate* unmanaged{nogc}< {managedArgss} >) nativeFunctions[{i++}];" ); } foreach ( Variable f in c.Variables ) { WriteLine( $"{namespc}.{InternalNative}.Get__{f.MangledName} = (delegate* unmanaged[SuppressGCTransition])( nativeFunctions[{i++}] );" ); WriteLine( $"{namespc}.{InternalNative}.Set__{f.MangledName} = (delegate* unmanaged[SuppressGCTransition])( nativeFunctions[{i++}] );" ); } } EndBlock(); StartBlock( "catch ( System.Exception ___e )" ); { WriteLine( "onError( $\"{___e.Message}\\n\\n{___e.StackTrace}\" );" ); } EndBlock(); WriteLine( "_initialized = true;" ); } EndBlock(); StartBlock( "internal static void Free()" ); { WriteLine( "if ( _nativeLibraryHandle == IntPtr.Zero ) return;" ); WriteLine( "NativeLibrary.Free( _nativeLibraryHandle );" ); WriteLine( "_nativeLibraryHandle = IntPtr.Zero;" ); WriteLine( "_initialized = false;" ); } EndBlock(); } EndBlock(); if ( definitions.InitFrom == "Managed" ) { StartBlock( "internal unsafe static partial class Initialize" ); { ManagedInit(); } EndBlock(); } } private void ErrorFunction() { WriteLine( "[UnmanagedFunctionPointer( CallingConvention.Cdecl )]" ); WriteLine( "internal delegate void _ErrorFunction( string message );" ); WriteLine(); } private void ManagedInit() { WriteLine( "[UnmanagedFunctionPointer( CallingConvention.Cdecl )]" ); WriteLine( "internal delegate void _InitFunc( IntPtr initFunc, IntPtr function_pointers, int count );" ); WriteLine(); WriteLine( "[UnmanagedFunctionPointer( CallingConvention.Cdecl )]" ); WriteLine( "internal delegate void _ManagedInit( int hash, IntPtr fp, int* struct_sizes );" ); WriteLine(); StartBlock( "public static void FromLibrary( string dllPath )" ); { WriteLine( $"var library = NativeLibrary.Load( dllPath );" ); WriteLine( $"if ( library == null ) throw new System.Exception( $\"Couldn't load {{dllPath}}\" );" ); WriteLine( $"" ); WriteLine( $"var initFunc = NativeLibrary.GetExport( library, \"InitInterop_{definitions.Hash}\" );" ); WriteLine( $"if ( initFunc == null ) throw new System.Exception( $\"Couldn't find export in {{dllPath}} - possibly out of date?\" );" ); WriteLine( $"" ); WriteLine( $"var fp = Marshal.GetFunctionPointerForDelegate<_ManagedInit>( NativeInterop.Initialize );" ); WriteLine( $"" ); int exportCount = definitions.Classes.Where( x => x.Native == false ).SelectMany( x => x.Functions ).Count(); if ( exportCount > 0 ) { StartBlock( $"IntPtr[] functions = new[]" ); { foreach ( Class c in definitions.Classes.Where( x => x.Native == false ) ) { if ( ShouldSkip( c ) ) { continue; } foreach ( Function f in c.Functions ) { WriteLine( $"FunctionPointerFor( Exports.{f.MangledName} )," ); } } } EndBlock( ";" ); WriteLine( $"" ); WriteLine( $"var init = Marshal.GetDelegateForFunctionPointer<_InitFunc>( initFunc );" ); WriteLine( $"" ); StartBlock( "fixed ( IntPtr* ptr = functions )" ); { WriteLine( "init( fp, (IntPtr)ptr, functions.Length );" ); } EndBlock(); } else { WriteLine( $"var init = Marshal.GetDelegateForFunctionPointer<_InitFunc>( initFunc );" ); WriteLine( $"" ); WriteLine( "init( fp, IntPtr.Zero, 0 );" ); } } EndBlock(); WriteLine(); WriteLine( $"// Store for delegates, to stop them getting garbage collected" ); WriteLine( $"static List delegateStore = new List();" ); WriteLine( $"" ); WriteLine( $"// Store the delegate and return a function pointer" ); StartBlock( $"static IntPtr FunctionPointerFor( T del ) where T : Delegate" ); WriteLine( $" delegateStore.Add( del );" ); WriteLine( $" return Marshal.GetFunctionPointerForDelegate( del );" ); EndBlock(); } }