The changes consist of 3 major elements:
1 - changes to Dynamo for writing classes/methods that are unsafe
2 - refactoring NewClassCompiler such that nearly all auxiliary classes live in VTableDetails and handling of auxiliary classes is uniform.
3 - remove all vtable generation code to VTableDetails which now writes vtables such that there is a "local" vtable which contains managed delegates that call local receivers and there is a swift vtable which contains unmanaged delegates which call receivers that retrieve the local vtable and trampoline out to them.
The last element is the most important. What this does is allows the unmanaged delegates to live in their own class and compile correctly with [UnmanagedCallersOnly] set.
A lot of this is non-obvious code, so I tried to document VTableDetails to make it clear what's going on.
A couple notes:
There will be a local/swift vtable for every type that needs a vtable. The local vtable is technically not needed for non-generic classes, but that could be an optimization for another day.
TIL that you can't have pointer types in generics eg, Func<ExistentialContainer *> this required me using delegate types instead which are quite happy to have pointers, but at the same time delegate *unmanaged<> doesn't like pointer types for the same reason so I had to inject some ham-handed casting to turn pointer types into IntPtr and cast them back.
This is...not small.
The changes consist of 3 major elements: 1 - changes to Dynamo for writing classes/methods that are
unsafe
2 - refactoring NewClassCompiler such that nearly all auxiliary classes live inVTableDetails
and handling of auxiliary classes is uniform. 3 - remove all vtable generation code toVTableDetails
which now writes vtables such that there is a "local" vtable which contains managed delegates that call local receivers and there is a swift vtable which contains unmanaged delegates which call receivers that retrieve the local vtable and trampoline out to them.The last element is the most important. What this does is allows the unmanaged delegates to live in their own class and compile correctly with
[UnmanagedCallersOnly]
set.A lot of this is non-obvious code, so I tried to document VTableDetails to make it clear what's going on.
A couple notes: There will be a local/swift vtable for every type that needs a vtable. The local vtable is technically not needed for non-generic classes, but that could be an optimization for another day. TIL that you can't have pointer types in generics eg,
Func<ExistentialContainer *>
this required me usingdelegate
types instead which are quite happy to have pointers, but at the same timedelegate *unmanaged<>
doesn't like pointer types for the same reason so I had to inject some ham-handed casting to turn pointer types intoIntPtr
and cast them back.