Skip to content

Commit 8abd033

Browse files
committed
Removed type reference and ABI handling from TranslatedVTableEntry and reworked things to use the corresponding TranslatedFunction instead.
Fixes #147 (Type information redundant between TranslatedFunction and TranslatedVTableEntry.) Contributes to #53 (ABI handling in TranslatedVTableEntry was elimintated.) Fixes #103 (VTable entries using the base type for this in child vtables.)
1 parent 59fdbd1 commit 8abd033

File tree

7 files changed

+83
-125
lines changed

7 files changed

+83
-125
lines changed

Biohazrd.CSharp/CSharpLibraryGenerator.Functions.cs

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Diagnostics;
55
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
67
using static Biohazrd.CSharp.CSharpCodeWriter;
78

89
namespace Biohazrd.CSharp
@@ -127,7 +128,6 @@ private void EmitFunctionTrampoline(VisitorContext context, EmitFunctionContext
127128
// (We do this first so we can change our emit if the method is broken.)
128129
string? methodAccess = null;
129130
string? methodAccessFailure = null;
130-
TypeReference? thisTypeCast = null;
131131

132132
if (!declaration.IsVirtual)
133133
{ methodAccess = SanitizeIdentifier(emitContext.DllImportName); }
@@ -140,13 +140,15 @@ private void EmitFunctionTrampoline(VisitorContext context, EmitFunctionContext
140140
{ methodAccessFailure = "Class has no vTable pointer."; }
141141
else if (record.VTable is null)
142142
{ methodAccessFailure = "Class has no virtual method table."; }
143+
else if (declaration.Declaration is null)
144+
{ methodAccessFailure = "Virtual method has no associated Clang declaration."; }
143145
else
144146
{
145147
TranslatedVTableEntry? vTableEntry = null;
146148

147149
foreach (TranslatedVTableEntry entry in record.VTable.Entries)
148150
{
149-
if (entry.MethodDeclaration == declaration.Declaration)
151+
if (entry.Info.MethodDeclaration == declaration.Declaration.Handle)
150152
{
151153
vTableEntry = entry;
152154
break;
@@ -156,17 +158,7 @@ private void EmitFunctionTrampoline(VisitorContext context, EmitFunctionContext
156158
if (vTableEntry is null)
157159
{ methodAccessFailure = "Could not find entry in virtual method table."; }
158160
else
159-
{
160-
methodAccess = $"{SanitizeIdentifier(record.VTableField.Name)}->{SanitizeIdentifier(vTableEntry.Name)}";
161-
162-
// Determine if we need to cast the this pointer
163-
// (This happens if a virtual method is lifted from a base type to a child.)
164-
if (vTableEntry.Type is FunctionPointerTypeReference vTableFunctionPointer
165-
&& vTableFunctionPointer.ParameterTypes.Length > 0
166-
&& vTableFunctionPointer.ParameterTypes[0] is PointerTypeReference { Inner: TypeReference vTableThis } vTableThisPointer
167-
&& (vTableThis is not TranslatedTypeReference vTableThisTranslated || !ReferenceEquals(vTableThisTranslated.TryResolve(context.Library), context.ParentDeclaration)))
168-
{ thisTypeCast = vTableThisPointer; }
169-
}
161+
{ methodAccess = $"{SanitizeIdentifier(record.VTableField.Name)}->{SanitizeIdentifier(vTableEntry.Name)}"; }
170162
}
171163
}
172164

@@ -243,7 +235,7 @@ void EmitFunctionBodyWithReturnByReference(EmitFunctionContext emitContext)
243235
Writer.WriteLine($" {SanitizeIdentifier(emitContext.ReturnBufferParameterName)};");
244236

245237
Writer.Write($"{methodAccess}(");
246-
EmitFunctionParameterList(context, emitContext, declaration, EmitParameterListMode.TrampolineArguments, thisTypeCast);
238+
EmitFunctionParameterList(context, emitContext, declaration, EmitParameterListMode.TrampolineArguments);
247239
Writer.WriteLine(");");
248240

249241
Writer.WriteLine($"return {SanitizeIdentifier(emitContext.ReturnBufferParameterName)};");
@@ -268,7 +260,7 @@ void EmitFunctionBodyWithReturnByReference(EmitFunctionContext emitContext)
268260
{ Writer.Write("return "); }
269261

270262
Writer.Write($"{methodAccess}(");
271-
EmitFunctionParameterList(context, emitContext, declaration, EmitParameterListMode.TrampolineArguments, thisTypeCast);
263+
EmitFunctionParameterList(context, emitContext, declaration, EmitParameterListMode.TrampolineArguments);
272264
Writer.Write(");");
273265

274266
if (hasThis)
@@ -277,25 +269,56 @@ void EmitFunctionBodyWithReturnByReference(EmitFunctionContext emitContext)
277269
}
278270
}
279271

272+
private void EmitFunctionPointerForVTable(VisitorContext context, EmitFunctionContext emitContext, TranslatedFunction declaration)
273+
{
274+
string? callingConventionString = declaration.CallingConvention switch
275+
{
276+
CallingConvention.Cdecl => "unmanaged[Cdecl]",
277+
CallingConvention.StdCall => "unmanaged[Stdcall]",
278+
CallingConvention.ThisCall => "unmanaged[Thiscall]",
279+
CallingConvention.FastCall => "unmanaged[Fastcall]",
280+
_ => null
281+
};
282+
283+
if (callingConventionString is null)
284+
{
285+
Fatal(context, declaration, $"The {declaration.CallingConvention} convention is not supported.");
286+
Writer.Write("void*");
287+
return;
288+
}
289+
290+
Writer.Write($"delegate* {callingConventionString}<");
291+
292+
EmitFunctionParameterList(context, emitContext, declaration, EmitParameterListMode.VTableFunctionPointerParameters);
293+
294+
Writer.Write(", ");
295+
296+
if (declaration.ReturnByReference)
297+
{ WriteTypeAsReference(context, declaration, declaration.ReturnType); }
298+
else
299+
{ WriteType(context, declaration, declaration.ReturnType); }
300+
301+
Writer.Write('>');
302+
}
303+
280304
private enum EmitParameterListMode
281305
{
282306
DllImportParameters,
283307
TrampolineParameters,
284308
TrampolineArguments,
309+
VTableFunctionPointerParameters,
285310
}
286311

287-
private void EmitFunctionParameterList(VisitorContext context, EmitFunctionContext emitContext, TranslatedFunction declaration, EmitParameterListMode mode, TypeReference? thisCastType = null)
312+
private void EmitFunctionParameterList(VisitorContext context, EmitFunctionContext emitContext, TranslatedFunction declaration, EmitParameterListMode mode)
288313
{
289-
if (thisCastType is not null && mode != EmitParameterListMode.TrampolineArguments)
290-
{ throw new ArgumentException("Emitting a this cast is only possible for trampoline arguments.", nameof(thisCastType)); }
291-
292314
if (declaration.FunctionAbi is null)
293315
{ throw new ArgumentException("Cannot emit a parameter list for an uncallable function since they lack ABI information.", nameof(declaration)); }
294316

295317
bool first = true;
296318

297-
bool writeImplicitParameters = mode == EmitParameterListMode.DllImportParameters || mode == EmitParameterListMode.TrampolineArguments;
298-
bool writeTypes = mode == EmitParameterListMode.DllImportParameters || mode == EmitParameterListMode.TrampolineParameters;
319+
bool writeImplicitParameters = mode is EmitParameterListMode.DllImportParameters or EmitParameterListMode.TrampolineArguments or EmitParameterListMode.VTableFunctionPointerParameters;
320+
bool writeTypes = mode is EmitParameterListMode.DllImportParameters or EmitParameterListMode.TrampolineParameters or EmitParameterListMode.VTableFunctionPointerParameters;
321+
bool writeNames = mode is EmitParameterListMode.DllImportParameters or EmitParameterListMode.TrampolineArguments or EmitParameterListMode.TrampolineParameters;
299322
bool writeDefautValues = mode switch
300323
{
301324
EmitParameterListMode.DllImportParameters => !declaration.IsInstanceMethod, // We only emit the defaults on the trampoline.
@@ -321,10 +344,18 @@ void WriteOutReturnBuffer(EmitFunctionContext emitContext)
321344
if (writeTypes)
322345
{
323346
WriteType(context, declaration, declaration.ReturnType);
324-
Writer.Write(' ');
347+
348+
//TODO: When fixing https://github.com/InfectedLibraries/Biohazrd/issues/196, use WriteTypeAsReference instead.
349+
if (mode == EmitParameterListMode.VTableFunctionPointerParameters)
350+
{ Writer.Write('*'); }
351+
352+
if (writeNames)
353+
{ Writer.Write(' '); }
325354
}
326355

327-
Writer.WriteIdentifier(emitContext.ReturnBufferParameterName);
356+
if (writeNames)
357+
{ Writer.WriteIdentifier(emitContext.ReturnBufferParameterName); }
358+
328359
first = false;
329360
}
330361

@@ -341,16 +372,14 @@ void WriteOutReturnBuffer(EmitFunctionContext emitContext)
341372
if (writeTypes)
342373
{
343374
WriteType(context, declaration, emitContext.ThisType);
344-
Writer.Write(' ');
345-
}
346-
else if (thisCastType is not null)
347-
{
348-
Writer.Write('(');
349-
WriteType(context, declaration, thisCastType);
350-
Writer.Write(')');
375+
376+
if (writeNames)
377+
{ Writer.Write(' '); }
351378
}
352379

353-
Writer.WriteIdentifier(emitContext.ThisParameterName);
380+
if (writeNames)
381+
{ Writer.WriteIdentifier(emitContext.ThisParameterName); }
382+
354383
first = false;
355384
}
356385

@@ -380,10 +409,12 @@ void WriteOutReturnBuffer(EmitFunctionContext emitContext)
380409
WriteType(parameterContext, parameter, parameter.Type);
381410
}
382411

383-
Writer.Write(' ');
412+
if (writeNames)
413+
{ Writer.Write(' '); }
384414
}
385415

386-
Writer.WriteIdentifier(parameter.Name);
416+
if (writeNames)
417+
{ Writer.WriteIdentifier(parameter.Name); }
387418

388419
if (writeDefautValues && parameter.DefaultValue is not null)
389420
{ Writer.Write($" = {GetConstantAsString(parameterContext, parameter, parameter.DefaultValue, parameter.Type)}"); }

Biohazrd.CSharp/CSharpLibraryGenerator.Records.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Linq;
1+
using ClangSharp;
2+
using System.Linq;
23
using static Biohazrd.CSharp.CSharpCodeWriter;
34

45
namespace Biohazrd.CSharp
@@ -82,11 +83,19 @@ private void EmitVTable(VisitorContext context, TranslatedVTableField field, Tra
8283

8384
// For function pointers, write out the signature of the method as a documentation comment
8485
//TODO: This could/should reference the translated method if there is one.
85-
if (entry.IsFunctionPointer && entry.MethodDeclaration is not null)
86-
{ Writer.WriteLine($"/// <summary>Virtual method pointer for `{entry.MethodDeclaration}`</summary>"); }
86+
if (entry.IsFunctionPointer)
87+
{ Writer.WriteLine($"/// <summary>Virtual method pointer for `{entry.Info.MethodDeclaration}`</summary>"); }
8788

8889
Writer.Write($"{entry.Accessibility.ToCSharpKeyword()} ");
89-
WriteType(context.Add(entry), entry, entry.Type);
90+
91+
if (entry.IsFunctionPointer && entry.MethodReference?.TryResolve(context.Library) is TranslatedFunction associatedFunction)
92+
{
93+
EmitFunctionContext emitContext = new(context, associatedFunction);
94+
EmitFunctionPointerForVTable(context, emitContext, associatedFunction);
95+
}
96+
else
97+
{ WriteType(context.Add(entry), entry, VoidTypeReference.PointerInstance); }
98+
9099
Writer.WriteLine($" {SanitizeIdentifier(entry.Name)};");
91100
}
92101
}
@@ -96,7 +105,7 @@ protected override void VisitVTable(VisitorContext context, TranslatedVTable dec
96105
=> FatalContext(context, declaration, $"w/ {declaration.Entries.Length} entries");
97106

98107
protected override void VisitVTableEntry(VisitorContext context, TranslatedVTableEntry declaration)
99-
=> FatalContext(context, declaration, declaration.MethodDeclaration is not null ? $"({declaration.Info.Kind} to {declaration.MethodDeclaration})" : $"({declaration.Info.Kind})");
108+
=> FatalContext(context, declaration, $"({declaration.Info.Kind} to {declaration.Info.MethodDeclaration})");
100109

101110
protected override void VisitSynthesizedLooseDeclarationsType(VisitorContext context, SynthesizedLooseDeclarationsTypeDeclaration declaration)
102111
{

Biohazrd.Transformation/RawTypeTransformationBase.TransformDeclarationMethods.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,21 +113,5 @@ private TransformationResult TransformTypedefTypeReferences(TransformationContex
113113
else
114114
{ return declaration; }
115115
}
116-
117-
private TransformationResult TransformVTableEntryTypeReferences(TransformationContext context, TranslatedVTableEntry declaration)
118-
{
119-
TypeTransformationResult result = TransformTypeRecursively(context, declaration.Type);
120-
121-
if (result.IsChange(declaration.Type))
122-
{
123-
return declaration with
124-
{
125-
Type = result.TypeReference,
126-
Diagnostics = declaration.Diagnostics.AddRange(result.Diagnostics)
127-
};
128-
}
129-
else
130-
{ return declaration; }
131-
}
132116
}
133117
}

Biohazrd.Transformation/RawTypeTransformationBase.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ protected sealed override TransformationResult Transform(TransformationContext c
4949
TranslatedBaseField baseFieldDeclaration => TransformBaseFieldTypeReferences(context.Add(declaration), baseFieldDeclaration),
5050
TranslatedNormalField normalFieldDeclaration => TransformNormalFieldTypeReferences(context.Add(declaration), normalFieldDeclaration),
5151
TranslatedTypedef typedefDeclaration => TransformTypedefTypeReferences(context.Add(declaration), typedefDeclaration),
52-
TranslatedVTableEntry vTableEntryDeclaration => TransformVTableEntryTypeReferences(context.Add(declaration), vTableEntryDeclaration),
5352
TranslatedDeclaration => declaration
5453
};
5554

Biohazrd.Transformation/__TypeReferenceVisitor.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,6 @@ protected override void VisitTypedef(VisitorContext context, TranslatedTypedef d
4949
base.VisitTypedef(context, declaration);
5050
}
5151

52-
protected override void VisitVTableEntry(VisitorContext context, TranslatedVTableEntry declaration)
53-
{
54-
VisitTypeReference(context, declaration, declaration.Type);
55-
base.VisitVTableEntry(context, declaration);
56-
}
57-
58-
5952
private void VisitTypeReference(VisitorContext parentDeclarationContext, TranslatedDeclaration parentDeclaration, TypeReference typeReference)
6053
=> VisitTypeReference(parentDeclarationContext.Add(parentDeclaration), ImmutableArray<TypeReference>.Empty, typeReference);
6154

Biohazrd/#Declarations/TranslatedVTableEntry.cs

Lines changed: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ public sealed record TranslatedVTableEntry : TranslatedDeclaration
88
public PathogenVTableEntry Info { get; }
99

1010
public bool IsFunctionPointer { get; }
11-
public CXXMethodDecl? MethodDeclaration { get; }
12-
13-
public TypeReference Type { get; init; }
11+
public DeclarationReference? MethodReference { get; init; }
1412

1513
internal TranslatedVTableEntry(TranslationUnitParser parsingContext, TranslatedFile file, PathogenVTableEntry info, string name)
1614
: base(file)
@@ -19,66 +17,19 @@ internal TranslatedVTableEntry(TranslationUnitParser parsingContext, TranslatedF
1917
Accessibility = AccessModifier.Public;
2018

2119
Info = info;
22-
Type = VoidTypeReference.PointerInstance;
2320

2421
IsFunctionPointer = false;
25-
MethodDeclaration = null;
22+
MethodReference = null;
2623

2724
if (Info.Kind.IsFunctionPointerKind())
2825
{
2926
IsFunctionPointer = true;
3027
Cursor methodDeclarationCursor = parsingContext.FindCursor(info.MethodDeclaration);
3128

32-
if (methodDeclarationCursor is CXXMethodDecl { Type: FunctionProtoType functionType } methodDeclaration)
33-
{
34-
MethodDeclaration = methodDeclaration;
35-
36-
FunctionPointerTypeReference functionTypeReference = new(functionType);
37-
38-
// Convert parameters which must be passed by reference into pointers
39-
//HACK: This really shouldn't be done here because it's making an assumption about how the generator will interpret this type.
40-
// Ideally we should be able to encode this some other way.
41-
for (int i = 0; i < functionTypeReference.ParameterTypes.Length; i++)
42-
{
43-
if (functionTypeReference.ParameterTypes[i] is ClangTypeReference clangType && clangType.ClangType.MustBePassedByReference(isForInstanceMethodReturnValue: false))
44-
{
45-
functionTypeReference = functionTypeReference with
46-
{
47-
ParameterTypes = functionTypeReference.ParameterTypes.SetItem(i, new PointerTypeReference(clangType))
48-
};
49-
}
50-
}
51-
52-
//TODO: This depends on the calling convention
53-
// Add the retbuf parameter if necessary
54-
if (functionType.ReturnType.MustBePassedByReference(isForInstanceMethodReturnValue: true))
55-
{
56-
PointerTypeReference returnBufferType = new(functionTypeReference.ReturnType);
57-
58-
functionTypeReference = functionTypeReference with
59-
{
60-
ParameterTypes = functionTypeReference.ParameterTypes.Insert(0, returnBufferType),
61-
ReturnType = returnBufferType
62-
};
63-
}
64-
65-
// Add the this pointer parameter
66-
TypeReference thisPointerType = VoidTypeReference.PointerInstance;
67-
68-
if (methodDeclaration.Parent is RecordDecl recordDeclaration)
69-
{ thisPointerType = new PointerTypeReference(TranslatedTypeReference.Create(recordDeclaration)); }
70-
else
71-
{ Diagnostics = Diagnostics.Add(Severity.Warning, $"Could not figure out this pointer type for {methodDeclaration}."); }
72-
73-
functionTypeReference = functionTypeReference with
74-
{
75-
ParameterTypes = functionTypeReference.ParameterTypes.Insert(0, thisPointerType)
76-
};
77-
78-
Type = functionTypeReference;
79-
}
29+
if (methodDeclarationCursor is CXXMethodDecl methodDeclaration)
30+
{ MethodReference = new DeclarationReference(methodDeclaration); }
8031
else
81-
{ Diagnostics = Diagnostics.Add(Severity.Warning, $"VTable function point did not resolve to a C++ method declaration."); }
32+
{ Diagnostics = Diagnostics.Add(Severity.Warning, $"VTable function pointer resolved to a {methodDeclarationCursor.GetType().Name} rather than a C++ method declaration."); }
8233
}
8334
}
8435

0 commit comments

Comments
 (0)