Skip to content

Commit

Permalink
Removed type reference and ABI handling from TranslatedVTableEntry an…
Browse files Browse the repository at this point in the history
…d 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.)
  • Loading branch information
PathogenDavid committed Jul 4, 2021
1 parent 59fdbd1 commit 8abd033
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 125 deletions.
97 changes: 64 additions & 33 deletions Biohazrd.CSharp/CSharpLibraryGenerator.Functions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static Biohazrd.CSharp.CSharpCodeWriter;

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

if (!declaration.IsVirtual)
{ methodAccess = SanitizeIdentifier(emitContext.DllImportName); }
Expand All @@ -140,13 +140,15 @@ private void EmitFunctionTrampoline(VisitorContext context, EmitFunctionContext
{ methodAccessFailure = "Class has no vTable pointer."; }
else if (record.VTable is null)
{ methodAccessFailure = "Class has no virtual method table."; }
else if (declaration.Declaration is null)
{ methodAccessFailure = "Virtual method has no associated Clang declaration."; }
else
{
TranslatedVTableEntry? vTableEntry = null;

foreach (TranslatedVTableEntry entry in record.VTable.Entries)
{
if (entry.MethodDeclaration == declaration.Declaration)
if (entry.Info.MethodDeclaration == declaration.Declaration.Handle)
{
vTableEntry = entry;
break;
Expand All @@ -156,17 +158,7 @@ private void EmitFunctionTrampoline(VisitorContext context, EmitFunctionContext
if (vTableEntry is null)
{ methodAccessFailure = "Could not find entry in virtual method table."; }
else
{
methodAccess = $"{SanitizeIdentifier(record.VTableField.Name)}->{SanitizeIdentifier(vTableEntry.Name)}";

// Determine if we need to cast the this pointer
// (This happens if a virtual method is lifted from a base type to a child.)
if (vTableEntry.Type is FunctionPointerTypeReference vTableFunctionPointer
&& vTableFunctionPointer.ParameterTypes.Length > 0
&& vTableFunctionPointer.ParameterTypes[0] is PointerTypeReference { Inner: TypeReference vTableThis } vTableThisPointer
&& (vTableThis is not TranslatedTypeReference vTableThisTranslated || !ReferenceEquals(vTableThisTranslated.TryResolve(context.Library), context.ParentDeclaration)))
{ thisTypeCast = vTableThisPointer; }
}
{ methodAccess = $"{SanitizeIdentifier(record.VTableField.Name)}->{SanitizeIdentifier(vTableEntry.Name)}"; }
}
}

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

Writer.Write($"{methodAccess}(");
EmitFunctionParameterList(context, emitContext, declaration, EmitParameterListMode.TrampolineArguments, thisTypeCast);
EmitFunctionParameterList(context, emitContext, declaration, EmitParameterListMode.TrampolineArguments);
Writer.WriteLine(");");

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

Writer.Write($"{methodAccess}(");
EmitFunctionParameterList(context, emitContext, declaration, EmitParameterListMode.TrampolineArguments, thisTypeCast);
EmitFunctionParameterList(context, emitContext, declaration, EmitParameterListMode.TrampolineArguments);
Writer.Write(");");

if (hasThis)
Expand All @@ -277,25 +269,56 @@ void EmitFunctionBodyWithReturnByReference(EmitFunctionContext emitContext)
}
}

private void EmitFunctionPointerForVTable(VisitorContext context, EmitFunctionContext emitContext, TranslatedFunction declaration)
{
string? callingConventionString = declaration.CallingConvention switch
{
CallingConvention.Cdecl => "unmanaged[Cdecl]",
CallingConvention.StdCall => "unmanaged[Stdcall]",
CallingConvention.ThisCall => "unmanaged[Thiscall]",
CallingConvention.FastCall => "unmanaged[Fastcall]",
_ => null
};

if (callingConventionString is null)
{
Fatal(context, declaration, $"The {declaration.CallingConvention} convention is not supported.");
Writer.Write("void*");
return;
}

Writer.Write($"delegate* {callingConventionString}<");

EmitFunctionParameterList(context, emitContext, declaration, EmitParameterListMode.VTableFunctionPointerParameters);

Writer.Write(", ");

if (declaration.ReturnByReference)
{ WriteTypeAsReference(context, declaration, declaration.ReturnType); }
else
{ WriteType(context, declaration, declaration.ReturnType); }

Writer.Write('>');
}

private enum EmitParameterListMode
{
DllImportParameters,
TrampolineParameters,
TrampolineArguments,
VTableFunctionPointerParameters,
}

private void EmitFunctionParameterList(VisitorContext context, EmitFunctionContext emitContext, TranslatedFunction declaration, EmitParameterListMode mode, TypeReference? thisCastType = null)
private void EmitFunctionParameterList(VisitorContext context, EmitFunctionContext emitContext, TranslatedFunction declaration, EmitParameterListMode mode)
{
if (thisCastType is not null && mode != EmitParameterListMode.TrampolineArguments)
{ throw new ArgumentException("Emitting a this cast is only possible for trampoline arguments.", nameof(thisCastType)); }

if (declaration.FunctionAbi is null)
{ throw new ArgumentException("Cannot emit a parameter list for an uncallable function since they lack ABI information.", nameof(declaration)); }

bool first = true;

bool writeImplicitParameters = mode == EmitParameterListMode.DllImportParameters || mode == EmitParameterListMode.TrampolineArguments;
bool writeTypes = mode == EmitParameterListMode.DllImportParameters || mode == EmitParameterListMode.TrampolineParameters;
bool writeImplicitParameters = mode is EmitParameterListMode.DllImportParameters or EmitParameterListMode.TrampolineArguments or EmitParameterListMode.VTableFunctionPointerParameters;
bool writeTypes = mode is EmitParameterListMode.DllImportParameters or EmitParameterListMode.TrampolineParameters or EmitParameterListMode.VTableFunctionPointerParameters;
bool writeNames = mode is EmitParameterListMode.DllImportParameters or EmitParameterListMode.TrampolineArguments or EmitParameterListMode.TrampolineParameters;
bool writeDefautValues = mode switch
{
EmitParameterListMode.DllImportParameters => !declaration.IsInstanceMethod, // We only emit the defaults on the trampoline.
Expand All @@ -321,10 +344,18 @@ void WriteOutReturnBuffer(EmitFunctionContext emitContext)
if (writeTypes)
{
WriteType(context, declaration, declaration.ReturnType);
Writer.Write(' ');

//TODO: When fixing https://github.com/InfectedLibraries/Biohazrd/issues/196, use WriteTypeAsReference instead.
if (mode == EmitParameterListMode.VTableFunctionPointerParameters)
{ Writer.Write('*'); }

if (writeNames)
{ Writer.Write(' '); }
}

Writer.WriteIdentifier(emitContext.ReturnBufferParameterName);
if (writeNames)
{ Writer.WriteIdentifier(emitContext.ReturnBufferParameterName); }

first = false;
}

Expand All @@ -341,16 +372,14 @@ void WriteOutReturnBuffer(EmitFunctionContext emitContext)
if (writeTypes)
{
WriteType(context, declaration, emitContext.ThisType);
Writer.Write(' ');
}
else if (thisCastType is not null)
{
Writer.Write('(');
WriteType(context, declaration, thisCastType);
Writer.Write(')');

if (writeNames)
{ Writer.Write(' '); }
}

Writer.WriteIdentifier(emitContext.ThisParameterName);
if (writeNames)
{ Writer.WriteIdentifier(emitContext.ThisParameterName); }

first = false;
}

Expand Down Expand Up @@ -380,10 +409,12 @@ void WriteOutReturnBuffer(EmitFunctionContext emitContext)
WriteType(parameterContext, parameter, parameter.Type);
}

Writer.Write(' ');
if (writeNames)
{ Writer.Write(' '); }
}

Writer.WriteIdentifier(parameter.Name);
if (writeNames)
{ Writer.WriteIdentifier(parameter.Name); }

if (writeDefautValues && parameter.DefaultValue is not null)
{ Writer.Write($" = {GetConstantAsString(parameterContext, parameter, parameter.DefaultValue, parameter.Type)}"); }
Expand Down
19 changes: 14 additions & 5 deletions Biohazrd.CSharp/CSharpLibraryGenerator.Records.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using ClangSharp;
using System.Linq;
using static Biohazrd.CSharp.CSharpCodeWriter;

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

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

Writer.Write($"{entry.Accessibility.ToCSharpKeyword()} ");
WriteType(context.Add(entry), entry, entry.Type);

if (entry.IsFunctionPointer && entry.MethodReference?.TryResolve(context.Library) is TranslatedFunction associatedFunction)
{
EmitFunctionContext emitContext = new(context, associatedFunction);
EmitFunctionPointerForVTable(context, emitContext, associatedFunction);
}
else
{ WriteType(context.Add(entry), entry, VoidTypeReference.PointerInstance); }

Writer.WriteLine($" {SanitizeIdentifier(entry.Name)};");
}
}
Expand All @@ -96,7 +105,7 @@ protected override void VisitVTable(VisitorContext context, TranslatedVTable dec
=> FatalContext(context, declaration, $"w/ {declaration.Entries.Length} entries");

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

protected override void VisitSynthesizedLooseDeclarationsType(VisitorContext context, SynthesizedLooseDeclarationsTypeDeclaration declaration)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,21 +113,5 @@ private TransformationResult TransformTypedefTypeReferences(TransformationContex
else
{ return declaration; }
}

private TransformationResult TransformVTableEntryTypeReferences(TransformationContext context, TranslatedVTableEntry declaration)
{
TypeTransformationResult result = TransformTypeRecursively(context, declaration.Type);

if (result.IsChange(declaration.Type))
{
return declaration with
{
Type = result.TypeReference,
Diagnostics = declaration.Diagnostics.AddRange(result.Diagnostics)
};
}
else
{ return declaration; }
}
}
}
1 change: 0 additions & 1 deletion Biohazrd.Transformation/RawTypeTransformationBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ protected sealed override TransformationResult Transform(TransformationContext c
TranslatedBaseField baseFieldDeclaration => TransformBaseFieldTypeReferences(context.Add(declaration), baseFieldDeclaration),
TranslatedNormalField normalFieldDeclaration => TransformNormalFieldTypeReferences(context.Add(declaration), normalFieldDeclaration),
TranslatedTypedef typedefDeclaration => TransformTypedefTypeReferences(context.Add(declaration), typedefDeclaration),
TranslatedVTableEntry vTableEntryDeclaration => TransformVTableEntryTypeReferences(context.Add(declaration), vTableEntryDeclaration),
TranslatedDeclaration => declaration
};

Expand Down
7 changes: 0 additions & 7 deletions Biohazrd.Transformation/__TypeReferenceVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,6 @@ protected override void VisitTypedef(VisitorContext context, TranslatedTypedef d
base.VisitTypedef(context, declaration);
}

protected override void VisitVTableEntry(VisitorContext context, TranslatedVTableEntry declaration)
{
VisitTypeReference(context, declaration, declaration.Type);
base.VisitVTableEntry(context, declaration);
}


private void VisitTypeReference(VisitorContext parentDeclarationContext, TranslatedDeclaration parentDeclaration, TypeReference typeReference)
=> VisitTypeReference(parentDeclarationContext.Add(parentDeclaration), ImmutableArray<TypeReference>.Empty, typeReference);

Expand Down
59 changes: 5 additions & 54 deletions Biohazrd/#Declarations/TranslatedVTableEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ public sealed record TranslatedVTableEntry : TranslatedDeclaration
public PathogenVTableEntry Info { get; }

public bool IsFunctionPointer { get; }
public CXXMethodDecl? MethodDeclaration { get; }

public TypeReference Type { get; init; }
public DeclarationReference? MethodReference { get; init; }

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

Info = info;
Type = VoidTypeReference.PointerInstance;

IsFunctionPointer = false;
MethodDeclaration = null;
MethodReference = null;

if (Info.Kind.IsFunctionPointerKind())
{
IsFunctionPointer = true;
Cursor methodDeclarationCursor = parsingContext.FindCursor(info.MethodDeclaration);

if (methodDeclarationCursor is CXXMethodDecl { Type: FunctionProtoType functionType } methodDeclaration)
{
MethodDeclaration = methodDeclaration;

FunctionPointerTypeReference functionTypeReference = new(functionType);

// Convert parameters which must be passed by reference into pointers
//HACK: This really shouldn't be done here because it's making an assumption about how the generator will interpret this type.
// Ideally we should be able to encode this some other way.
for (int i = 0; i < functionTypeReference.ParameterTypes.Length; i++)
{
if (functionTypeReference.ParameterTypes[i] is ClangTypeReference clangType && clangType.ClangType.MustBePassedByReference(isForInstanceMethodReturnValue: false))
{
functionTypeReference = functionTypeReference with
{
ParameterTypes = functionTypeReference.ParameterTypes.SetItem(i, new PointerTypeReference(clangType))
};
}
}

//TODO: This depends on the calling convention
// Add the retbuf parameter if necessary
if (functionType.ReturnType.MustBePassedByReference(isForInstanceMethodReturnValue: true))
{
PointerTypeReference returnBufferType = new(functionTypeReference.ReturnType);

functionTypeReference = functionTypeReference with
{
ParameterTypes = functionTypeReference.ParameterTypes.Insert(0, returnBufferType),
ReturnType = returnBufferType
};
}

// Add the this pointer parameter
TypeReference thisPointerType = VoidTypeReference.PointerInstance;

if (methodDeclaration.Parent is RecordDecl recordDeclaration)
{ thisPointerType = new PointerTypeReference(TranslatedTypeReference.Create(recordDeclaration)); }
else
{ Diagnostics = Diagnostics.Add(Severity.Warning, $"Could not figure out this pointer type for {methodDeclaration}."); }

functionTypeReference = functionTypeReference with
{
ParameterTypes = functionTypeReference.ParameterTypes.Insert(0, thisPointerType)
};

Type = functionTypeReference;
}
if (methodDeclarationCursor is CXXMethodDecl methodDeclaration)
{ MethodReference = new DeclarationReference(methodDeclaration); }
else
{ Diagnostics = Diagnostics.Add(Severity.Warning, $"VTable function point did not resolve to a C++ method declaration."); }
{ Diagnostics = Diagnostics.Add(Severity.Warning, $"VTable function pointer resolved to a {methodDeclarationCursor.GetType().Name} rather than a C++ method declaration."); }
}
}

Expand Down
Loading

0 comments on commit 8abd033

Please sign in to comment.