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 @@ @@ -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 @@ -438,7 +438,9 @@ namespace CppSharp.Generators.CSharp
var valueType = Options.GenerateFinalizerFor(@class) ? $"global::System.WeakReference<{printedClass}>" : $"{printedClass}";
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);
// 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 @@ -446,32 +448,35 @@ namespace CppSharp.Generators.CSharp
// will never get invoked: unless the managed object is Disposed, there will always be a reference
// in the dictionary.
PushBlock(BlockKind.Method);
if (Options.GenerateFinalizerFor(@class))
{
WriteLines($@"
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;
return NativeToManagedMap.TryGetValue(native, out var wr) && wr.TryGetTarget(out managed);
}}");
}
else
{
WriteLines($@"
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);
}}");
if (generateNativeToManaged)
{
if (Options.GenerateFinalizerFor(@class))
{
WriteLines($@"
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;
return NativeToManagedMap.TryGetValue(native, out var wr) && wr.TryGetTarget(out managed);
}}");
}
else
{
WriteLines($@"
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);
}}");
}
}
}
PopBlock(NewLineKind.BeforeNextBlock);
@ -2270,7 +2275,8 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat @@ -2270,7 +2275,8 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat
WriteLineIndent("return;");
// 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 classInternal = TypePrinter.PrintNative(realClass);
if (@class.IsDynamic && GetUniqueVTableMethodEntries(realClass).Count != 0)
@ -2402,7 +2408,10 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat @@ -2402,7 +2408,10 @@ internal static bool {Helpers.TryGetNativeToManagedMappingIdentifier}(IntPtr nat
{
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)
{{
if (native == {TypePrinter.IntPtrType}.Zero)
@ -2414,7 +2423,8 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetOrCreateInst @@ -2414,7 +2423,8 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetOrCreateInst
{Helpers.RecordNativeToManagedMappingIdentifier}(native, result);
return result;
}}");
NewLine();
NewLine();
}
if (HasVirtualTables(@class))
{
@ -2422,9 +2432,16 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetOrCreateInst @@ -2422,9 +2432,16 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetOrCreateInst
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"");
throw new global::System.Exception(""No managed instance was found"");");
}
WriteLines($@"
var result = ({printedClass})managed;
if (result.{Helpers.OwnsNativeInstanceIdentifier})
result.SetupVTables();
@ -2540,7 +2557,8 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty @@ -2540,7 +2557,8 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty
if (@class.IsRefType)
{
WriteLine($"{Helpers.OwnsNativeInstanceIdentifier} = true;");
WriteLine($"{Helpers.RecordNativeToManagedMappingIdentifier}({Helpers.InstanceIdentifier}, this);");
if (Options.GenerateNativeToManagedFor(@class))
WriteLine($"{Helpers.RecordNativeToManagedMappingIdentifier}({Helpers.InstanceIdentifier}, this);");
}
else
{
@ -2985,7 +3003,9 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty @@ -2985,7 +3003,9 @@ internal static{(@new ? " new" : string.Empty)} {printedClass} __GetInstance({Ty
@class.IsAbstractImpl ? @class.BaseClass : @class);
WriteLine($"{Helpers.InstanceIdentifier} = Marshal.AllocHGlobal(sizeof({@internal}));");
WriteLine($"{Helpers.OwnsNativeInstanceIdentifier} = true;");
WriteLine($"{Helpers.RecordNativeToManagedMappingIdentifier}({Helpers.InstanceIdentifier}, this);");
if (Options.GenerateNativeToManagedFor(@class))
WriteLine($"{Helpers.RecordNativeToManagedMappingIdentifier}({Helpers.InstanceIdentifier}, this);");
if (method.IsCopyConstructor)
{

6
src/Generator/Options.cs

@ -136,6 +136,12 @@ namespace CppSharp @@ -136,6 +136,12 @@ namespace CppSharp
/// </remarks>
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>
/// An internal convenience method that combines the effect of <see
/// cref="GenerateFinalizers"/> and <see cref="GenerateFinalizersFilter"/>.

6
tests/CSharp/CSharp.Gen.cs

@ -33,6 +33,12 @@ namespace CppSharp.Tests @@ -33,6 +33,12 @@ namespace CppSharp.Tests
driver.Options.MarshalCharAsManagedChar = true;
driver.Options.GenerateDefaultValuesForArguments = 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)

7
tests/CSharp/CSharp.Tests.cs

@ -1893,4 +1893,11 @@ public unsafe class CSharpTests @@ -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.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 @@ -1534,3 +1534,5 @@ DLL_API int TestFunctionToInstanceMethodStruct(FTIStruct* bb, FTIStruct defaultV
DLL_API int TestFunctionToInstanceMethodRefStruct(FTIStruct* bb, FTIStruct& defaultValue);
DLL_API int TestFunctionToInstanceMethodConstStruct(FTIStruct* bb, const FTIStruct defaultValue);
DLL_API int TestFunctionToInstanceMethodConstRefStruct(FTIStruct* bb, const FTIStruct& defaultValue);
class ClassWithoutNativeToManaged { };
Loading…
Cancel
Save