From 84e53f9ee5516429b7eb42c2e05e3cd9800c83b5 Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Thu, 2 Nov 2023 23:35:38 +0100 Subject: [PATCH] (#354) CodeGen: match compatibility ptr types with the actual pointers in method lookup --- ...rInterop_architecture=Dynamic.verified.txt | 22 ++++++++++ .../Contexts/TranslationUnitContext.cs | 10 ++--- Cesium.CodeGen/Extensions/TypeSystemEx.cs | 43 ++++++++++++++++++- 3 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 Cesium.CodeGen.Tests/verified/CodeGenNetInteropTests.CPtrInterop_architecture=Dynamic.verified.txt diff --git a/Cesium.CodeGen.Tests/verified/CodeGenNetInteropTests.CPtrInterop_architecture=Dynamic.verified.txt b/Cesium.CodeGen.Tests/verified/CodeGenNetInteropTests.CPtrInterop_architecture=Dynamic.verified.txt new file mode 100644 index 00000000..e824a808 --- /dev/null +++ b/Cesium.CodeGen.Tests/verified/CodeGenNetInteropTests.CPtrInterop_architecture=Dynamic.verified.txt @@ -0,0 +1,22 @@ +Module: Primary + Type: + Methods: + System.Int32 ::main() + Locals: + System.Int32 V_0 + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + // TODO: There should be type cast or ctor call here + IL_0004: call System.Int32 Test::Func(Cesium.Runtime.CPtr`1) + IL_0009: 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/TranslationUnitContext.cs b/Cesium.CodeGen/Contexts/TranslationUnitContext.cs index 993a0f74..7016644c 100644 --- a/Cesium.CodeGen/Contexts/TranslationUnitContext.cs +++ b/Cesium.CodeGen/Contexts/TranslationUnitContext.cs @@ -14,10 +14,6 @@ namespace Cesium.CodeGen.Contexts; public class TranslationUnitContext { - private const string _cPtrFullTypeName = "Cesium.Runtime.CPtr`1"; - private const string _voidPtrFullTypeName = "Cesium.Runtime.VoidPtr"; - private const string _funcPtrFullTypeName = "Cesium.Runtime.FuncPtr`1"; - public AssemblyContext AssemblyContext { get; } public string Name { get; } @@ -47,9 +43,9 @@ TypeReference GetRuntimeType(string typeName) => assemblyContext.CesiumRuntimeAssembly.GetType(typeName) ?? throw new AssertException($"Could not find type {typeName} in the runtime assembly."); - _runtimeCPtr = Module.ImportReference(GetRuntimeType(_cPtrFullTypeName)); - RuntimeVoidPtr = Module.ImportReference(GetRuntimeType(_voidPtrFullTypeName)); - _runtimeFuncPtr = Module.ImportReference(GetRuntimeType(_funcPtrFullTypeName)); + _runtimeCPtr = Module.ImportReference(GetRuntimeType(TypeSystemEx.CPtrFullTypeName)); + RuntimeVoidPtr = Module.ImportReference(GetRuntimeType(TypeSystemEx.VoidPtrFullTypeName)); + _runtimeFuncPtr = Module.ImportReference(GetRuntimeType(TypeSystemEx.FuncPtrFullTypeName)); _importedActionDelegates = new("System", "Action", Module, TypeSystem); _importedFuncDelegates = new("System", "Func", Module, TypeSystem); diff --git a/Cesium.CodeGen/Extensions/TypeSystemEx.cs b/Cesium.CodeGen/Extensions/TypeSystemEx.cs index 07a805d8..c77bee64 100644 --- a/Cesium.CodeGen/Extensions/TypeSystemEx.cs +++ b/Cesium.CodeGen/Extensions/TypeSystemEx.cs @@ -9,6 +9,10 @@ namespace Cesium.CodeGen.Extensions; internal static class TypeSystemEx { + public const string CPtrFullTypeName = "Cesium.Runtime.CPtr`1"; + public const string VoidPtrFullTypeName = "Cesium.Runtime.VoidPtr"; + public const string FuncPtrFullTypeName = "Cesium.Runtime.FuncPtr`1"; + public static MethodReference MethodLookup( this TranslationUnitContext context, string memberName, @@ -85,7 +89,7 @@ private static bool Match( } var declReturnReified = returnType.Resolve(context); - if (declReturnReified.FullName != method.ReturnType.FullName) + if (!TypesCorrespond(declReturnReified, method.ReturnType)) { similarMethods.Add((method, $"Returns types do not match: {declReturnReified.Name} in declaration, {method.ReturnType.Name} in source.")); return false; @@ -99,7 +103,7 @@ private static bool Match( var srcParam = methodParameters[i]; var srcParamType = srcParam.ParameterType; - if (declParamType.FullName != srcParamType.FullName) + if (!TypesCorrespond(declParamType, srcParamType)) { similarMethods.Add((method, $"Type of argument #{i} does not match: {declParamType} in declaration, {srcParamType} in source.")); return false; @@ -132,6 +136,41 @@ private static bool Match( return true; } + /// Determines whether the types correspond to each other. + /// + /// This tries to handle the pointer interop between the arch-independent pointer types introduced by the Cesium + /// compatibility model and the actual runtime pointer types. + /// + private static bool TypesCorrespond(TypeReference type1, TypeReference type2) + { + // let type 1 to be pointer out of these two + if (type2.IsPointer || type2.IsFunctionPointer) (type1, type2) = (type2, type1); + var isType1AnyPointer = type1.IsPointer || type1.IsFunctionPointer; + var isType2AnyPointer = type2.IsPointer || type2.IsFunctionPointer; + if (!isType1AnyPointer || isType2AnyPointer) + { + // If type1 is not a pointer, then we don't need to use the compatibility model for this type pair. + // If type2 is a pointer, then type1 is also a pointer, and so no compatibility is required as well. + return type1.FullName == type2.FullName; + } + + if (!type2.IsGenericInstance) return false; + type2 = type2.GetElementType(); + if (type1.IsPointer) + { + // TODO: Analyze the generic argument. + return type2.FullName == CPtrFullTypeName || type2.FullName == VoidPtrFullTypeName; + } + + if (type1.IsFunctionPointer) + { + // TODO: Analyze the generic argument. + return type2.FullName == FuncPtrFullTypeName; + } + + throw new AssertException("Impossible: type1 should be either a pointer or a function pointer."); + } + private static string SimilarMethodsMessage(string name, List<(MethodDefinition, string)> similarMethods) { return $"Cannot find an appropriate overload for CLI-imported function {name}. Candidates:\n"