forked from MochiLibraries/Biohazrd
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathCSharpLibraryGenerator.Records.cs
149 lines (126 loc) · 7.33 KB
/
CSharpLibraryGenerator.Records.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
using Biohazrd.CSharp.Trampolines;
using System.Linq;
using static Biohazrd.CSharp.CSharpCodeWriter;
namespace Biohazrd.CSharp
{
partial class CSharpLibraryGenerator
{
protected override void VisitRecord(VisitorContext context, TranslatedRecord declaration)
{
Writer.Using("System.Runtime.InteropServices");
Writer.EnsureSeparation();
Writer.Write($"[StructLayout(LayoutKind.Explicit, Size = {declaration.Size}");
// Write out CharSet.Unicode if necessary. An explicit charset ensures char fields in this struct are considered blittable by the runtime
// https://github.com/dotnet/runtime/blob/29e9b5b7fd95231d9cd9d3ae351404e63cbb6d5a/src/coreclr/src/vm/fieldmarshaler.cpp#L233-L235
foreach (TranslatedNormalField field in declaration.Members.OfType<TranslatedNormalField>())
{
if (field.Type.IsCSharpType(context.Library, CSharpBuiltinType.Char))
{
Writer.Write(", CharSet = CharSet.Unicode");
break;
}
}
Writer.WriteLine(")]");
Writer.WriteLine($"{declaration.Accessibility.ToCSharpKeyword()} unsafe partial struct {SanitizeIdentifier(declaration.Name)}");
using (Writer.Block())
{
VisitorContext childContext = context.Add(declaration);
// Emit base field
if (declaration.NonVirtualBaseField is not null)
{ VisitBaseField(childContext, declaration.NonVirtualBaseField); }
// Emit normal members
foreach (TranslatedDeclaration member in declaration.Members)
{ Visit(childContext, member); }
// Emit VTable+Field
if (declaration.VTable is not null && declaration.VTableField is not null)
{ EmitVTable(childContext, declaration.VTableField, declaration.VTable); }
// List any unsupported members
if (declaration.UnsupportedMembers.Count > 0)
{
Diagnostics.Add(Severity.Warning, $"Record {declaration.Name} has {declaration.UnsupportedMembers.Count} unsupported members which will not be translated.");
Writer.EnsureSeparation();
Writer.WriteLine("// The following members could not be translated:");
foreach (TranslatedDeclaration unsupportedMember in declaration.UnsupportedMembers)
{ Writer.WriteLine($"// {unsupportedMember.GetType().Name} {unsupportedMember.Name}"); }
}
}
}
private void EmitVTable(VisitorContext context, TranslatedVTableField field, TranslatedVTable vTable)
{
// Emit the VTable field
Writer.EnsureSeparation();
StartField(field);
Writer.WriteLine($"{SanitizeIdentifier(vTable.Name)}* {SanitizeIdentifier(field.Name)};");
// Emit the VTable type
Writer.EnsureSeparation();
Writer.Using("System.Runtime.InteropServices");
Writer.WriteLine("[StructLayout(LayoutKind.Sequential)]");
Writer.WriteLine($"{vTable.Accessibility.ToCSharpKeyword()} unsafe struct {SanitizeIdentifier(vTable.Name)}");
using (Writer.Block())
{
bool foundFirstFunctionPointer = false;
foreach (TranslatedVTableEntry entry in vTable.Entries)
{
// If this entry is a function pointer kind, we can start writing
// (VTables have non-vtable stuff like RTTI before the vtable pointer, we don't want to translate those.)
if (entry.IsFunctionPointer)
{ foundFirstFunctionPointer = true; }
// If we haven't found a function pointer yet, we don't want to start writing
if (!foundFirstFunctionPointer)
{ continue; }
// 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)
{ Writer.WriteLine($"/// <summary>Virtual method pointer for `{entry.Info.MethodDeclaration}`</summary>"); }
Writer.Write($"{entry.Accessibility.ToCSharpKeyword()} ");
TranslatedFunction? associatedFunction = null;
// Prefer methods which are a sibling to the vtable
// (This is a workaround for https://github.com/MochiLibraries/Biohazrd/issues/239)
if (entry.MethodReference is DeclarationReference methodReference && context.ParentDeclaration is TranslatedRecord record)
{
foreach (TranslatedDeclaration sibling in record)
{
if (sibling is TranslatedFunction function && methodReference.__HACK__CouldResolveTo(function))
{
associatedFunction = function;
break;
}
}
}
// If we didn't find it as a sibling search the entire library
associatedFunction ??= entry.MethodReference?.TryResolve(context.Library) as TranslatedFunction;
if (entry.IsFunctionPointer && associatedFunction is not null)
{
if (associatedFunction.Metadata.TryGet(out TrampolineCollection trampolines))
{
trampolines.NativeFunction.EmitFunctionPointer(this, context, associatedFunction, Writer);
}
else
{
EmitFunctionContext emitContext = new(context, associatedFunction);
EmitFunctionPointerForVTable(context, emitContext, associatedFunction);
}
}
else
{ WriteType(context.Add(entry), entry, VoidTypeReference.PointerInstance); }
Writer.WriteLine($" {SanitizeIdentifier(entry.Name)};");
}
}
}
protected override void VisitVTable(VisitorContext context, TranslatedVTable declaration)
=> FatalContext(context, declaration, $"w/ {declaration.Entries.Length} entries");
protected override void VisitVTableEntry(VisitorContext context, TranslatedVTableEntry declaration)
=> FatalContext(context, declaration, $"({declaration.Info.Kind} to {declaration.Info.MethodDeclaration})");
protected override void VisitSynthesizedLooseDeclarationsType(VisitorContext context, SynthesizedLooseDeclarationsTypeDeclaration declaration)
{
Writer.EnsureSeparation();
Writer.WriteLine($"{declaration.Accessibility.ToCSharpKeyword()} unsafe static partial class {SanitizeIdentifier(declaration.Name)}");
using (Writer.Block())
{
context = context.Add(declaration);
foreach (TranslatedDeclaration member in declaration.Members)
{ Visit(context, member); }
}
}
}
}