Files
sbox-public/engine/Tools/InteropGen/Writer/ManagerWriter.cs
Lorenz Junglas 64fc154f39 Fix private PR formatting check (#3473)
* Make sure our private PR action run also check BuildTool formatting

* Format BuildTools
2025-11-27 10:17:39 +00:00

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();
}
}