@ -1655,18 +1655,26 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat
@@ -1655,18 +1655,26 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat
if ( wrappedEntries . Count = = 0 )
return ;
bool generateNativeToManaged = Options . GenerateNativeToManagedFor ( @class ) ;
PushBlock ( BlockKind . Region ) ;
WriteLine ( "#region Virtual table interop" ) ;
NewLine ( ) ;
bool hasDynamicBase = @class . NeedsBase & & @class . BaseClass . IsDynamic ;
var originalTableClass = @class . IsDependent ? @class . Specializations [ 0 ] : @class ;
// vtable hooks don't work without a NativeToManaged map, because we can't look up the managed
// instance from a native pointer without the map, so don't generate them here.
// this also means we can't inherit from this class and override virtual methods in C#
if ( generateNativeToManaged )
{
// Generate a delegate type for each method.
foreach ( var method in wrappedEntries . Select ( e = > e . Method ) . Where ( m = > ! m . Ignore ) )
GenerateVTableMethodDelegates ( containingClass , method . Namespace . IsDependent ?
( Method ) method . InstantiatedFrom : method ) ;
var hasVirtualDtor = wrappedEntries . Any ( e = > e . Method . IsDestructor ) ;
bool hasDynamicBase = @class . NeedsBase & & @class . BaseClass . IsDynamic ;
var originalTableClass = @class . IsDependent ? @class . Specializations [ 0 ] : @class ;
var destructorOnly = "destructorOnly" ;
using ( WriteBlock ( $"internal static{(hasDynamicBase ? " new " : string.Empty)} class VTableLoader" ) )
@ -1755,6 +1763,7 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat
@@ -1755,6 +1763,7 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat
}
}
NewLine ( ) ;
}
if ( ! hasDynamicBase )
WriteLine ( "protected CppSharp.Runtime.VTables __vtables;" ) ;
@ -1774,11 +1783,15 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat
@@ -1774,11 +1783,15 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat
}
using ( WriteBlock ( $"internal {(hasDynamicBase ? " override " : " virtual ")} void SetupVTables(bool destructorOnly = false)" ) )
{
// same reason as above, we can't hook vtable without ManagedToNative map
if ( generateNativeToManaged )
{
WriteLines ( $ @ "
if ( _ _ VTables . IsTransient )
_ _ VTables = VTableLoader . SetupVTables ( _ _ Instance , destructorOnly ) ; ", trimIndentation: true);
}
}
WriteLine ( "#endregion" ) ;
PopBlock ( NewLineKind . BeforeNextBlock ) ;
@ -2399,22 +2412,16 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetOrCreateInst
@@ -2399,22 +2412,16 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetOrCreateInst
NewLine ( ) ;
}
if ( HasVirtualTables ( @class ) )
// __GetInstance doesn't work without a ManagedToNativeMap, so don't generate it
if ( HasVirtualTables ( @class ) & & generateNativeToManaged )
{
@new = @class . HasBase & & HasVirtualTables ( @class . Bases . First ( ) . Class ) ;
WriteLines ( $ @ "
internal static { ( @new ? " new" : string . Empty ) } { printedClass } _ _ GetInstance ( { TypePrinter . IntPtrType } native )
{ { ");
if ( generateNativeToManaged )
{
WriteLines ( $ @ "
{ {
if ( ! { Helpers . TryGetNativeToManagedMappingIdentifier } ( native , out var managed ) )
throw new global :: System . Exception ( "" No managed instance was found "" ) ; ");
}
WriteLines ( $ @ "
throw new global :: System . Exception ( "" No managed instance was found "" ) ;
var result = ( { printedClass } ) managed ;
if ( result . { Helpers . OwnsNativeInstanceIdentifier } )
result . SetupVTables ( ) ;
@ -2950,12 +2957,24 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty
@@ -2950,12 +2957,24 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty
private void GenerateClassConstructor ( Method method , Class @class )
{
var generateNativeToManaged = Options . GenerateNativeToManagedFor ( @class ) ;
if ( ! generateNativeToManaged )
{
// if we don't have a NativeToManaged map, we can't do vtable hooking, because we can't
// fetch the managed class from the native pointer. vtable hooking is required to allow C++
// code to call virtual methods defined on a C++ class but overwritten in a C# class.
// todo: throwing an exception at runtime is ugly, we should seal the class instead
var typeFullName = TypePrinter . VisitClassDecl ( @class ) . Type . Replace ( "global::" , string . Empty ) ;
WriteLine ( $@"if (GetType().FullName != ""{typeFullName}"")" ) ;
WriteLineIndent ( $@"throw new Exception(""{typeFullName}: Can't inherit from classes with disabled NativeToManaged map"");" ) ;
}
var @internal = TypePrinter . PrintNative (
@class . IsAbstractImpl ? @class . BaseClass : @class ) ;
WriteLine ( $"{Helpers.InstanceIdentifier} = Marshal.AllocHGlobal(sizeof({@internal}));" ) ;
WriteLine ( $"{Helpers.OwnsNativeInstanceIdentifier} = true;" ) ;
if ( Options . GenerateNativeToManagedFor ( @class ) )
if ( generateNativeToManaged )
WriteLine ( $"{Helpers.RecordNativeToManagedMappingIdentifier}({Helpers.InstanceIdentifier}, this);" ) ;
if ( method . IsCopyConstructor )