Browse Source

VTable: improve codegen & make it thread safe (#1469)

pull/1470/head
josetr 5 years ago committed by GitHub
parent
commit
5912c236ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 130
      src/Generator/Generators/CSharp/CSharpSources.cs
  2. 24
      src/Runtime/VTables.cs

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

@ -1583,59 +1583,67 @@ namespace CppSharp.Generators.CSharp @@ -1583,59 +1583,67 @@ namespace CppSharp.Generators.CSharp
var hasVirtualDtor = wrappedEntries.Any(e => e.Method.IsDestructor);
bool hasDynamicBase = @class.NeedsBase && @class.BaseClass.IsDynamic;
var originalTableClass = @class.IsDependent ? @class.Specializations[0] : @class;
var originalTablePointers = $"new IntPtr[] {{ {string.Join(", ", originalTableClass.Layout.VTablePointers.Select(v => $"*(IntPtr*)({Helpers.InstanceIdentifier} + {v.Offset})"))} }}";
var destructorOnly = "destructorOnly";
WriteLine("internal static class VTableLoader");
{
WriteLine($"internal static{(hasDynamicBase ? " new" : string.Empty)} class VTableLoader");
{
WriteOpenBraceAndIndent();
WriteLine("private static bool initialized;");
WriteLine("private static IntPtr[] ManagedVTables;");
WriteLine("private static IntPtr[] Thunks;");
WriteLine("public static CppSharp.Runtime.VTables VTables;");
WriteLine($"private static volatile bool initialized;");
WriteLine($"private static readonly IntPtr*[] ManagedVTables = new IntPtr*[{@class.Layout.VTablePointers.Count}];");
if (hasVirtualDtor)
WriteLine($"private static IntPtr[] ManagedVTablesDtorOnly;");
WriteLine("private static readonly global::System.Collections.Generic.List<global::CppSharp.Runtime.SafeUnmanagedMemoryHandle>");
WriteLineIndent("HandleManagedVTables = new global::System.Collections.Generic.List<global::CppSharp.Runtime.SafeUnmanagedMemoryHandle>();");
WriteLine($"private static readonly IntPtr*[] ManagedVTablesDtorOnly = new IntPtr*[{@class.Layout.VTablePointers.Count}];");
WriteLine($"private static readonly IntPtr[] Thunks = new IntPtr[{wrappedEntries.Count}];");
WriteLine("private static CppSharp.Runtime.VTables VTables;");
WriteLine($"private static readonly global::System.Collections.Generic.List<CppSharp.Runtime.SafeUnmanagedMemoryHandle>");
WriteLineIndent("SafeHandles = new global::System.Collections.Generic.List<CppSharp.Runtime.SafeUnmanagedMemoryHandle>();");
NewLine();
WriteLine($"public static CppSharp.Runtime.VTables SetupVTables(IntPtr __Instance, bool {destructorOnly} = false)");
WriteLine($"static VTableLoader()");
{
WriteOpenBraceAndIndent();
WriteLine("if (!initialized)");
foreach (var entry in wrappedEntries.Distinct())
{
WriteOpenBraceAndIndent();
WriteLine($"Thunks = new IntPtr[{wrappedEntries.Count}];");
WriteLine($"var methodDelegates = new Delegate[{originalTableClass.Layout.VTablePointers.Count}][];");
var uniqueEntries = new HashSet<VTableComponent>();
var name = GetVTableMethodDelegateName(entry.Method);
WriteLine($"{name + "Instance"} += {name}Hook;");
}
for (var i = 0; i < wrappedEntries.Count; ++i)
{
var entry = wrappedEntries[i];
var name = GetVTableMethodDelegateName(entry.Method);
WriteLine($"Thunks[{i}] = Marshal.GetFunctionPointerForDelegate({name + "Instance"});");
}
UnindentAndWriteCloseBrace();
}
NewLine();
for (var i = 0; i < wrappedEntries.Count; ++i)
WriteLine($"public static CppSharp.Runtime.VTables SetupVTables(IntPtr instance, bool {destructorOnly} = false)");
{
WriteOpenBraceAndIndent();
WriteLine($"if (!initialized)");
{
WriteOpenBraceAndIndent();
WriteLine($"lock (ManagedVTables)");
WriteOpenBraceAndIndent();
WriteLine($"if (!initialized)");
{
var entry = wrappedEntries[i];
var name = GetVTableMethodDelegateName(entry.Method);
if (uniqueEntries.Add(entry))
WriteLine($"{name + "Instance"} += {name}Hook;");
WriteLine($"Thunks[{i}] = Marshal.GetFunctionPointerForDelegate({name + "Instance"});");
}
NewLine();
WriteLine($"var originalVTables = {originalTablePointers};");
if (hasVirtualDtor)
AllocateNewVTables(@class, wrappedEntries, destructorOnly: true, "ManagedVTablesDtorOnly");
WriteOpenBraceAndIndent();
WriteLine($"initialized = true;");
WriteLine($"VTables.Tables = {($"new IntPtr[] {{ {string.Join(", ", originalTableClass.Layout.VTablePointers.Select(x => $"*(IntPtr*)(instance + {x.Offset})"))} }}")};");
WriteLine($"VTables.Methods = new Delegate[{originalTableClass.Layout.VTablePointers.Count}][];");
AllocateNewVTables(@class, wrappedEntries, destructorOnly: false, "ManagedVTables");
if (hasVirtualDtor)
AllocateNewVTables(@class, wrappedEntries, destructorOnly: true, "ManagedVTablesDtorOnly");
WriteLine($"initialized = true;");
WriteLine($"VTables = new CppSharp.Runtime.VTables(originalVTables, methodDelegates);");
AllocateNewVTables(@class, wrappedEntries, destructorOnly: false, "ManagedVTables");
if (!hasVirtualDtor)
{
WriteLine($"if ({destructorOnly})");
WriteLineIndent("return VTables;");
if (!hasVirtualDtor)
{
WriteLine($"if ({destructorOnly})");
WriteLineIndent("return VTables;");
}
UnindentAndWriteCloseBrace();
}
UnindentAndWriteCloseBrace();
UnindentAndWriteCloseBrace();
}
NewLine();
@ -1675,7 +1683,7 @@ namespace CppSharp.Generators.CSharp @@ -1675,7 +1683,7 @@ namespace CppSharp.Generators.CSharp
{{
get {{
if (__vtables.IsEmpty)
__vtables = new CppSharp.Runtime.VTables({originalTablePointers});
__vtables.Tables = {($"new IntPtr[] {{ {string.Join(", ", originalTableClass.Layout.VTablePointers.Select(x => $"*(IntPtr*)({Helpers.InstanceIdentifier} + {x.Offset})"))} }}")};
return __vtables;
}}
@ -1701,8 +1709,6 @@ namespace CppSharp.Generators.CSharp @@ -1701,8 +1709,6 @@ namespace CppSharp.Generators.CSharp
AllocateNewVTablesMS(@class, wrappedEntries, destructorOnly, table);
else
AllocateNewVTablesItanium(@class, wrappedEntries, destructorOnly, table);
NewLine();
}
private void AssignNewVTableEntries(Class @class, string table)
@ -1710,15 +1716,13 @@ namespace CppSharp.Generators.CSharp @@ -1710,15 +1716,13 @@ namespace CppSharp.Generators.CSharp
for (int i = 0; i < @class.Layout.VTablePointers.Count; i++)
{
var offset = @class.Layout.VTablePointers[i].Offset;
WriteLine($"*(IntPtr*) ({Helpers.InstanceIdentifier} + {offset}) = {table}[{i}];");
WriteLine($"*(IntPtr**)(instance + {offset}) = {table}[{i}];");
}
}
private void AllocateNewVTablesMS(Class @class, IList<VTableComponent> wrappedEntries,
bool destructorOnly, string table)
{
WriteLine($"{table} = new IntPtr[{@class.Layout.VFTables.Count}];");
{
for (int i = 0; i < @class.Layout.VFTables.Count; i++)
{
VFTableInfo vftable = @class.Layout.VFTables[i];
@ -1734,8 +1738,6 @@ namespace CppSharp.Generators.CSharp @@ -1734,8 +1738,6 @@ namespace CppSharp.Generators.CSharp
private void AllocateNewVTablesItanium(Class @class, IList<VTableComponent> wrappedEntries,
bool destructorOnly, string table)
{
WriteLine($"{table} = new IntPtr[1];");
AllocateNewVTableEntries(@class.Layout.Layout.Components,
wrappedEntries, @class.Layout.VTablePointers[0].Offset, 0,
VTables.ItaniumOffsetToTopAndRTTI, destructorOnly, table);
@ -1747,31 +1749,13 @@ namespace CppSharp.Generators.CSharp @@ -1747,31 +1749,13 @@ namespace CppSharp.Generators.CSharp
{
string suffix = (destructorOnly ? "_dtor" : string.Empty) +
(tableIndex == 0 ? string.Empty : tableIndex.ToString(CultureInfo.InvariantCulture));
int size = entries.Count;
uint pointerSize = Context.TargetInfo.PointerWidth / 8;
string vtptr = $"vtptr{suffix}";
// allocate memory for the new v-table
WriteLine($"var {vtptr} = Marshal.AllocHGlobal({size} * {pointerSize});");
// ensure it's automatically freed when no longer needed
WriteLine($"HandleManagedVTables.Add(new global::CppSharp.Runtime.SafeUnmanagedMemoryHandle({vtptr}, true));");
string vfptr = $"vfptr{suffix}";
// obtain a pointer in the table to the start of virtual functions
WriteLine($"var {vfptr} = {vtptr} + {offsetRTTI} * {pointerSize};");
WriteLine($"{table}[{tableIndex}] = {vfptr};");
if (!destructorOnly)
WriteLine($"methodDelegates[{tableIndex}] = new Delegate[{entries.Count}];");
WriteLine($"{table}[{tableIndex}] = CppSharp.Runtime.VTables.CloneTable(SafeHandles, instance, {vptrOffset}, {entries.Count});");
// fill the newly allocated v-table
for (var i = 0; i < entries.Count; i++)
{
var entry = entries[i];
var offset = pointerSize * (i - offsetRTTI);
var nativeVftableEntry = $@"*(IntPtr*) ((*(IntPtr*) {
Helpers.InstanceIdentifier}) + {vptrOffset} + {offset})";
var managedVftableEntry = $"*(IntPtr*) ({vfptr} + {offset})";
if ((entry.Kind == VTableComponentKind.FunctionPointer ||
entry.Kind == VTableComponentKind.DeletingDtorPointer) &&
@ -1779,11 +1763,11 @@ namespace CppSharp.Generators.CSharp @@ -1779,11 +1763,11 @@ namespace CppSharp.Generators.CSharp
(!destructorOnly || entry.Method.IsDestructor ||
Context.Options.ExplicitlyPatchedVirtualFunctions.Contains(entry.Method.QualifiedOriginalName)))
// patch with pointers to managed code where needed
WriteLine("{0} = Thunks[{1}];", managedVftableEntry, wrappedEntries.IndexOf(entry));
else
// and simply copy the rest
WriteLine("{0} = {1};", managedVftableEntry, nativeVftableEntry);
WriteLine("{0}[{1}][{2}] = Thunks[{3}];", table, tableIndex, i - offsetRTTI, wrappedEntries.IndexOf(entry));
}
if (!destructorOnly)
WriteLine($"VTables.Methods[{tableIndex}] = new Delegate[{entries.Count}];");
}
private void GenerateVTableClassSetupCall(Class @class, bool destructorOnly = false)

24
src/Runtime/VTables.cs

@ -1,14 +1,15 @@ @@ -1,14 +1,15 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace CppSharp.Runtime
{
public unsafe struct VTables
public struct VTables
{
public Delegate[][] Methods { get; }
public IntPtr[] Tables { get; }
private Dictionary<(short, short, int), Delegate> Specializations;
public Delegate[][] Methods { get; set; }
public IntPtr[] Tables { get; set; }
private ConcurrentDictionary<(short, short, int), Delegate> Specializations;
public VTables(IntPtr[] tables, Delegate[][] methods = null)
{
@ -34,7 +35,7 @@ namespace CppSharp.Runtime @@ -34,7 +35,7 @@ namespace CppSharp.Runtime
else
{
if (Specializations == null)
Specializations = new Dictionary<(short, short, int), Delegate>();
Specializations = new ConcurrentDictionary<(short, short, int), Delegate>();
var key = (specialiation, table, slot);
@ -44,5 +45,16 @@ namespace CppSharp.Runtime @@ -44,5 +45,16 @@ namespace CppSharp.Runtime
return (T)method;
}
}
public unsafe static IntPtr* CloneTable(List<SafeUnmanagedMemoryHandle> cache, IntPtr instance, int offset, int size)
{
var sizeInBytes = size * sizeof(IntPtr);
var src = ((*(IntPtr*)instance) + offset).ToPointer();
var entries = (IntPtr*)Marshal.AllocHGlobal(sizeInBytes);
Buffer.MemoryCopy(src, entries, sizeInBytes, sizeInBytes);
cache.Add(new SafeUnmanagedMemoryHandle((IntPtr)entries, true));
return entries;
}
}
}
}

Loading…
Cancel
Save