2022-09-22 08:56:07 +08:00
using dnlib.DotNet ;
2022-10-17 12:16:18 +08:00
using HybridCLR.Editor.ABI ;
2022-09-22 08:56:07 +08:00
using HybridCLR.Editor.Meta ;
2024-05-26 11:59:54 +08:00
using HybridCLR.Editor.ReversePInvokeWrap ;
2022-09-22 08:56:07 +08:00
using HybridCLR.Editor.Template ;
using System ;
using System.Collections.Generic ;
2023-06-07 11:47:17 +08:00
using System.ComponentModel.DataAnnotations ;
2022-09-22 08:56:07 +08:00
using System.IO ;
using System.Linq ;
using System.Reflection ;
using System.Text ;
using System.Threading.Tasks ;
using UnityEditor ;
using UnityEngine ;
2022-10-17 12:16:18 +08:00
using TypeInfo = HybridCLR . Editor . ABI . TypeInfo ;
2024-05-26 11:59:54 +08:00
using CallingConvention = System . Runtime . InteropServices . CallingConvention ;
2022-09-22 08:56:07 +08:00
2022-09-23 09:40:06 +08:00
namespace HybridCLR.Editor.MethodBridge
2022-09-22 08:56:07 +08:00
{
public class Generator
{
public class Options
{
public string TemplateCode { get ; set ; }
public string OutputFile { get ; set ; }
public IReadOnlyCollection < GenericMethod > GenericMethods { get ; set ; }
2024-05-24 18:32:44 +08:00
2024-05-26 11:59:54 +08:00
public List < RawReversePInvokeMethodInfo > ReversePInvokeMethods { get ; set ; }
2024-05-24 18:32:44 +08:00
public bool Development { get ; set ; }
2022-09-22 08:56:07 +08:00
}
2023-06-27 08:57:02 +08:00
private readonly List < GenericMethod > _genericMethods ;
2022-09-22 08:56:07 +08:00
2024-05-26 11:59:54 +08:00
private readonly List < RawReversePInvokeMethodInfo > _originalReversePInvokeMethods ;
2022-09-22 08:56:07 +08:00
private readonly string _templateCode ;
private readonly string _outputFile ;
2024-05-24 18:32:44 +08:00
private readonly bool _development ;
2023-08-22 11:16:31 +08:00
private readonly TypeCreator _typeCreator ;
2022-09-22 08:56:07 +08:00
2022-10-17 12:16:18 +08:00
private readonly HashSet < MethodDesc > _managed2nativeMethodSet = new HashSet < MethodDesc > ( ) ;
2022-09-22 08:56:07 +08:00
2022-10-17 12:16:18 +08:00
private readonly HashSet < MethodDesc > _native2managedMethodSet = new HashSet < MethodDesc > ( ) ;
2022-09-22 08:56:07 +08:00
2022-10-17 12:16:18 +08:00
private readonly HashSet < MethodDesc > _adjustThunkMethodSet = new HashSet < MethodDesc > ( ) ;
2024-05-26 11:59:54 +08:00
private List < ABIReversePInvokeMethodInfo > _reversePInvokeMethods ;
2022-09-22 08:56:07 +08:00
public Generator ( Options options )
{
2023-06-27 08:57:02 +08:00
List < ( GenericMethod , string ) > genericMethodInfo = options . GenericMethods . Select ( m = > ( m , m . ToString ( ) ) ) . ToList ( ) ;
genericMethodInfo . Sort ( ( a , b ) = > string . CompareOrdinal ( a . Item2 , b . Item2 ) ) ;
_genericMethods = genericMethodInfo . Select ( m = > m . Item1 ) . ToList ( ) ;
2024-05-26 11:59:54 +08:00
_originalReversePInvokeMethods = options . ReversePInvokeMethods ;
2023-06-27 08:57:02 +08:00
2022-09-22 08:56:07 +08:00
_templateCode = options . TemplateCode ;
_outputFile = options . OutputFile ;
2023-08-22 11:16:31 +08:00
_typeCreator = new TypeCreator ( ) ;
2024-05-24 18:32:44 +08:00
_development = options . Development ;
2022-09-22 08:56:07 +08:00
}
2023-11-01 20:23:31 +08:00
private readonly Dictionary < string , TypeInfo > _sig2Types = new Dictionary < string , TypeInfo > ( ) ;
private TypeInfo GetSharedTypeInfo ( TypeSig type )
{
var typeInfo = _typeCreator . CreateTypeInfo ( type ) ;
if ( ! typeInfo . IsStruct )
{
return typeInfo ;
}
string sigName = ToFullName ( typeInfo . Klass ) ;
if ( ! _sig2Types . TryGetValue ( sigName , out var sharedTypeInfo ) )
{
sharedTypeInfo = typeInfo ;
_sig2Types . Add ( sigName , sharedTypeInfo ) ;
}
return sharedTypeInfo ;
}
2022-09-22 08:56:07 +08:00
2022-10-17 12:16:18 +08:00
private MethodDesc CreateMethodDesc ( MethodDef methodDef , bool forceRemoveThis , TypeSig returnType , List < TypeSig > parameters )
2022-09-22 08:56:07 +08:00
{
var paramInfos = new List < ParamInfo > ( ) ;
if ( forceRemoveThis & & ! methodDef . IsStatic )
{
parameters . RemoveAt ( 0 ) ;
}
2023-08-22 11:16:31 +08:00
if ( returnType . ContainsGenericParameter )
{
throw new Exception ( $"[PreservedMethod] method:{methodDef} has generic parameters" ) ;
}
2022-09-22 08:56:07 +08:00
foreach ( var paramInfo in parameters )
{
2023-08-22 11:16:31 +08:00
if ( paramInfo . ContainsGenericParameter )
{
throw new Exception ( $"[PreservedMethod] method:{methodDef} has generic parameters" ) ;
}
2023-11-01 20:23:31 +08:00
paramInfos . Add ( new ParamInfo ( ) { Type = GetSharedTypeInfo ( paramInfo ) } ) ;
2022-09-22 08:56:07 +08:00
}
2022-10-17 12:16:18 +08:00
var mbs = new MethodDesc ( )
2022-09-22 08:56:07 +08:00
{
MethodDef = methodDef ,
2023-11-01 20:23:31 +08:00
ReturnInfo = new ReturnInfo ( ) { Type = returnType ! = null ? GetSharedTypeInfo ( returnType ) : TypeInfo . s_void } ,
2022-09-22 08:56:07 +08:00
ParamInfos = paramInfos ,
} ;
return mbs ;
}
2022-10-17 12:16:18 +08:00
private void AddManaged2NativeMethod ( MethodDesc method )
2022-09-22 08:56:07 +08:00
{
2022-10-17 21:38:39 +08:00
method . Init ( ) ;
_managed2nativeMethodSet . Add ( method ) ;
2022-09-22 08:56:07 +08:00
}
2022-10-17 12:16:18 +08:00
private void AddNative2ManagedMethod ( MethodDesc method )
2022-09-22 08:56:07 +08:00
{
2022-10-17 21:38:39 +08:00
method . Init ( ) ;
_native2managedMethodSet . Add ( method ) ;
2022-09-22 08:56:07 +08:00
}
2022-10-17 12:16:18 +08:00
private void AddAdjustThunkMethod ( MethodDesc method )
2022-09-22 08:56:07 +08:00
{
2022-10-17 21:38:39 +08:00
method . Init ( ) ;
_adjustThunkMethodSet . Add ( method ) ;
2022-09-22 08:56:07 +08:00
}
private void ProcessMethod ( MethodDef method , List < TypeSig > klassInst , List < TypeSig > methodInst )
{
if ( method . IsPrivate | | ( method . IsAssembly & & ! method . IsPublic & & ! method . IsFamily ) )
{
2023-08-22 11:16:31 +08:00
if ( klassInst = = null & & methodInst = = null )
2023-06-07 11:47:17 +08:00
{
return ;
}
else
{
//Debug.Log($"[PreservedMethod] method:{method}");
}
2022-09-22 08:56:07 +08:00
}
2023-08-22 11:16:31 +08:00
ICorLibTypes corLibTypes = method . Module . CorLibTypes ;
2022-09-22 08:56:07 +08:00
TypeSig returnType ;
List < TypeSig > parameters ;
if ( klassInst = = null & & methodInst = = null )
{
2023-08-22 11:16:31 +08:00
if ( method . HasGenericParameters )
{
throw new Exception ( $"[PreservedMethod] method:{method} has generic parameters" ) ;
}
returnType = MetaUtil . ToShareTypeSig ( corLibTypes , method . ReturnType ) ;
parameters = method . Parameters . Select ( p = > MetaUtil . ToShareTypeSig ( corLibTypes , p . Type ) ) . ToList ( ) ;
2022-09-22 08:56:07 +08:00
}
else
{
var gc = new GenericArgumentContext ( klassInst , methodInst ) ;
2023-08-22 11:16:31 +08:00
returnType = MetaUtil . ToShareTypeSig ( corLibTypes , MetaUtil . Inflate ( method . ReturnType , gc ) ) ;
parameters = method . Parameters . Select ( p = > MetaUtil . ToShareTypeSig ( corLibTypes , MetaUtil . Inflate ( p . Type , gc ) ) ) . ToList ( ) ;
2022-09-22 08:56:07 +08:00
}
2022-10-17 12:16:18 +08:00
var m2nMethod = CreateMethodDesc ( method , false , returnType , parameters ) ;
2022-09-22 08:56:07 +08:00
AddManaged2NativeMethod ( m2nMethod ) ;
if ( method . IsVirtual )
{
if ( method . DeclaringType . IsInterface )
{
AddAdjustThunkMethod ( m2nMethod ) ;
}
2022-10-17 12:16:18 +08:00
//var adjustThunkMethod = CreateMethodDesc(method, true, returnType, parameters);
2022-09-22 08:56:07 +08:00
AddNative2ManagedMethod ( m2nMethod ) ;
}
if ( method . Name = = "Invoke" & & method . DeclaringType . IsDelegate )
{
2022-10-17 12:16:18 +08:00
var openMethod = CreateMethodDesc ( method , true , returnType , parameters ) ;
2022-09-22 08:56:07 +08:00
AddNative2ManagedMethod ( openMethod ) ;
}
}
2024-05-26 11:59:54 +08:00
private void PrepareGenericMethods ( )
2022-09-22 08:56:07 +08:00
{
foreach ( var method in _genericMethods )
{
ProcessMethod ( method . Method , method . KlassInst , method . MethodInst ) ;
}
}
2023-11-01 20:23:31 +08:00
static void CheckUnique ( IEnumerable < string > names )
{
var set = new HashSet < string > ( ) ;
foreach ( var name in names )
{
if ( ! set . Add ( name ) )
{
throw new Exception ( $"[CheckUnique] duplicate name:{name}" ) ;
}
}
}
2023-12-16 15:17:30 +08:00
private List < MethodDesc > _managed2NativeMethodList0 ;
private List < MethodDesc > _native2ManagedMethodList0 ;
private List < MethodDesc > _adjustThunkMethodList0 ;
private List < TypeInfo > _structTypes0 ;
private void CollectTypesAndMethods ( )
2022-09-22 08:56:07 +08:00
{
2023-12-16 15:17:30 +08:00
_managed2NativeMethodList0 = _managed2nativeMethodSet . ToList ( ) ;
_managed2NativeMethodList0 . Sort ( ( a , b ) = > string . CompareOrdinal ( a . Sig , b . Sig ) ) ;
2022-09-22 08:56:07 +08:00
2023-12-16 15:17:30 +08:00
_native2ManagedMethodList0 = _native2managedMethodSet . ToList ( ) ;
_native2ManagedMethodList0 . Sort ( ( a , b ) = > string . CompareOrdinal ( a . Sig , b . Sig ) ) ;
_adjustThunkMethodList0 = _adjustThunkMethodSet . ToList ( ) ;
_adjustThunkMethodList0 . Sort ( ( a , b ) = > string . CompareOrdinal ( a . Sig , b . Sig ) ) ;
var structTypeSet = new HashSet < TypeInfo > ( ) ;
CollectStructDefs ( _managed2NativeMethodList0 , structTypeSet ) ;
CollectStructDefs ( _native2ManagedMethodList0 , structTypeSet ) ;
CollectStructDefs ( _adjustThunkMethodList0 , structTypeSet ) ;
_structTypes0 = structTypeSet . ToList ( ) ;
_structTypes0 . Sort ( ( a , b ) = > a . TypeId - b . TypeId ) ;
CheckUnique ( _structTypes0 . Select ( t = > ToFullName ( t . Klass ) ) ) ;
CheckUnique ( _structTypes0 . Select ( t = > t . CreateSigName ( ) ) ) ;
Debug . LogFormat ( "== before optimization struct:{3} managed2native:{0} native2managed:{1} adjustThunk:{2}" ,
_managed2NativeMethodList0 . Count , _native2ManagedMethodList0 . Count , _adjustThunkMethodList0 . Count , _structTypes0 . Count ) ;
}
private class AnalyzeTypeInfo
{
public TypeInfo toSharedType ;
public List < TypeInfo > fields ;
public string signature ;
}
private readonly Dictionary < TypeInfo , AnalyzeTypeInfo > _analyzeTypeInfos = new Dictionary < TypeInfo , AnalyzeTypeInfo > ( ) ;
private readonly Dictionary < string , TypeInfo > _signature2Type = new Dictionary < string , TypeInfo > ( ) ;
private AnalyzeTypeInfo CalculateAnalyzeTypeInfoBasic ( TypeInfo typeInfo )
{
TypeSig type = typeInfo . Klass ;
TypeDef typeDef = type . ToTypeDefOrRef ( ) . ResolveTypeDefThrow ( ) ;
List < TypeSig > klassInst = type . ToGenericInstSig ( ) ? . GenericArguments ? . ToList ( ) ;
GenericArgumentContext ctx = klassInst ! = null ? new GenericArgumentContext ( klassInst , null ) : null ;
ClassLayout sa = typeDef . ClassLayout ;
var analyzeTypeInfo = new AnalyzeTypeInfo ( ) ;
// don't share type with explicit layout
if ( sa ! = null )
{
analyzeTypeInfo . toSharedType = typeInfo ;
analyzeTypeInfo . signature = typeInfo . CreateSigName ( ) ;
_signature2Type . Add ( analyzeTypeInfo . signature , typeInfo ) ;
return analyzeTypeInfo ;
}
var fields = analyzeTypeInfo . fields = new List < TypeInfo > ( ) ;
2022-09-22 08:56:07 +08:00
2023-12-16 15:17:30 +08:00
foreach ( FieldDef field in typeDef . Fields )
{
if ( field . IsStatic )
{
continue ;
}
TypeSig fieldType = ctx ! = null ? MetaUtil . Inflate ( field . FieldType , ctx ) : field . FieldType ;
fields . Add ( GetSharedTypeInfo ( fieldType ) ) ;
}
return analyzeTypeInfo ;
}
private string GetOrCalculateTypeInfoSignature ( TypeInfo typeInfo )
{
if ( ! typeInfo . IsStruct )
{
return typeInfo . CreateSigName ( ) ;
}
2023-06-27 08:57:02 +08:00
2023-12-16 15:17:30 +08:00
var ati = _analyzeTypeInfos [ typeInfo ] ;
2023-06-27 08:57:02 +08:00
2023-12-16 15:17:30 +08:00
//if (_analyzeTypeInfos.TryGetValue(typeInfo, out var ati))
//{
// return ati.signature;
//}
//ati = CalculateAnalyzeTypeInfoBasic(typeInfo);
//_analyzeTypeInfos.Add(typeInfo, ati);
if ( ati . signature ! = null )
{
return ati . signature ;
}
2023-06-27 08:57:02 +08:00
2023-12-16 15:17:30 +08:00
var sigBuf = new StringBuilder ( ) ;
foreach ( var field in ati . fields )
{
2024-04-23 11:42:22 +08:00
sigBuf . Append ( "{" + GetOrCalculateTypeInfoSignature ( ToIsomorphicType ( field ) ) + "}" ) ;
2023-12-16 15:17:30 +08:00
}
return ati . signature = sigBuf . ToString ( ) ;
}
2022-09-22 08:56:07 +08:00
2023-12-16 15:17:30 +08:00
private TypeInfo ToIsomorphicType ( TypeInfo type )
{
if ( ! type . IsStruct )
{
return type ;
}
if ( ! _analyzeTypeInfos . TryGetValue ( type , out var ati ) )
{
ati = CalculateAnalyzeTypeInfoBasic ( type ) ;
_analyzeTypeInfos . Add ( type , ati ) ;
}
if ( ati . toSharedType = = null )
{
string signature = GetOrCalculateTypeInfoSignature ( type ) ;
Debug . Assert ( signature = = ati . signature ) ;
if ( _signature2Type . TryGetValue ( signature , out var sharedType ) )
{
// Debug.Log($"[ToIsomorphicType] type:{type.Klass} ==> sharedType:{sharedType.Klass} signature:{signature} ");
ati . toSharedType = sharedType ;
}
else
{
ati . toSharedType = type ;
_signature2Type . Add ( signature , type ) ;
}
}
return ati . toSharedType ;
}
2023-08-22 11:16:31 +08:00
2023-12-16 15:17:30 +08:00
private MethodDesc ToIsomorphicMethod ( MethodDesc method )
{
var paramInfos = new List < ParamInfo > ( ) ;
foreach ( var paramInfo in method . ParamInfos )
{
paramInfos . Add ( new ParamInfo ( ) { Type = ToIsomorphicType ( paramInfo . Type ) } ) ;
}
var mbs = new MethodDesc ( )
{
MethodDef = method . MethodDef ,
ReturnInfo = new ReturnInfo ( ) { Type = ToIsomorphicType ( method . ReturnInfo . Type ) } ,
ParamInfos = paramInfos ,
} ;
mbs . Init ( ) ;
return mbs ;
}
private List < MethodDesc > _managed2NativeMethodList ;
private List < MethodDesc > _native2ManagedMethodList ;
private List < MethodDesc > _adjustThunkMethodList ;
private List < TypeInfo > structTypes ;
private void BuildAnalyzeTypeInfos ( )
{
foreach ( var type in _structTypes0 )
{
ToIsomorphicType ( type ) ;
}
structTypes = _signature2Type . Values . ToList ( ) ;
2023-08-22 11:16:31 +08:00
structTypes . Sort ( ( a , b ) = > a . TypeId - b . TypeId ) ;
2023-12-16 15:17:30 +08:00
}
private List < MethodDesc > ToUniqueOrderedList ( List < MethodDesc > methods )
{
var methodMap = new SortedDictionary < string , MethodDesc > ( ) ;
foreach ( var method in methods )
{
var sharedMethod = ToIsomorphicMethod ( method ) ;
var sig = sharedMethod . Sig ;
if ( ! methodMap . TryGetValue ( sig , out var _ ) )
{
methodMap . Add ( sig , sharedMethod ) ;
}
}
return methodMap . Values . ToList ( ) ;
}
2024-05-26 11:59:54 +08:00
private static string MakeSignature ( MethodDesc desc , CallingConvention CallingConventionention )
{
string convStr = ( ( char ) ( 'A' + ( int ) CallingConventionention - 1 ) ) . ToString ( ) ;
return $"{convStr}{desc.Sig}" ;
}
private static CallingConvention GetCallingConvention ( MethodDef method )
{
var monoPInvokeCallbackAttr = method . CustomAttributes . FirstOrDefault ( ca = > ca . AttributeType . Name = = "MonoPInvokeCallbackAttribute" ) ;
if ( monoPInvokeCallbackAttr = = null )
{
return CallingConvention . Winapi ;
}
object delegateTypeSig = monoPInvokeCallbackAttr . ConstructorArguments [ 0 ] . Value ;
TypeDef delegateTypeDef ;
if ( delegateTypeSig is ClassSig classSig )
{
2024-05-30 12:10:52 +08:00
delegateTypeDef = classSig . TypeDefOrRef . ResolveTypeDefThrow ( ) ;
2024-05-26 11:59:54 +08:00
}
else if ( delegateTypeSig is GenericInstSig genericInstSig )
{
delegateTypeDef = genericInstSig . GenericType . TypeDefOrRef . ResolveTypeDefThrow ( ) ;
}
else
{
2024-05-30 12:10:52 +08:00
delegateTypeDef = null ;
2024-05-26 11:59:54 +08:00
}
if ( delegateTypeDef = = null )
{
2024-05-30 12:10:52 +08:00
throw new NotSupportedException ( $"Unsupported delegate type: {delegateTypeSig}" ) ;
2024-05-26 11:59:54 +08:00
}
var attr = delegateTypeDef . CustomAttributes . FirstOrDefault ( ca = > ca . AttributeType . FullName = = "System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute" ) ;
if ( attr = = null )
{
return CallingConvention . Winapi ;
}
var conv = attr . ConstructorArguments [ 0 ] . Value ;
return ( CallingConvention ) conv ;
}
private List < ABIReversePInvokeMethodInfo > BuildABIMethods ( List < RawReversePInvokeMethodInfo > rawMethods )
{
var methodsBySig = new Dictionary < string , ABIReversePInvokeMethodInfo > ( ) ;
foreach ( var method in rawMethods )
{
var sharedMethod = new MethodDesc
{
MethodDef = method . Method ,
ReturnInfo = new ReturnInfo { Type = _typeCreator . CreateTypeInfo ( method . Method . ReturnType ) } ,
ParamInfos = method . Method . Parameters . Select ( p = > new ParamInfo { Type = _typeCreator . CreateTypeInfo ( p . Type ) } ) . ToList ( ) ,
} ;
sharedMethod . Init ( ) ;
sharedMethod = ToIsomorphicMethod ( sharedMethod ) ;
CallingConvention callingConv = GetCallingConvention ( method . Method ) ;
string signature = MakeSignature ( sharedMethod , callingConv ) ;
if ( ! methodsBySig . TryGetValue ( signature , out var arm ) )
{
arm = new ABIReversePInvokeMethodInfo ( )
{
Method = sharedMethod ,
Signature = signature ,
Count = 0 ,
Callvention = callingConv ,
} ;
methodsBySig . Add ( signature , arm ) ;
}
int preserveCount = method . GenerationAttribute ! = null ? ( int ) method . GenerationAttribute . ConstructorArguments [ 0 ] . Value : 1 ;
arm . Count + = preserveCount ;
}
var newMethods = methodsBySig . Values . ToList ( ) ;
newMethods . Sort ( ( a , b ) = > string . CompareOrdinal ( a . Signature , b . Signature ) ) ;
return newMethods ;
}
2023-12-16 15:17:30 +08:00
private void BuildOptimizedMethods ( )
{
_managed2NativeMethodList = ToUniqueOrderedList ( _managed2NativeMethodList0 ) ;
_native2ManagedMethodList = ToUniqueOrderedList ( _native2ManagedMethodList0 ) ;
_adjustThunkMethodList = ToUniqueOrderedList ( _adjustThunkMethodList0 ) ;
2024-05-26 11:59:54 +08:00
_reversePInvokeMethods = BuildABIMethods ( _originalReversePInvokeMethods ) ;
2023-12-16 15:17:30 +08:00
}
private void OptimizationTypesAndMethods ( )
{
BuildAnalyzeTypeInfos ( ) ;
BuildOptimizedMethods ( ) ;
Debug . LogFormat ( "== after optimization struct:{3} managed2native:{0} native2managed:{1} adjustThunk:{2}" ,
_managed2NativeMethodList . Count , _native2ManagedMethodList . Count , _adjustThunkMethodList . Count , structTypes . Count ) ;
}
private void GenerateCode ( )
{
var frr = new FileRegionReplace ( _templateCode ) ;
2024-05-26 11:59:54 +08:00
List < string > lines = new List < string > ( 20_0000 )
{
"\n" ,
$"// DEVELOPMENT={(_development ? 1 : 0)}" ,
"\n"
} ;
2024-05-24 18:32:44 +08:00
2023-08-22 11:16:31 +08:00
var classInfos = new List < ClassInfo > ( ) ;
var classTypeSet = new HashSet < TypeInfo > ( ) ;
foreach ( var type in structTypes )
{
GenerateClassInfo ( type , classTypeSet , classInfos ) ;
}
GenerateStructDefines ( classInfos , lines ) ;
2023-12-16 15:17:30 +08:00
// use structTypes0 to generate signature
GenerateStructureSignatureStub ( _structTypes0 , lines ) ;
foreach ( var method in _managed2NativeMethodList )
2022-09-22 08:56:07 +08:00
{
2023-08-22 11:16:31 +08:00
GenerateManaged2NativeMethod ( method , lines ) ;
2022-09-22 08:56:07 +08:00
}
2023-12-16 15:17:30 +08:00
GenerateManaged2NativeStub ( _managed2NativeMethodList , lines ) ;
2022-09-22 08:56:07 +08:00
2023-12-16 15:17:30 +08:00
foreach ( var method in _native2ManagedMethodList )
2022-09-22 08:56:07 +08:00
{
2023-08-22 11:16:31 +08:00
GenerateNative2ManagedMethod ( method , lines ) ;
2022-09-22 08:56:07 +08:00
}
2023-12-16 15:17:30 +08:00
GenerateNative2ManagedStub ( _native2ManagedMethodList , lines ) ;
2022-09-22 08:56:07 +08:00
2023-12-16 15:17:30 +08:00
foreach ( var method in _adjustThunkMethodList )
2022-09-22 08:56:07 +08:00
{
2023-08-22 11:16:31 +08:00
GenerateAdjustThunkMethod ( method , lines ) ;
2022-09-22 08:56:07 +08:00
}
2023-12-16 15:17:30 +08:00
GenerateAdjustThunkStub ( _adjustThunkMethodList , lines ) ;
2022-09-22 08:56:07 +08:00
2024-05-26 11:59:54 +08:00
GenerateReversePInvokeWrappers ( _reversePInvokeMethods , lines ) ;
2022-10-17 21:38:39 +08:00
frr . Replace ( "CODE" , string . Join ( "\n" , lines ) ) ;
2022-09-22 08:56:07 +08:00
Directory . CreateDirectory ( Path . GetDirectoryName ( _outputFile ) ) ;
frr . Commit ( _outputFile ) ;
}
2024-05-26 11:59:54 +08:00
private static string GetIl2cppCallConventionName ( CallingConvention conv )
{
switch ( conv )
{
case 0 :
case CallingConvention . Winapi :
return "DEFAULT_CALL" ;
case CallingConvention . Cdecl :
return "CDECL" ;
case CallingConvention . StdCall :
return "STDCALL" ;
case CallingConvention . ThisCall :
return "THISCALL" ;
case CallingConvention . FastCall :
return "FASTCALL" ;
default :
throw new NotSupportedException ( $"Unsupported CallingConvention {conv}" ) ;
}
}
private void GenerateReversePInvokeWrappers ( List < ABIReversePInvokeMethodInfo > methods , List < string > lines )
{
int methodIndex = 0 ;
var stubCodes = new List < string > ( ) ;
foreach ( var methodInfo in methods )
{
MethodDesc method = methodInfo . Method ;
string il2cppCallConventionName = GetIl2cppCallConventionName ( methodInfo . Callvention ) ;
string paramDeclaringListWithoutMethodInfoStr = string . Join ( ", " , method . ParamInfos . Select ( p = > $"{p.Type.GetTypeName()} __arg{p.Index}" ) ) ;
string paramNameListWithoutMethodInfoStr = string . Join ( ", " , method . ParamInfos . Select ( p = > $"__arg{p.Index}" ) . Concat ( new string [ ] { "method" } ) ) ;
string paramTypeListWithMethodInfoStr = string . Join ( ", " , method . ParamInfos . Select ( p = > $"{p.Type.GetTypeName()}" ) . Concat ( new string [ ] { "const MethodInfo*" } ) ) ;
string methodTypeDef = $"typedef {method.ReturnInfo.Type.GetTypeName()} (*Callback)({paramTypeListWithMethodInfoStr})" ;
for ( int i = 0 ; i < methodInfo . Count ; i + + , methodIndex + + )
{
lines . Add ( $ @ "
{ method . ReturnInfo . Type . GetTypeName ( ) } { il2cppCallConventionName } __ReversePInvokeMethod_ { methodIndex } ( { paramDeclaringListWithoutMethodInfoStr } )
{ {
il2cpp : : vm : : ScopedThreadAttacher _vmThreadHelper ;
const MethodInfo * method = InterpreterModule : : GetMethodInfoByReversePInvokeWrapperIndex ( { methodIndex } ) ;
{ methodTypeDef } ;
{ ( method . ReturnInfo . IsVoid ? "" : "return " ) } ( ( Callback ) ( method - > methodPointerCallByInterp ) ) ( { paramNameListWithoutMethodInfoStr } ) ;
} }
");
stubCodes . Add ( $"\t{{\" { methodInfo . Signature } \ ", (Il2CppMethodPointer)__ReversePInvokeMethod_{methodIndex}}}," ) ;
}
Debug . Log ( $"[ReversePInvokeWrap.Generator] method:{method.MethodDef} wrapperCount:{methodInfo.Count}" ) ;
}
lines . Add ( @ "
const ReversePInvokeMethodData hybridclr : : interpreter : : g_reversePInvokeMethodStub [ ]
{
");
lines . AddRange ( stubCodes ) ;
lines . Add ( @ "
{ nullptr , nullptr } ,
} ;
");
}
2023-12-16 15:17:30 +08:00
public void Generate ( )
{
2024-05-26 11:59:54 +08:00
PrepareGenericMethods ( ) ;
2023-12-16 15:17:30 +08:00
CollectTypesAndMethods ( ) ;
OptimizationTypesAndMethods ( ) ;
GenerateCode ( ) ;
}
2023-08-22 11:16:31 +08:00
private void CollectStructDefs ( List < MethodDesc > methods , HashSet < TypeInfo > structTypes )
{
foreach ( var method in methods )
{
foreach ( var paramInfo in method . ParamInfos )
{
if ( paramInfo . Type . IsStruct )
{
structTypes . Add ( paramInfo . Type ) ;
if ( paramInfo . Type . Klass . ContainsGenericParameter )
{
throw new Exception ( $"[CollectStructDefs] method:{method.MethodDef} type:{paramInfo.Type.Klass} contains generic parameter" ) ;
}
}
}
if ( method . ReturnInfo . Type . IsStruct )
{
structTypes . Add ( method . ReturnInfo . Type ) ;
if ( method . ReturnInfo . Type . Klass . ContainsGenericParameter )
{
throw new Exception ( $"[CollectStructDefs] method:{method.MethodDef} type:{method.ReturnInfo.Type.Klass} contains generic parameter" ) ;
}
}
}
}
class FieldInfo
{
public FieldDef field ;
public TypeInfo type ;
}
class ClassInfo
{
public TypeInfo type ;
public TypeDef typeDef ;
public List < FieldInfo > fields = new List < FieldInfo > ( ) ;
public ClassLayout layout ;
}
private void GenerateClassInfo ( TypeInfo type , HashSet < TypeInfo > typeSet , List < ClassInfo > classInfos )
{
if ( ! typeSet . Add ( type ) )
{
return ;
}
TypeSig typeSig = type . Klass ;
var fields = new List < FieldInfo > ( ) ;
TypeDef typeDef = typeSig . ToTypeDefOrRef ( ) . ResolveTypeDefThrow ( ) ;
List < TypeSig > klassInst = typeSig . ToGenericInstSig ( ) ? . GenericArguments ? . ToList ( ) ;
GenericArgumentContext ctx = klassInst ! = null ? new GenericArgumentContext ( klassInst , null ) : null ;
ClassLayout sa = typeDef . ClassLayout ;
ICorLibTypes corLibTypes = typeDef . Module . CorLibTypes ;
foreach ( FieldDef field in typeDef . Fields )
{
if ( field . IsStatic )
{
continue ;
}
TypeSig fieldType = ctx ! = null ? MetaUtil . Inflate ( field . FieldType , ctx ) : field . FieldType ;
fieldType = MetaUtil . ToShareTypeSig ( corLibTypes , fieldType ) ;
2023-12-16 15:17:30 +08:00
var fieldTypeInfo = ToIsomorphicType ( _typeCreator . CreateTypeInfo ( fieldType ) ) ;
2023-08-22 11:16:31 +08:00
if ( fieldTypeInfo . IsStruct )
{
GenerateClassInfo ( fieldTypeInfo , typeSet , classInfos ) ;
}
fields . Add ( new FieldInfo { field = field , type = fieldTypeInfo } ) ;
}
classInfos . Add ( new ClassInfo ( ) { type = type , typeDef = typeDef , fields = fields , layout = sa } ) ;
}
private void GenerateStructDefines ( List < ClassInfo > classInfos , List < string > lines )
{
foreach ( var ci in classInfos )
{
lines . Add ( $"// {ci.type.Klass}" ) ;
uint packingSize = ci . layout ? . PackingSize ? ? 0 ;
if ( packingSize ! = 0 )
{
lines . Add ( $"#pragma pack(push, {packingSize})" ) ;
}
uint classSize = ci . layout ? . ClassSize ? ? 0 ;
if ( ci . typeDef . IsExplicitLayout )
{
lines . Add ( $"union {ci.type.GetTypeName()} {{" ) ;
if ( classSize > 0 )
{
lines . Add ( $"\tstruct {{ char __fieldSize_offsetPadding[{classSize}];}};" ) ;
}
int index = 0 ;
foreach ( var field in ci . fields )
{
uint offset = field . field . FieldOffset . Value ;
2023-11-24 09:59:22 +08:00
string fieldName = $"__{index}" ;
string commentFieldName = $"{field.field.Name}" ;
2023-08-22 11:16:31 +08:00
lines . Add ( "\t#pragma pack(push, 1)" ) ;
2023-11-24 09:59:22 +08:00
lines . Add ( $"\tstruct {{ {(offset > 0 ? $" char { fieldName } _offsetPadding [ { offset } ] ; " : " ")} {field.type.GetTypeName()} {fieldName};}}; // {commentFieldName}" ) ;
2023-08-22 11:16:31 +08:00
lines . Add ( $"\t#pragma pack(pop)" ) ;
2023-11-24 09:59:22 +08:00
lines . Add ( $"\tstruct {{ {field.type.GetTypeName()} {fieldName}_forAlignmentOnly;}}; // {commentFieldName}" ) ;
2023-08-22 11:16:31 +08:00
+ + index ;
}
}
else
{
lines . Add ( $"{(classSize > 0 ? " union " : " struct ")} {ci.type.GetTypeName()} {{" ) ;
if ( classSize > 0 )
{
lines . Add ( $"\tstruct {{ char __fieldSize_offsetPadding[{classSize}];}};" ) ;
lines . Add ( "\tstruct {" ) ;
}
int index = 0 ;
foreach ( var field in ci . fields )
{
2023-11-24 09:59:22 +08:00
string fieldName = $"__{index}" ;
string commentFieldName = $"{field.field.Name}" ;
lines . Add ( $"\t{field.type.GetTypeName()} {fieldName}; // {commentFieldName}" ) ;
2023-08-22 11:16:31 +08:00
+ + index ;
}
if ( classSize > 0 )
{
lines . Add ( "\t};" ) ;
}
}
lines . Add ( "};" ) ;
if ( packingSize ! = 0 )
{
lines . Add ( $"#pragma pack(pop)" ) ;
}
}
}
2023-08-31 11:06:06 +08:00
public const string SigOfObj = "u" ;
2023-08-22 11:16:31 +08:00
public static string ToFullName ( TypeSig type )
{
type = type . RemovePinnedAndModifiers ( ) ;
switch ( type . ElementType )
{
case ElementType . Void : return "v" ;
case ElementType . Boolean : return "u1" ;
case ElementType . I1 : return "i1" ;
case ElementType . U1 : return "u1" ;
case ElementType . I2 : return "i2" ;
case ElementType . Char :
2023-08-31 11:06:06 +08:00
case ElementType . U2 : return "u2" ;
2023-08-22 11:16:31 +08:00
case ElementType . I4 : return "i4" ;
case ElementType . U4 : return "u4" ;
case ElementType . I8 : return "i8" ;
case ElementType . U8 : return "u8" ;
case ElementType . R4 : return "r4" ;
case ElementType . R8 : return "r8" ;
2023-08-31 11:06:06 +08:00
case ElementType . I : return "i" ;
case ElementType . U :
2023-08-22 11:16:31 +08:00
case ElementType . String :
case ElementType . Ptr :
case ElementType . ByRef :
case ElementType . Class :
case ElementType . Array :
case ElementType . SZArray :
case ElementType . FnPtr :
case ElementType . Object :
2023-08-31 11:06:06 +08:00
return SigOfObj ;
2023-08-22 11:16:31 +08:00
case ElementType . Module :
case ElementType . Var :
case ElementType . MVar :
throw new NotSupportedException ( $"ToFullName type:{type}" ) ;
case ElementType . TypedByRef : return TypeInfo . strTypedByRef ;
case ElementType . ValueType :
{
TypeDef typeDef = type . ToTypeDefOrRef ( ) . ResolveTypeDef ( ) ;
if ( typeDef = = null )
{
2023-08-30 11:07:12 +08:00
throw new Exception ( $"type:{type} definition could not be found. Please try `HybridCLR/Genergate/LinkXml`, then Build once to generate the AOT dll, and then regenerate the bridge function" ) ;
2023-08-22 11:16:31 +08:00
}
if ( typeDef . IsEnum )
{
return ToFullName ( typeDef . GetEnumUnderlyingType ( ) ) ;
}
return ToValueTypeFullName ( ( ClassOrValueTypeSig ) type ) ;
}
case ElementType . GenericInst :
{
GenericInstSig gis = ( GenericInstSig ) type ;
if ( ! gis . GenericType . IsValueType )
{
2023-08-31 11:06:06 +08:00
return SigOfObj ;
2023-08-22 11:16:31 +08:00
}
TypeDef typeDef = gis . GenericType . ToTypeDefOrRef ( ) . ResolveTypeDef ( ) ;
if ( typeDef . IsEnum )
{
return ToFullName ( typeDef . GetEnumUnderlyingType ( ) ) ;
}
return $"{ToValueTypeFullName(gis.GenericType)}<{string.Join(" , ", gis.GenericArguments.Select(a => ToFullName(a)))}>" ;
}
default : throw new NotSupportedException ( $"{type.ElementType}" ) ;
}
}
private static bool IsSystemOrUnityAssembly ( ModuleDef module )
{
if ( module . IsCoreLibraryModule = = true )
{
return true ;
}
string assName = module . Assembly . Name . String ;
return assName . StartsWith ( "System." ) | | assName . StartsWith ( "UnityEngine." ) ;
}
private static string ToValueTypeFullName ( ClassOrValueTypeSig type )
{
TypeDef typeDef = type . ToTypeDefOrRef ( ) . ResolveTypeDef ( ) ;
if ( typeDef = = null )
{
throw new Exception ( $"type:{type} resolve fail" ) ;
}
if ( typeDef . DeclaringType ! = null )
{
return $"{ToValueTypeFullName((ClassOrValueTypeSig)typeDef.DeclaringType.ToTypeSig())}/{typeDef.Name}" ;
}
if ( IsSystemOrUnityAssembly ( typeDef . Module ) )
{
return type . FullName ;
}
return $"{Path.GetFileNameWithoutExtension(typeDef.Module.Name)}:{typeDef.FullName}" ;
}
public void GenerateStructureSignatureStub ( List < TypeInfo > types , List < string > lines )
{
2024-05-26 11:59:54 +08:00
lines . Add ( "const FullName2Signature hybridclr::interpreter::g_fullName2SignatureStub[] = {" ) ;
2023-08-22 11:16:31 +08:00
foreach ( var type in types )
{
2023-12-16 15:17:30 +08:00
TypeInfo isoType = ToIsomorphicType ( type ) ;
lines . Add ( $"\t{{\" { ToFullName ( type . Klass ) } \ ", \"{isoType.CreateSigName()}\"}}," ) ;
2023-08-22 11:16:31 +08:00
}
lines . Add ( "\t{ nullptr, nullptr}," ) ;
lines . Add ( "};" ) ;
}
public void GenerateManaged2NativeStub ( List < MethodDesc > methods , List < string > lines )
{
lines . Add ( $ @ "
2024-05-26 11:59:54 +08:00
const Managed2NativeMethodInfo hybridclr : : interpreter : : g_managed2nativeStub [ ] =
2023-08-22 11:16:31 +08:00
{ {
");
foreach ( var method in methods )
{
lines . Add ( $"\t{{\" { method . CreateInvokeSigName ( ) } \ ", __M2N_{method.CreateInvokeSigName()}}}," ) ;
}
lines . Add ( $"\t{{nullptr, nullptr}}," ) ;
lines . Add ( "};" ) ;
}
public void GenerateNative2ManagedStub ( List < MethodDesc > methods , List < string > lines )
{
lines . Add ( $ @ "
2024-05-26 11:59:54 +08:00
const Native2ManagedMethodInfo hybridclr : : interpreter : : g_native2managedStub [ ] =
2023-08-22 11:16:31 +08:00
{ {
");
foreach ( var method in methods )
{
lines . Add ( $"\t{{\" { method . CreateInvokeSigName ( ) } \ ", (Il2CppMethodPointer)__N2M_{method.CreateInvokeSigName()}}}," ) ;
}
lines . Add ( $"\t{{nullptr, nullptr}}," ) ;
lines . Add ( "};" ) ;
}
public void GenerateAdjustThunkStub ( List < MethodDesc > methods , List < string > lines )
{
lines . Add ( $ @ "
2024-05-26 11:59:54 +08:00
const NativeAdjustThunkMethodInfo hybridclr : : interpreter : : g_adjustThunkStub [ ] =
2023-08-22 11:16:31 +08:00
{ {
");
foreach ( var method in methods )
{
lines . Add ( $"\t{{\" { method . CreateInvokeSigName ( ) } \ ", (Il2CppMethodPointer)__N2M_AdjustorThunk_{method.CreateCallSigName()}}}," ) ;
}
lines . Add ( $"\t{{nullptr, nullptr}}," ) ;
lines . Add ( "};" ) ;
}
private string GetManaged2NativePassParam ( TypeInfo type , string varName )
{
return $"M2NFromValueOrAddress<{type.GetTypeName()}>({varName})" ;
}
private string GetNative2ManagedPassParam ( TypeInfo type , string varName )
{
return type . NeedExpandValue ( ) ? $"(uint64_t)({varName})" : $"N2MAsUint64ValueOrAddress<{type.GetTypeName()}>({varName})" ;
}
public void GenerateManaged2NativeMethod ( MethodDesc method , List < string > lines )
{
string paramListStr = string . Join ( ", " , method . ParamInfos . Select ( p = > $"{p.Type.GetTypeName()} __arg{p.Index}" ) . Concat ( new string [ ] { "const MethodInfo* method" } ) ) ;
string paramNameListStr = string . Join ( ", " , method . ParamInfos . Select ( p = > GetManaged2NativePassParam ( p . Type , $"localVarBase+argVarIndexs[{p.Index}]" ) ) . Concat ( new string [ ] { "method" } ) ) ;
lines . Add ( $ @ "
static void __M2N_ { method . CreateCallSigName ( ) } ( const MethodInfo * method , uint16_t * argVarIndexs , StackObject * localVarBase , void * ret )
{ {
typedef { method . ReturnInfo . Type . GetTypeName ( ) } ( * NativeMethod ) ( { paramListStr } ) ;
{ ( ! method . ReturnInfo . IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "" ) } ( ( NativeMethod ) ( method - > methodPointerCallByInterp ) ) ( { paramNameListStr } ) ;
} }
");
}
2023-08-24 19:26:50 +08:00
public string GenerateArgumentSizeAndOffset ( List < ParamInfo > paramInfos )
{
StringBuilder s = new StringBuilder ( ) ;
int index = 0 ;
foreach ( var param in paramInfos )
{
s . AppendLine ( $"\tconstexpr int __ARG_OFFSET_{index}__ = {(index > 0 ? $" __ARG_OFFSET_ { index - 1 } __ + __ARG_SIZE_ { index - 1 } __ " : " 0 ")};" ) ;
s . AppendLine ( $"\tconstexpr int __ARG_SIZE_{index}__ = (sizeof(__arg{index}) + 7)/8;" ) ;
index + + ;
}
s . AppendLine ( $"\tconstexpr int __TOTAL_ARG_SIZE__ = {(paramInfos.Count > 0 ? $" __ARG_OFFSET_ { index - 1 } __ + __ARG_SIZE_ { index - 1 } __ " : " 1 ")};" ) ;
return s . ToString ( ) ;
}
2023-08-31 19:22:07 +08:00
public string GenerateCopyArgumentToInterpreterStack ( List < ParamInfo > paramInfos )
2023-08-24 19:26:50 +08:00
{
StringBuilder s = new StringBuilder ( ) ;
int index = 0 ;
foreach ( var param in paramInfos )
{
if ( param . Type . IsPrimitiveType )
{
if ( param . Type . NeedExpandValue ( ) )
{
2023-08-31 19:22:07 +08:00
s . AppendLine ( $"\targs[__ARG_OFFSET_{index}__].u64 = __arg{index};" ) ;
2023-08-24 19:26:50 +08:00
}
else
{
2023-08-31 19:22:07 +08:00
s . AppendLine ( $"\t*({param.Type.GetTypeName()}*)(args + __ARG_OFFSET_{index}__) = __arg{index};" ) ;
2023-08-24 19:26:50 +08:00
}
}
else
{
s . AppendLine ( $"\t*({param.Type.GetTypeName()}*)(args + __ARG_OFFSET_{index}__) = __arg{index};" ) ;
}
index + + ;
}
return s . ToString ( ) ;
}
2023-08-31 19:22:07 +08:00
private void GenerateNative2ManagedMethod0 ( MethodDesc method , bool adjustorThunk , List < string > lines )
2023-08-22 11:16:31 +08:00
{
string paramListStr = string . Join ( ", " , method . ParamInfos . Select ( p = > $"{p.Type.GetTypeName()} __arg{p.Index}" ) . Concat ( new string [ ] { "const MethodInfo* method" } ) ) ;
lines . Add ( $ @ "
2023-08-31 19:22:07 +08:00
static { method . ReturnInfo . Type . GetTypeName ( ) } __N2M_ { ( adjustorThunk ? "AdjustorThunk_" : "" ) } { method . CreateCallSigName ( ) } ( { paramListStr } )
2023-08-22 11:16:31 +08:00
{ {
2023-08-31 19:22:07 +08:00
{ ( adjustorThunk ? "__arg0 += sizeof(Il2CppObject);" : "" ) }
2023-08-24 19:26:50 +08:00
{ GenerateArgumentSizeAndOffset ( method . ParamInfos ) }
StackObject args [ __TOTAL_ARG_SIZE__ ] ;
2023-08-31 19:22:07 +08:00
{ GenerateCopyArgumentToInterpreterStack ( method . ParamInfos ) }
2023-08-22 11:16:31 +08:00
{ ( method . ReturnInfo . IsVoid ? "Interpreter::Execute(method, args, nullptr);" : $"{method.ReturnInfo.Type.GetTypeName()} ret; Interpreter::Execute(method, args, &ret); return ret;" ) }
} }
");
}
2023-08-31 19:22:07 +08:00
public void GenerateNative2ManagedMethod ( MethodDesc method , List < string > lines )
2023-08-22 11:16:31 +08:00
{
2023-08-31 19:22:07 +08:00
GenerateNative2ManagedMethod0 ( method , false , lines ) ;
}
2023-08-22 11:16:31 +08:00
2023-08-31 19:22:07 +08:00
public void GenerateAdjustThunkMethod ( MethodDesc method , List < string > lines )
{
GenerateNative2ManagedMethod0 ( method , true , lines ) ;
2023-08-22 11:16:31 +08:00
}
2022-09-22 08:56:07 +08:00
}
}