Drop support for generating "safe" code#69
Conversation
…lying native type.
|
Sorry for the "big" PR. LLVMSharp is going to be just as bad. |
| public TranslationUnit TranslationUnit { get; } | ||
|
|
||
| public CXToken[] Tokenize(Cursor cursor) | ||
| public Span<CXToken> Tokenize(Cursor cursor) |
There was a problem hiding this comment.
Took a dependency on System.Memory to avoid creating arrays and copying overhead where possible.
| return EscapeName(name); | ||
| } | ||
|
|
||
| private string GetAccessSpecifierName(NamedDecl namedDecl) |
There was a problem hiding this comment.
Also added support for matching the native access specifier.
| if (type is ArrayType arrayType) | ||
| { | ||
| name = GetTypeName(namedDecl, arrayType.ElementType); | ||
| name = GetTypeName(namedDecl, arrayType.ElementType, out var nativeElementTypeName); |
There was a problem hiding this comment.
The secondary variables here are only used for debugging purposes. In some future PR, they might be used to drop more NativeTypeNameAttributes when possible (but the logic for treating unsigned int * as equal to uint* or transforming const unsigned int * const * to be const uint * const * is a bit tricky).
|
|
||
| switch (name) | ||
| { | ||
| case "int8_t": |
There was a problem hiding this comment.
All of these "just work" now.
| public CXString GetParent(ref CXCursorKind kind) => clang.getCompletionParent(this, ref kind); | ||
| public CXString GetParent(out CXCursorKind kind) | ||
| { | ||
| fixed (CXCursorKind* pKind = &kind) |
There was a problem hiding this comment.
The downside (or upside, depending on how you look at it) to having unsafe bindings is you get to explicitly see all the "hidden" overhead that the marshaler was adding (such as implicitly pinning a value).
|
|
||
| namespace ClangSharp | ||
| { | ||
| public unsafe partial struct CXModuleMapDescriptor : IDisposable |
There was a problem hiding this comment.
Had to add a couple more "safe" wrappers so the non-PInvokeGenerator unit tests would pass.
|
|
||
| public CXErrorCode SetFrameworkModuleName(string name) | ||
| { | ||
| using (var marshaledName = new MarshaledString(name)) |
There was a problem hiding this comment.
This is how I opted to do the string marshaling (and cleanup) currently.
Ideally the "safe" API would take a UTF8String rather than string, but that type doesn't exist yet (and support for treating ReadOnlySpan<byte> as a utf8string is fairly limited in netstandard2.0).
| } | ||
|
|
||
| var span = new ReadOnlySpan<byte>(pCString, int.MaxValue); | ||
| return span.Slice(0, span.IndexOf((byte)'\0')).AsString(); |
There was a problem hiding this comment.
Marshaling from native to string just utilizes ROSpan<byte> and System.Text.Encoding.UTF8.GetBytes
| using (var marshaledCommandLineArgs = new MarshaledStringArray(commandLineArgs)) | ||
| fixed (CXUnsavedFile* pUnsavedFiles = unsavedFiles) | ||
| { | ||
| var pCommandLineArgs = stackalloc sbyte*[marshaledCommandLineArgs.Count]; |
There was a problem hiding this comment.
Marshaling string arrays is currently a bit more complex. I had tried to allocate a sbyte** and then fill it directly (to avoid needing to allocate a sbyte*[]), but that was having problems and I decided to resolve it later.
| public void Dispose() => clang.disposeTranslationUnit(this); | ||
|
|
||
| public void DisposeTokens(CXToken[] tokens) => clang.disposeTokens(this, tokens, (uint)tokens.Length); | ||
| public void AnnotateTokens(CXToken[] tokens, CXCursor[] cursors) |
There was a problem hiding this comment.
It would be nice if most of these were updated to take a ROSpan<T> or Span<T>, as appropriate. So you can avoid allocations where appropriate, but I left that to a future PR.
| @@ -1,325 +0,0 @@ | |||
| namespace ClangSharp | |||
There was a problem hiding this comment.
I was able to get rid of all method/type exclusions and just generate the bindings directly instead.
| public unsafe partial struct CXCodeCompleteResults | ||
| { | ||
| public IntPtr Results; | ||
| [NativeTypeName("CXCompletionResult *")] |
There was a problem hiding this comment.
Here is an example of where, if we "normalized" the native type name when checking for "matches managed type name", we could drop the attribute.
| public partial struct CXCompletionResult | ||
| public unsafe partial struct CXCompletionResult | ||
| { | ||
| [NativeTypeName("enum CXCursorKind")] |
There was a problem hiding this comment.
Another example of where "normalizing" the native type name would allow us to drop the attribute.
| public IntPtr data0; public IntPtr data1; public IntPtr data2; | ||
|
|
||
| [NativeTypeName("const void *[3]")] | ||
| public _data_e__FixedBuffer data; |
There was a problem hiding this comment.
I find the attributes to, at a glance, make it much easier to determine the original signature. While still allowing you to use the least expensive managed signature. Here is a good example.
| public CXIdxObjCContainerDeclInfo* containerInfo; | ||
|
|
||
| [NativeTypeName("const CXIdxBaseClassInfo *")] | ||
| public CXIdxBaseClassInfo* superInfo; |
There was a problem hiding this comment.
It also allows you to see the "const" info, which can be very useful for exposing the right managed API (and knowing things like: "should this be span or rospan").
|
|
||
| namespace ClangSharp | ||
| { | ||
| [Conditional("DEBUG")] |
There was a problem hiding this comment.
Conditional("DEBUG") will cause the attribute to be stripped from release binaries, avoiding bloat.
|
|
||
| public IntPtr Pointer; | ||
|
|
||
| public static implicit operator CXCursorSet(CXCursorSetImpl* value) |
There was a problem hiding this comment.
Using implicit operators to convert from the native type to the wrapper type when it is an "exact" match.
|
|
||
| public IntPtr Pointer; | ||
|
|
||
| public static explicit operator CXDiagnostic(void* value) |
There was a problem hiding this comment.
Using explicit operators to convert from the native type to the wrapper type when it is an "ambiguous" match.
As called out here, the current support for generating "safe" code is very error prone and isn't going to be easy to maintain long term.
Namely, almost all of the support is a "best" guess heuristic where the heuristics can vary wildly from header file to header file and we frequently get it wrong, especially when dealing with
bool,out,ref, orarrays.This removes that support and switches to only generating
unsafecode. As part of that, it updates the logic of when theunsafekeyword is emitted to ensure it is only emitted when actually required. This also addsNativeTypeNameattribute metadata to members where the native type and managed type do not line up (this could do with some tuning in the future, as it is currently overly conservative). The attribute is marked as[Conditional("DEBUG")]so it doesn't bloat the release binaries.This then regenerates ClangSharp and fixes up the higher level bindings where required.