Skip to content

Commit 20d281c

Browse files
Merge pull request #272 from tannergooding/improvements
Updating Cursor and Type to avoid repeated hot path allocations
2 parents 1fe801a + 8faf434 commit 20d281c

File tree

2 files changed

+61
-21
lines changed

2 files changed

+61
-21
lines changed

sources/ClangSharp/Cursors/Cursor.cs

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Diagnostics;
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
68
using ClangSharp.Interop;
79

810
namespace ClangSharp
911
{
1012
[DebuggerDisplay("{Handle.DebuggerDisplayString,nq}")]
1113
public unsafe class Cursor : IEquatable<Cursor>
1214
{
13-
private readonly Lazy<IReadOnlyList<Cursor>> _cursorChildren;
15+
private readonly Lazy<string> _kindSpelling;
1416
private readonly Lazy<Cursor> _lexicalParentCursor;
1517
private readonly Lazy<Cursor> _semanticParentCursor;
18+
private readonly Lazy<string> _spelling;
1619
private readonly Lazy<TranslationUnit> _translationUnit;
20+
private List<Cursor> _cursorChildren;
1721

1822
private protected Cursor(CXCursor handle, CXCursorKind expectedCursorKind)
1923
{
@@ -23,28 +27,60 @@ private protected Cursor(CXCursor handle, CXCursorKind expectedCursorKind)
2327
}
2428
Handle = handle;
2529

26-
_cursorChildren = new Lazy<IReadOnlyList<Cursor>>(() => {
27-
var cursors = new List<Cursor>();
28-
29-
_ = Handle.VisitChildren((cursor, parent, clientData) => {
30-
var cursorChild = TranslationUnit.GetOrCreate<Cursor>(cursor);
31-
cursors.Add(cursorChild);
32-
return CXChildVisitResult.CXChildVisit_Continue;
33-
}, clientData: default);
34-
35-
return cursors;
36-
});
37-
30+
_kindSpelling = new Lazy<string>(() => Handle.KindSpelling.ToString());
3831
_lexicalParentCursor = new Lazy<Cursor>(() => TranslationUnit.GetOrCreate<Cursor>(Handle.LexicalParent));
3932
_semanticParentCursor = new Lazy<Cursor>(() => TranslationUnit.GetOrCreate<Cursor>(Handle.SemanticParent));
33+
_spelling = new Lazy<string>(() => Handle.Spelling.ToString());
4034
_translationUnit = new Lazy<TranslationUnit>(() => TranslationUnit.GetOrCreate(Handle.TranslationUnit));
4135
}
4236

43-
public IReadOnlyList<Cursor> CursorChildren => _cursorChildren.Value;
37+
public IReadOnlyList<Cursor> CursorChildren
38+
{
39+
get
40+
{
41+
if (_cursorChildren is null)
42+
{
43+
var cursorChildren = GCHandle.Alloc(new List<Cursor>());
44+
45+
#if !NET5_0_OR_GREATER
46+
var visitor = (CXCursorVisitor)Visitor;
47+
var pVisitor = Marshal.GetFunctionPointerForDelegate(visitor);
48+
#else
49+
var pVisitor = (delegate* unmanaged[Cdecl]<CXCursor, CXCursor, nint*, CXChildVisitResult>)&Visitor;
50+
#endif
51+
52+
var client_data = stackalloc nint[2] {
53+
GCHandle.ToIntPtr(cursorChildren),
54+
TranslationUnit.Handle.Handle
55+
};
56+
57+
_ = clang.visitChildren(Handle, (IntPtr)pVisitor, client_data);
58+
59+
_cursorChildren = (List<Cursor>)cursorChildren.Target;
60+
cursorChildren.Free();
61+
62+
#if !NET5_0_OR_GREATER
63+
GC.KeepAlive(visitor);
64+
#else
65+
[UnmanagedCallersOnly(CallConvs = new System.Type[] { typeof(CallConvCdecl) })]
66+
#endif
67+
static CXChildVisitResult Visitor(CXCursor cursor, CXCursor parent, void* client_data)
68+
{
69+
var cursorChildren = (List<Cursor>)GCHandle.FromIntPtr(((nint*)client_data)[0]).Target;
70+
var translationUnit = TranslationUnit.GetOrCreate((CXTranslationUnitImpl*)((nint*)client_data)[1]);
71+
72+
var cursorChild = translationUnit.GetOrCreate<Cursor>(cursor);
73+
cursorChildren.Add(cursorChild);
74+
return CXChildVisitResult.CXChildVisit_Continue;
75+
}
76+
}
77+
return _cursorChildren;
78+
}
79+
}
4480

45-
public CXCursorKind CursorKind => Handle.Kind;
81+
public CXCursorKind CursorKind => Handle.kind;
4682

47-
public string CursorKindSpelling => Handle.KindSpelling.ToString();
83+
public string CursorKindSpelling => _kindSpelling.Value;
4884

4985
public CXSourceRange Extent => Handle.Extent;
5086

@@ -56,7 +92,7 @@ private protected Cursor(CXCursor handle, CXCursorKind expectedCursorKind)
5692

5793
public Cursor SemanticParentCursor => _semanticParentCursor.Value;
5894

59-
public string Spelling => Handle.Spelling.ToString();
95+
public string Spelling => _spelling.Value;
6096

6197
public TranslationUnit TranslationUnit => _translationUnit.Value;
6298

@@ -103,6 +139,6 @@ internal static Cursor Create(CXCursor handle)
103139

104140
public override int GetHashCode() => Handle.GetHashCode();
105141

106-
public override string ToString() => Handle.ToString();
142+
public override string ToString() => Spelling;
107143
}
108144
}

sources/ClangSharp/Types/Type.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ namespace ClangSharp
99
[DebuggerDisplay("{Handle.DebuggerDisplayString,nq}")]
1010
public unsafe class Type : IEquatable<Type>
1111
{
12+
private readonly Lazy<string> _asString;
1213
private readonly Lazy<Type> _canonicalType;
1314
private readonly Lazy<Type> _desugar;
15+
private readonly Lazy<string> _kindSpelling;
1416
private readonly Lazy<Type> _pointeeType;
1517
private readonly Lazy<TranslationUnit> _translationUnit;
1618

@@ -27,15 +29,17 @@ protected Type(CXType handle, CXTypeKind expectedKind, CX_TypeClass expectedType
2729
}
2830
Handle = handle;
2931

32+
_asString = new Lazy<string>(() => Handle.Spelling.ToString());
3033
_canonicalType = new Lazy<Type>(() => TranslationUnit.GetOrCreate<Type>(Handle.CanonicalType));
3134
_desugar = new Lazy<Type>(() => TranslationUnit.GetOrCreate<Type>(Handle.Desugar));
35+
_kindSpelling = new Lazy<string>(() => Handle.KindSpelling.ToString());
3236
_pointeeType = new Lazy<Type>(() => TranslationUnit.GetOrCreate<Type>(Handle.PointeeType));
3337
_translationUnit = new Lazy<TranslationUnit>(() => TranslationUnit.GetOrCreate((CXTranslationUnit)Handle.data[1]));
3438
}
3539

3640
public CXXRecordDecl AsCXXRecordDecl => AsTagDecl as CXXRecordDecl;
3741

38-
public string AsString => Handle.Spelling.ToString();
42+
public string AsString => _asString.Value;
3943

4044
public TagDecl AsTagDecl
4145
{
@@ -79,7 +83,7 @@ public bool IsIntegralOrEnumerationType
7983

8084
public CXTypeKind Kind => Handle.kind;
8185

82-
public string KindSpelling => Handle.KindSpelling.ToString();
86+
public string KindSpelling => _kindSpelling.Value;
8387

8488
public Type PointeeType => _pointeeType.Value;
8589

@@ -191,6 +195,6 @@ public T GetAs<T>()
191195

192196
public override int GetHashCode() => Handle.GetHashCode();
193197

194-
public override string ToString() => Handle.ToString();
198+
public override string ToString() => AsString;
195199
}
196200
}

0 commit comments

Comments
 (0)