Skip to content

Commit a92d8da

Browse files
committed
Fixed NativeBoolean/NativeChar no longer being used for vtable function pointers.
Related: #147 Related: #200
1 parent 6e462ec commit a92d8da

4 files changed

+53
-4
lines changed

Biohazrd.CSharp/#Transformations/CSharpTranslationVerifier.cs

+5
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ static bool TypeCanHaveDefaultInCSharp(TranslatedLibrary library, TypeReference
229229
return TypeCanHaveDefaultInCSharp(library, typedef.UnderlyingType);
230230
case TranslatedEnum:
231231
return true;
232+
// NativeBoolean and NativeChar will become bool/char on the trampoline surface and should not appear in places where trampolines aren't generated since
233+
// they end up using MarshalAs instead.
234+
case NativeBooleanDeclaration:
235+
case NativeCharDeclaration:
236+
return true;
232237
}
233238
}
234239

Biohazrd.CSharp/#Transformations/WrapNonBlittableTypesWhereNecessaryTransformation.cs

+10-1
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,17 @@ protected override TypeTransformationResult TransformCSharpBuiltinTypeReference(
6666
if (context.Parent is PointerTypeReference)
6767
{ return type; }
6868

69+
// If the type is for a virtual method return value, we always wrap non-blittable types since these types will be used for the vtable pointer
70+
// See https://github.com/InfectedLibraries/Biohazrd/issues/200 for details
71+
if (context.ParentDeclaration is TranslatedFunction { IsVirtual: true })
72+
{ }
73+
// Same for parameters of virtual methods
74+
//TODO: C# 10: Use list pattern syntax: context.ParentDeclarations is [.., TranslatedParameter, TranslatedFunction { IsVirtual: True }]
75+
else if (context.ParentDeclaration is TranslatedParameter
76+
&& context.ParentDeclarations.Length >= 2 && context.ParentDeclarations[context.ParentDeclarations.Length - 2] is TranslatedFunction { IsVirtual: true })
77+
{ }
6978
// If the parent type reference isn't a function pointer, Blittablebool/BlittableChar should not be necessary
70-
if (!context.Parents.Any(t => t is FunctionPointerTypeReference))
79+
else if (!context.Parents.Any(t => t is FunctionPointerTypeReference))
7180
{ return type; }
7281

7382
if (type.Type == CSharpBuiltinType.Bool)

Biohazrd.CSharp/CSharpLibraryGenerator.Constants.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,19 @@ private string GetConstantAsString(VisitorContext context, TranslatedDeclaration
1818
case IntegerConstant integerConstant:
1919
{
2020
// Bools come through as integer types
21-
if (targetType is CSharpBuiltinTypeReference cSharpTypeReference && cSharpTypeReference.Type == CSharpBuiltinType.Bool)
21+
static bool IsBooleanType(VisitorContext context, TypeReference targetType)
22+
{
23+
if (targetType is CSharpBuiltinTypeReference cSharpTypeReference && cSharpTypeReference.Type == CSharpBuiltinType.Bool)
24+
{ return true; }
25+
// NativeBoolean is used for virtual methods which end up getting wrapped by trampolines which use bool so emitting them as true/false contants are valid in that context
26+
// See https://github.com/InfectedLibraries/Biohazrd/issues/200 for details.
27+
else if (targetType is TranslatedTypeReference translatedTypeReference && translatedTypeReference.TryResolve(context.Library) is NativeBooleanDeclaration)
28+
{ return true; }
29+
else
30+
{ return false; }
31+
}
32+
33+
if (IsBooleanType(context, targetType))
2234
{
2335
if (integerConstant.Value == 0)
2436
{ return "false"; }

Biohazrd.CSharp/CSharpLibraryGenerator.Functions.cs

+25-2
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ private void EmitFunctionTrampoline(VisitorContext context, EmitFunctionContext
189189
Writer.Write($"{declaration.Accessibility.ToCSharpKeyword()} unsafe ");
190190
if (!declaration.IsInstanceMethod)
191191
{ Writer.Write("static "); }
192-
WriteType(context, declaration, declaration.ReturnType);
192+
WriteTypeForTrampoline(context, declaration, declaration.ReturnType);
193193
Writer.Write($" {SanitizeIdentifier(declaration.Name)}(");
194194
EmitFunctionParameterList(context, emitContext, declaration, EmitParameterListMode.TrampolineParameters);
195195
Writer.WriteLine(')');
@@ -406,7 +406,10 @@ void WriteOutReturnBuffer(EmitFunctionContext emitContext)
406406
if (mode == EmitParameterListMode.DllImportParameters && parameter.Type.IsCSharpType(CSharpBuiltinType.Bool))
407407
{ Writer.Write("[MarshalAs(UnmanagedType.I1)] "); }
408408

409-
WriteType(parameterContext, parameter, parameter.Type);
409+
if (mode == EmitParameterListMode.TrampolineParameters)
410+
{ WriteTypeForTrampoline(parameterContext, parameter, parameter.Type); }
411+
else
412+
{ WriteType(parameterContext, parameter, parameter.Type); }
410413
}
411414

412415
if (writeNames)
@@ -469,6 +472,26 @@ private void EmitMethodImplAttribute(TranslatedFunction declaration)
469472
Writer.WriteLine(")]");
470473
}
471474

475+
private void WriteTypeForTrampoline(VisitorContext context, TranslatedDeclaration declaration, TypeReference type)
476+
{
477+
// For trampolines we want to hide the semantics of NativeBoolean/NativeChar so we silently replace them with their actual C# type
478+
// See https://github.com/InfectedLibraries/Biohazrd/issues/200 for details.
479+
if (type is TranslatedTypeReference typeReference)
480+
{
481+
switch (typeReference.TryResolve(context.Library))
482+
{
483+
case NativeBooleanDeclaration:
484+
WriteType(context, declaration, CSharpBuiltinType.Bool);
485+
return;
486+
case NativeCharDeclaration:
487+
WriteType(context, declaration, CSharpBuiltinType.Char);
488+
return;
489+
}
490+
}
491+
492+
WriteType(context, declaration, type);
493+
}
494+
472495
protected override void VisitParameter(VisitorContext context, TranslatedParameter declaration)
473496
=> FatalContext(context, declaration);
474497
}

0 commit comments

Comments
 (0)