Skip to content

Commit d6ffc9c

Browse files
committed
Progress
1 parent 61dd272 commit d6ffc9c

File tree

6 files changed

+107
-27
lines changed

6 files changed

+107
-27
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using Biohazrd.CSharp.Infrastructure;
2+
using Biohazrd.Transformation;
3+
using Biohazrd.Transformation.Infrastructure;
4+
using System;
5+
6+
namespace Biohazrd.CSharp;
7+
8+
public sealed record ByRefTypeReference : TypeReference, ICustomTypeReference, ICustomCSharpTypeReference
9+
{
10+
private ByRefKind _Kind;
11+
public ByRefKind Kind
12+
{
13+
get => _Kind;
14+
init
15+
{
16+
if (!Enum.IsDefined(value))
17+
{ throw new ArgumentOutOfRangeException(nameof(value)); }
18+
19+
_Kind = value;
20+
}
21+
}
22+
23+
public TypeReference Inner { get; init; }
24+
25+
public string Keyword => Kind.GetKeyword();
26+
27+
public ByRefTypeReference(ByRefKind kind, TypeReference inner)
28+
{
29+
if (!Enum.IsDefined(kind))
30+
{ throw new ArgumentOutOfRangeException(nameof(kind)); }
31+
32+
_Kind = kind;
33+
Inner = inner;
34+
}
35+
36+
TypeTransformationResult ICustomTypeReference.TransformChildren(ITypeTransformation transformation, TypeTransformationContext context)
37+
{
38+
DiagnosticAccumulator diagnostics = new();
39+
SingleTypeTransformHelper newInnerType = new(Inner, ref diagnostics);
40+
41+
// Transform inner type
42+
newInnerType.SetValue(transformation.TransformTypeRecursively(context, Inner));
43+
44+
// Create the result
45+
TypeTransformationResult result = newInnerType.WasChanged ? this with { Inner = newInnerType.NewValue } : this;
46+
result.AddDiagnostics(diagnostics.MoveToImmutable());
47+
return result;
48+
}
49+
50+
string ICustomCSharpTypeReference.GetTypeAsString(ICSharpOutputGenerator outputGenerator, VisitorContext context, TranslatedDeclaration declaration)
51+
=> $"{Keyword} {outputGenerator.GetTypeAsString(context, declaration, Inner)}";
52+
53+
public override string ToString()
54+
=> $"{Keyword} {Inner}";
55+
}

Biohazrd.CSharp/BiohzardExtensions.cs

+12
Original file line numberDiff line numberDiff line change
@@ -99,5 +99,17 @@ internal static ParameterOutputMode GetParameterOutputMode(this TranslatedParame
9999

100100
public static Trampoline GetPrimaryTrampoline(this TranslatedFunction function)
101101
=> function.TryGetPrimaryTrampoline() ?? throw new InvalidOperationException("Tried to get the primary trampoline of a function with no trampoline metadata.");
102+
103+
public static TranslatedFunction WithSecondaryTrampoline(this TranslatedFunction function, Trampoline secondaryTrampoline)
104+
{
105+
if (!function.Metadata.TryGet(out TrampolineCollection trampolines))
106+
{ throw new InvalidOperationException("Cannot add a secondary trampoline to a function which has no trampolines."); }
107+
108+
trampolines = trampolines.WithTrampoline(secondaryTrampoline);
109+
return function with
110+
{
111+
Metadata = function.Metadata.Set(trampolines)
112+
};
113+
}
102114
}
103115
}

Biohazrd.CSharp/ByRefKind.cs

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
62

73
namespace Biohazrd.CSharp;
84

@@ -12,3 +8,15 @@ public enum ByRefKind
128
In,
139
Out
1410
}
11+
12+
public static class ByRefKindExtensions
13+
{
14+
public static string GetKeyword(this ByRefKind kind)
15+
=> kind switch
16+
{
17+
ByRefKind.Ref => "ref",
18+
ByRefKind.In => "in",
19+
ByRefKind.Out => "out",
20+
_ => throw new ArgumentOutOfRangeException(nameof(kind))
21+
};
22+
}

Biohazrd.CSharp/Trampolines/ByRefAdapter.cs

+1-17
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,14 @@ public ByRefAdapter(Adapter target, ByRefKind kind)
2222
{ throw new ArgumentOutOfRangeException(nameof(kind)); }
2323

2424
OutputType = pointerType;
25-
InputType = pointerType.Inner; //TODO: This is somewhat misleading because it's actually ByRef
25+
InputType = new ByRefTypeReference(kind, pointerType.Inner);
2626
ParameterName = target.ParameterName;
2727
TemporaryName = $"__{ParameterName}P";
2828
Kind = kind;
2929
}
3030

3131
public override void WriteInputParameter(TrampolineContext context, CSharpCodeWriter writer, bool emitDefaultValue)
3232
{
33-
switch (Kind)
34-
{
35-
case ByRefKind.In:
36-
writer.Write("in ");
37-
break;
38-
case ByRefKind.Out:
39-
writer.Write("out ");
40-
break;
41-
case ByRefKind.Ref:
42-
writer.Write("ref ");
43-
break;
44-
default:
45-
Debug.Fail("Invalid byref kind!");
46-
break;
47-
}
48-
4933
context.WriteType(InputType);
5034
writer.Write(' ');
5135
writer.WriteIdentifier(ParameterName);

Biohazrd.CSharp/Trampolines/PassthroughAdapter.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,15 @@ public override bool WriteBlockBeforeCall(TrampolineContext context, CSharpCodeW
4040
=> false;
4141

4242
public override void WriteOutputArgument(TrampolineContext context, CSharpCodeWriter writer)
43-
=> writer.WriteIdentifier(ParameterName);
43+
{
44+
if (InputType is ByRefTypeReference byRefType)
45+
{
46+
// In theory we don't have to do this for `in` parameters, but C# allows you to overload between byref in and by value.
47+
// Detecting if this was done is more effort than it's wroth to save 3 characters, so we write out an explicit `in` keyword to ensure the correct overload is chosen.
48+
writer.Write(byRefType.Kind.GetKeyword());
49+
writer.Write(' ');
50+
}
51+
52+
writer.WriteIdentifier(ParameterName);
53+
}
4454
}

Biohazrd.CSharp/Trampolines/Trampoline.cs

+16-5
Original file line numberDiff line numberDiff line change
@@ -49,23 +49,34 @@ internal Trampoline(TrampolineBuilder builder)
4949
{ ReturnAdapter = builder.ReturnAdapter; }
5050
else if (template?.ReturnAdapter is not null)
5151
{ ReturnAdapter = template.ReturnAdapter; }
52-
else if (Target.ReturnAdapter is VoidReturnAdapter)
52+
else if (Target.ReturnAdapter.OutputType is VoidTypeReference)
5353
{ ReturnAdapter = VoidReturnAdapter.Instance; }
5454
else
5555
{ ReturnAdapter = new PassthroughReturnAdapter(Target.ReturnAdapter); }
5656

5757
// Add parameter adapters
58-
int expectedLength = template is not null ? template.Adapters.Length : Target.Adapters.Length;
58+
int expectedLength;
59+
if (template is not null)
60+
{ expectedLength = template.Adapters.Length; }
61+
else
62+
{
63+
expectedLength = 0;
64+
foreach (Adapter adapter in Target.Adapters)
65+
{
66+
if (adapter.AcceptsInput)
67+
{ expectedLength++; }
68+
}
69+
}
5970
ImmutableArray<Adapter>.Builder adapters = ImmutableArray.CreateBuilder<Adapter>(expectedLength);
6071

6172
foreach (Adapter targetAdapter in template?.Adapters ?? Target.Adapters)
6273
{
6374
// If the builder adapted this adapter, insert its adapter
6475
if (builder.Adapters?.TryGetValue(targetAdapter, out Adapter? adapter) ?? false)
65-
{ adapters.Add(adapter); }
76+
{ adapters.Add(adapter); } //TODO: Debug.Assert(template is not null || targetAdapter.AcceptsInput); ? Is it enough this is done in Adapter's constructor? (Probably.)
6677
else if (builder.TargetIsTemplate)
6778
{ adapters.Add(targetAdapter); }
68-
else
79+
else if (targetAdapter.AcceptsInput)
6980
{ adapters.Add(new PassthroughAdapter(targetAdapter)); }
7081
}
7182

@@ -109,7 +120,7 @@ internal void Write(ICSharpOutputGenerator outputGenerator, VisitorContext conte
109120
string? virtualMethodAccess = null;
110121
string? virtualMethodAccessFailure = null;
111122

112-
if (declaration.IsVirtual)
123+
if (declaration.IsVirtual && Target is not null && Target.IsNativeFunction)
113124
{
114125
// Figure out how to access the VTable entry
115126
if (context.ParentDeclaration is not TranslatedRecord record)

0 commit comments

Comments
 (0)