mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-16 18:29:15 -05:00
* Make sure our private PR action run also check BuildTool formatting * Format BuildTools
353 lines
10 KiB
C#
353 lines
10 KiB
C#
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( "// <auto-generated>" );
|
|
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( "// </auto-generated>" );
|
|
|
|
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<Function> exports = definitions.Classes.Where( x => x.Native == true ).SelectMany( x => x.Functions );
|
|
IEnumerable<Function> 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<NetCoreImportDelegate>( 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<string> 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<string> 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]<IntPtr, {f.Return.GetManagedDelegateType( true )}>)( nativeFunctions[{i++}] );" );
|
|
WriteLine( $"{namespc}.{InternalNative}.Set__{f.MangledName} = (delegate* unmanaged[SuppressGCTransition]<IntPtr, {f.Return.GetManagedDelegateType( true )}, void>)( 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}_d>( 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<Delegate> delegateStore = new List<Delegate>();" );
|
|
WriteLine( $"" );
|
|
WriteLine( $"// Store the delegate and return a function pointer" );
|
|
StartBlock( $"static IntPtr FunctionPointerFor<T>( T del ) where T : Delegate" );
|
|
|
|
WriteLine( $" delegateStore.Add( del );" );
|
|
WriteLine( $" return Marshal.GetFunctionPointerForDelegate<T>( del );" );
|
|
EndBlock();
|
|
}
|
|
}
|