diff --git a/Cesium.CodeGen.Tests/CodeGenNetInteropTests.cs b/Cesium.CodeGen.Tests/CodeGenNetInteropTests.cs index be2efc0c..c63c2030 100644 --- a/Cesium.CodeGen.Tests/CodeGenNetInteropTests.cs +++ b/Cesium.CodeGen.Tests/CodeGenNetInteropTests.cs @@ -95,7 +95,7 @@ public Task FuncPtrInterop(TargetArchitectureSet architecture) => DoTest( @"using Cesium.Runtime; public static class Test { - public static int Func(FPtr> ptr) => 1; + public static int Func(FuncPtr> ptr) => 1; } ", """ __cli_import("Test::Func") diff --git a/Cesium.CodeGen.Tests/verified/CodeGenNetInteropTests.FuncPtrInterop_architecture=Dynamic.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenNetInteropTests.FuncPtrInterop_architecture=Dynamic.verified.txt new file mode 100644 index 00000000..d58cb886 --- /dev/null +++ b/Cesium.CodeGen.Tests/verified/CodeGenNetInteropTests.FuncPtrInterop_architecture=Dynamic.verified.txt @@ -0,0 +1,23 @@ +Module: Primary + Type: + Methods: + System.Int32 ::myFunc() + IL_0000: ldc.i4.0 + IL_0001: ret + + System.Int32 ::main() + IL_0000: ldftn System.Int32 ::myFunc() + IL_0006: conv.i + IL_0007: newobj Cesium.Runtime.FuncPtr`1 Cesium.Runtime.FuncPtr`1>::.ctor(System.Void*) + IL_000c: call System.Int32 Test::Func(Cesium.Runtime.FuncPtr`1>) + IL_0011: ret + + System.Int32 ::() + Locals: + System.Int32 V_0 + IL_0000: call System.Int32 ::main() + IL_0005: stloc.s V_0 + IL_0007: ldloc.s V_0 + IL_0009: call System.Void Cesium.Runtime.RuntimeHelpers::Exit(System.Int32) + IL_000e: ldloc.s V_0 + IL_0010: ret diff --git a/Cesium.CodeGen.Tests/verified/CodeGenNetInteropTests.FuncPtrInterop_architecture=Wide.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenNetInteropTests.FuncPtrInterop_architecture=Wide.verified.txt new file mode 100644 index 00000000..d58cb886 --- /dev/null +++ b/Cesium.CodeGen.Tests/verified/CodeGenNetInteropTests.FuncPtrInterop_architecture=Wide.verified.txt @@ -0,0 +1,23 @@ +Module: Primary + Type: + Methods: + System.Int32 ::myFunc() + IL_0000: ldc.i4.0 + IL_0001: ret + + System.Int32 ::main() + IL_0000: ldftn System.Int32 ::myFunc() + IL_0006: conv.i + IL_0007: newobj Cesium.Runtime.FuncPtr`1 Cesium.Runtime.FuncPtr`1>::.ctor(System.Void*) + IL_000c: call System.Int32 Test::Func(Cesium.Runtime.FuncPtr`1>) + IL_0011: ret + + System.Int32 ::() + Locals: + System.Int32 V_0 + IL_0000: call System.Int32 ::main() + IL_0005: stloc.s V_0 + IL_0007: ldloc.s V_0 + IL_0009: call System.Void Cesium.Runtime.RuntimeHelpers::Exit(System.Int32) + IL_000e: ldloc.s V_0 + IL_0010: ret diff --git a/Cesium.CodeGen/Contexts/AssemblyContext.cs b/Cesium.CodeGen/Contexts/AssemblyContext.cs index bf90bc4c..365443c0 100644 --- a/Cesium.CodeGen/Contexts/AssemblyContext.cs +++ b/Cesium.CodeGen/Contexts/AssemblyContext.cs @@ -82,11 +82,14 @@ public MethodReference CPtrConverter(TypeReference argument) => _cPtrConverterCache.GetOrImportMethod(argument); public TypeReference RuntimeVoidPtr { get; } - private readonly TypeReference _runtimeFuncPtr; - private readonly Lazy _voidPtrConverter; public MethodReference VoidPtrConverter => _voidPtrConverter.Value; + private readonly TypeReference _runtimeFuncPtr; + private readonly ConversionMethodCache _funcPtrConstructorCache; + public MethodReference FuncPtrConstructor(TypeReference argument) => + _funcPtrConstructorCache.GetOrImportMethod(argument); + private AssemblyContext( AssemblyDefinition assembly, ModuleDefinition module, @@ -135,14 +138,16 @@ TypeDefinition GetRuntimeType(string typeName) => _runtimeCPtr = Module.ImportReference(GetRuntimeType(TypeSystemEx.CPtrFullTypeName)); _cPtrConverterCache = new ConversionMethodCache(_runtimeCPtr, "op_Implicit", Module); + RuntimeVoidPtr = Module.ImportReference(GetRuntimeType(TypeSystemEx.VoidPtrFullTypeName)); + _voidPtrConverter = new(() => GetImplicitCastOperator(TypeSystemEx.VoidPtrFullTypeName)); + _runtimeFuncPtr = Module.ImportReference(GetRuntimeType(TypeSystemEx.FuncPtrFullTypeName)); + _funcPtrConstructorCache = new ConversionMethodCache(_runtimeFuncPtr, ".ctor", Module); _importedActionDelegates = new("System", "Action", Module); _importedFuncDelegates = new("System", "Func", Module); - _voidPtrConverter = new(() => GetImplicitCastOperator(TypeSystemEx.VoidPtrFullTypeName)); - MethodReference GetImplicitCastOperator(string typeName) { var type = GetRuntimeType(typeName); diff --git a/Cesium.CodeGen/Ir/Expressions/TypeCastExpression.cs b/Cesium.CodeGen/Ir/Expressions/TypeCastExpression.cs index 2ebcc0c4..6c525b98 100644 --- a/Cesium.CodeGen/Ir/Expressions/TypeCastExpression.cs +++ b/Cesium.CodeGen/Ir/Expressions/TypeCastExpression.cs @@ -58,15 +58,8 @@ public void EmitTo(IEmitScope scope) Add(OpCodes.Conv_I); else if (TargetType is InteropType iType) { - var type = iType.UnderlyingType; - if (type.FullName == TypeSystemEx.VoidPtrFullTypeName - || type.IsGenericInstance && type.GetElementType().FullName == TypeSystemEx.CPtrFullTypeName) - { - Add(OpCodes.Conv_I); // TODO: Should only emit if required. - scope.Method.Body.Instructions.Add( - Instruction.Create(OpCodes.Call, iType.GetConvertCall(scope.AssemblyContext))); - } - else throw new WipException(WipException.ToDo, $"Cast to {iType.UnderlyingType} is not implemented, yet."); + Add(OpCodes.Conv_I); // TODO: Should only emit if required. + scope.Method.Body.Instructions.Add(iType.GetConvertInstruction(scope.AssemblyContext)); } else throw new AssertException($"Type {TargetType} is not supported."); diff --git a/Cesium.CodeGen/Ir/Types/InteropType.cs b/Cesium.CodeGen/Ir/Types/InteropType.cs index 8f1fa972..e758ade2 100644 --- a/Cesium.CodeGen/Ir/Types/InteropType.cs +++ b/Cesium.CodeGen/Ir/Types/InteropType.cs @@ -2,6 +2,7 @@ using Cesium.CodeGen.Extensions; using Cesium.Core; using Mono.Cecil; +using Mono.Cecil.Cil; namespace Cesium.CodeGen.Ir.Types; @@ -32,18 +33,24 @@ internal record InteropType(TypeReference UnderlyingType) : IType $"{nameof(InteropType)} doesn't know how to get size of an underlying {UnderlyingType}."); } - public MethodReference GetConvertCall(AssemblyContext context) + public Instruction GetConvertInstruction(AssemblyContext context) { if (UnderlyingType.FullName == TypeSystemEx.VoidPtrFullTypeName) - return context.VoidPtrConverter; + return Instruction.Create(OpCodes.Call, context.VoidPtrConverter); if (UnderlyingType is GenericInstanceType typeInstance) { var parent = typeInstance.GetElementType(); - if (parent.FullName == TypeSystemEx.CPtrFullTypeName) - return context.CPtrConverter(typeInstance.GenericArguments.Single()); - - throw new WipException(WipException.ToDo, "Cannot import the converter method for FuncPtr, yet."); + return parent.FullName switch + { + TypeSystemEx.CPtrFullTypeName => + Instruction.Create(OpCodes.Call, context.CPtrConverter(typeInstance.GenericArguments.Single())), + TypeSystemEx.FuncPtrFullTypeName => + Instruction.Create( + OpCodes.Newobj, + context.FuncPtrConstructor(typeInstance.GenericArguments.Single())), + _ => throw new AssertException($"No conversion available for interop type {parent}.") + }; } throw new AssertException(