Browse Source

Add `GenerateNativeToManaged` per-class option

refactor
josetr 3 years ago
parent
commit
37bd7e0155
  1. 7
      src/Generator/ClassOptions.cs
  2. 88
      src/Generator/Generators/CSharp/CSharpSources.cs
  3. 6
      src/Generator/Options.cs
  4. 6
      tests/CSharp/CSharp.Gen.cs
  5. 7
      tests/CSharp/CSharp.Tests.cs
  6. 2
      tests/CSharp/CSharp.h

7
src/Generator/ClassOptions.cs

@ -0,0 +1,7 @@
namespace CppSharp
{
public class ClassGenerationOptions
{
public bool GenerateNativeToManaged { get; set; }
}
}

88
src/Generator/Generators/CSharp/CSharpSources.cs

@ -438,7 +438,9 @@ namespace CppSharp.Generators.CSharp
var valueType = Options.GenerateFinalizerFor(@class) ? $"global::System.WeakReference<{printedClass}>" : $"{printedClass}"; var valueType = Options.GenerateFinalizerFor(@class) ? $"global::System.WeakReference<{printedClass}>" : $"{printedClass}";
var dict = $@"global::System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {valueType}>"; var dict = $@"global::System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {valueType}>";
WriteLine("internal static readonly {0} NativeToManagedMap = new {0}();", dict); var generateNativeToManaged = Options.GenerateNativeToManagedFor(@class);
if (generateNativeToManaged)
WriteLine("internal static readonly {0} NativeToManagedMap = new {0}();", dict);
PopBlock(NewLineKind.BeforeNextBlock); PopBlock(NewLineKind.BeforeNextBlock);
// Create a method to record/recover a mapping to make it easier to call from template instantiation // Create a method to record/recover a mapping to make it easier to call from template instantiation
@ -446,32 +448,35 @@ namespace CppSharp.Generators.CSharp
// will never get invoked: unless the managed object is Disposed, there will always be a reference // will never get invoked: unless the managed object is Disposed, there will always be a reference
// in the dictionary. // in the dictionary.
PushBlock(BlockKind.Method); PushBlock(BlockKind.Method);
if (Options.GenerateFinalizerFor(@class)) if (generateNativeToManaged)
{ {
WriteLines($@" if (Options.GenerateFinalizerFor(@class))
internal static void {Helpers.RecordNativeToManagedMappingIdentifier}(IntPtr native, {printedClass} managed) {
{{ WriteLines($@"
NativeToManagedMap[native] = new global::System.WeakReference<{printedClass}>(managed); internal static void {Helpers.RecordNativeToManagedMappingIdentifier}(IntPtr native, {printedClass} managed)
}} {{
NativeToManagedMap[native] = new global::System.WeakReference<{printedClass}>(managed);
internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr native, out {printedClass} managed) }}
{{
managed = default; internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr native, out {printedClass} managed)
return NativeToManagedMap.TryGetValue(native, out var wr) && wr.TryGetTarget(out managed); {{
}}"); managed = default;
} return NativeToManagedMap.TryGetValue(native, out var wr) && wr.TryGetTarget(out managed);
else }}");
{ }
WriteLines($@" else
internal static void {Helpers.RecordNativeToManagedMappingIdentifier}(IntPtr native, {printedClass} managed) {
{{ WriteLines($@"
NativeToManagedMap[native] = managed; internal static void {Helpers.RecordNativeToManagedMappingIdentifier}(IntPtr native, {printedClass} managed)
}} {{
NativeToManagedMap[native] = managed;
internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr native, out {printedClass} managed) }}
{{
return NativeToManagedMap.TryGetValue(native, out managed); internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr native, out {printedClass} managed)
}}"); {{
return NativeToManagedMap.TryGetValue(native, out managed);
}}");
}
} }
} }
PopBlock(NewLineKind.BeforeNextBlock); PopBlock(NewLineKind.BeforeNextBlock);
@ -2270,7 +2275,8 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat
WriteLineIndent("return;"); WriteLineIndent("return;");
// The local var must be of the exact type in the object map because of TryRemove // The local var must be of the exact type in the object map because of TryRemove
WriteLine("NativeToManagedMap.TryRemove({0}, out _);", Helpers.InstanceIdentifier); if (Options.GenerateNativeToManagedFor(@class))
WriteLine("NativeToManagedMap.TryRemove({0}, out _);", Helpers.InstanceIdentifier);
var realClass = @class.IsTemplate ? @class.Specializations[0] : @class; var realClass = @class.IsTemplate ? @class.Specializations[0] : @class;
var classInternal = TypePrinter.PrintNative(realClass); var classInternal = TypePrinter.PrintNative(realClass);
if (@class.IsDynamic && GetUniqueVTableMethodEntries(realClass).Count != 0) if (@class.IsDynamic && GetUniqueVTableMethodEntries(realClass).Count != 0)
@ -2402,7 +2408,10 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat
{ {
var @new = @class.HasBase && @class.HasRefBase(); var @new = @class.HasBase && @class.HasRefBase();
WriteLines($@" bool generateNativeToManaged = Options.GenerateNativeToManagedFor(@class);
if (generateNativeToManaged)
{
WriteLines($@"
internal static{(@new ? " new" : string.Empty)} {printedClass} __GetOrCreateInstance({TypePrinter.IntPtrType} native, bool saveInstance = false, bool skipVTables = false) internal static{(@new ? " new" : string.Empty)} {printedClass} __GetOrCreateInstance({TypePrinter.IntPtrType} native, bool saveInstance = false, bool skipVTables = false)
{{ {{
if (native == {TypePrinter.IntPtrType}.Zero) if (native == {TypePrinter.IntPtrType}.Zero)
@ -2414,7 +2423,8 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetOrCreateInst
{Helpers.RecordNativeToManagedMappingIdentifier}(native, result); {Helpers.RecordNativeToManagedMappingIdentifier}(native, result);
return result; return result;
}}"); }}");
NewLine(); NewLine();
}
if (HasVirtualTables(@class)) if (HasVirtualTables(@class))
{ {
@ -2422,9 +2432,16 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetOrCreateInst
WriteLines($@" WriteLines($@"
internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({TypePrinter.IntPtrType} native) internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({TypePrinter.IntPtrType} native)
{{ {{");
if (generateNativeToManaged)
{
WriteLines($@"
if (!{Helpers.TryGetNativeToManagedMappingIdentifier}(native, out var managed)) if (!{Helpers.TryGetNativeToManagedMappingIdentifier}(native, out var managed))
throw new global::System.Exception(""No managed instance was found""); throw new global::System.Exception(""No managed instance was found"");");
}
WriteLines($@"
var result = ({printedClass})managed; var result = ({printedClass})managed;
if (result.{Helpers.OwnsNativeInstanceIdentifier}) if (result.{Helpers.OwnsNativeInstanceIdentifier})
result.SetupVTables(); result.SetupVTables();
@ -2540,7 +2557,8 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty
if (@class.IsRefType) if (@class.IsRefType)
{ {
WriteLine($"{Helpers.OwnsNativeInstanceIdentifier} = true;"); WriteLine($"{Helpers.OwnsNativeInstanceIdentifier} = true;");
WriteLine($"{Helpers.RecordNativeToManagedMappingIdentifier}({Helpers.InstanceIdentifier}, this);"); if (Options.GenerateNativeToManagedFor(@class))
WriteLine($"{Helpers.RecordNativeToManagedMappingIdentifier}({Helpers.InstanceIdentifier}, this);");
} }
else else
{ {
@ -2985,7 +3003,9 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty
@class.IsAbstractImpl ? @class.BaseClass : @class); @class.IsAbstractImpl ? @class.BaseClass : @class);
WriteLine($"{Helpers.InstanceIdentifier} = Marshal.AllocHGlobal(sizeof({@internal}));"); WriteLine($"{Helpers.InstanceIdentifier} = Marshal.AllocHGlobal(sizeof({@internal}));");
WriteLine($"{Helpers.OwnsNativeInstanceIdentifier} = true;"); WriteLine($"{Helpers.OwnsNativeInstanceIdentifier} = true;");
WriteLine($"{Helpers.RecordNativeToManagedMappingIdentifier}({Helpers.InstanceIdentifier}, this);");
if (Options.GenerateNativeToManagedFor(@class))
WriteLine($"{Helpers.RecordNativeToManagedMappingIdentifier}({Helpers.InstanceIdentifier}, this);");
if (method.IsCopyConstructor) if (method.IsCopyConstructor)
{ {

6
src/Generator/Options.cs

@ -136,6 +136,12 @@ namespace CppSharp
/// </remarks> /// </remarks>
public Func<Class, bool> GenerateFinalizersFilter = (@class) => true; public Func<Class, bool> GenerateFinalizersFilter = (@class) => true;
/// <summary>
/// A callback that allows the user to provide generator options per-class.
/// </summary>
public Func<Class, ClassGenerationOptions> GetClassGenerationOptions = null;
internal bool GenerateNativeToManagedFor(Class @class) => GetClassGenerationOptions?.Invoke(@class)?.GenerateNativeToManaged ?? true;
/// <summary> /// <summary>
/// An internal convenience method that combines the effect of <see /// An internal convenience method that combines the effect of <see
/// cref="GenerateFinalizers"/> and <see cref="GenerateFinalizersFilter"/>. /// cref="GenerateFinalizers"/> and <see cref="GenerateFinalizersFilter"/>.

6
tests/CSharp/CSharp.Gen.cs

@ -33,6 +33,12 @@ namespace CppSharp.Tests
driver.Options.MarshalCharAsManagedChar = true; driver.Options.MarshalCharAsManagedChar = true;
driver.Options.GenerateDefaultValuesForArguments = true; driver.Options.GenerateDefaultValuesForArguments = true;
driver.Options.GenerateClassTemplates = true; driver.Options.GenerateClassTemplates = true;
var disableNativeToManaged = new ClassGenerationOptions { GenerateNativeToManaged = false };
driver.Options.GetClassGenerationOptions = e =>
{
return e.Name == "ClassWithoutNativeToManaged" ? disableNativeToManaged : null;
};
} }
public override void Preprocess(Driver driver, ASTContext ctx) public override void Preprocess(Driver driver, ASTContext ctx)

7
tests/CSharp/CSharp.Tests.cs

@ -1893,4 +1893,11 @@ public unsafe class CSharpTests
Assert.That(CSharp.CSharp.TestFunctionToInstanceMethodConstStruct(new FTIStruct(), new FTIStruct() { A = 6 }), Is.EqualTo(6)); Assert.That(CSharp.CSharp.TestFunctionToInstanceMethodConstStruct(new FTIStruct(), new FTIStruct() { A = 6 }), Is.EqualTo(6));
Assert.That(CSharp.CSharp.TestFunctionToInstanceMethodConstRefStruct(new FTIStruct(), new FTIStruct() { A = 6 }), Is.EqualTo(6)); Assert.That(CSharp.CSharp.TestFunctionToInstanceMethodConstRefStruct(new FTIStruct(), new FTIStruct() { A = 6 }), Is.EqualTo(6));
} }
[TestCase(typeof(FTIStruct), ExpectedResult = true)]
[TestCase(typeof(ClassWithoutNativeToManaged), ExpectedResult = false)]
public bool TestClassGenerateNativeToManaged(Type type)
{
return type.GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Any(x => x.Name.Contains("NativeToManaged"));
}
} }

2
tests/CSharp/CSharp.h

@ -1534,3 +1534,5 @@ DLL_API int TestFunctionToInstanceMethodStruct(FTIStruct* bb, FTIStruct defaultV
DLL_API int TestFunctionToInstanceMethodRefStruct(FTIStruct* bb, FTIStruct& defaultValue); DLL_API int TestFunctionToInstanceMethodRefStruct(FTIStruct* bb, FTIStruct& defaultValue);
DLL_API int TestFunctionToInstanceMethodConstStruct(FTIStruct* bb, const FTIStruct defaultValue); DLL_API int TestFunctionToInstanceMethodConstStruct(FTIStruct* bb, const FTIStruct defaultValue);
DLL_API int TestFunctionToInstanceMethodConstRefStruct(FTIStruct* bb, const FTIStruct& defaultValue); DLL_API int TestFunctionToInstanceMethodConstRefStruct(FTIStruct* bb, const FTIStruct& defaultValue);
class ClassWithoutNativeToManaged { };
Loading…
Cancel
Save