diff options
Diffstat (limited to 'src/Qt.DotNet.Adapter')
-rw-r--r-- | src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Delegates.cs | 38 | ||||
-rw-r--r-- | src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Fields.cs | 144 | ||||
-rw-r--r-- | src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Methods.cs | 13 | ||||
-rw-r--r-- | src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Objects.cs | 10 | ||||
-rw-r--r-- | src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Test.cs | 34 | ||||
-rw-r--r-- | src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.cs | 52 | ||||
-rw-r--r-- | src/Qt.DotNet.Adapter/Qt/DotNet/CodeGenerator.cs | 105 |
7 files changed, 357 insertions, 39 deletions
diff --git a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Delegates.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Delegates.cs index bb5e716..882b2a3 100644 --- a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Delegates.cs +++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Delegates.cs @@ -45,6 +45,44 @@ namespace Qt.DotNet [In] Parameter[] parameters); [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate IntPtr ResolveStaticFieldGet( + [MarshalAs(UnmanagedType.LPWStr)] + [In] string typeName, + [MarshalAs(UnmanagedType.LPWStr)] + [In] string fieldName, + [In] int parameterCount, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] + [In] Parameter[] parameters); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate IntPtr ResolveStaticFieldSet( + [MarshalAs(UnmanagedType.LPWStr)] + [In] string typeName, + [MarshalAs(UnmanagedType.LPWStr)] + [In] string fieldName, + [In] int parameterCount, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] + [In] Parameter[] parameters); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate IntPtr ResolveInstanceFieldGet( + [In] IntPtr objRefPtr, + [MarshalAs(UnmanagedType.LPWStr)] + [In] string fieldName, + [In] int parameterCount, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] + [In] Parameter[] parameters); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate IntPtr ResolveInstanceFieldSet( + [In] IntPtr objRefPtr, + [MarshalAs(UnmanagedType.LPWStr)] + [In] string fieldName, + [In] int parameterCount, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] + [In] Parameter[] parameters); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] public delegate IntPtr ResolveSafeMethod( [In] IntPtr funcPtr, [In] int parameterCount, diff --git a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Fields.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Fields.cs new file mode 100644 index 0000000..01e4810 --- /dev/null +++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Fields.cs @@ -0,0 +1,144 @@ +/*************************************************************************************************** + Copyright (C) 2025 The Qt Company Ltd. + SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +***************************************************************************************************/ + +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Remoting; + +namespace Qt.DotNet +{ + public partial class Adapter + { + public static IntPtr ResolveStaticFieldGet( + string typeName, + string fieldName, + int parameterCount, + Parameter[] parameters) + { +#if DEBUG + // Compile-time signature check of delegate vs. method + _ = new Delegates.ResolveStaticFieldGet(ResolveStaticFieldGet); +#endif + return ResolveStaticFieldAccess(typeName, fieldName, false, parameters); + } + + public static IntPtr ResolveStaticFieldSet( + string typeName, + string fieldName, + int parameterCount, + Parameter[] parameters) + { +#if DEBUG + // Compile-time signature check of delegate vs. method + _ = new Delegates.ResolveStaticFieldSet(ResolveStaticFieldSet); +#endif + return ResolveStaticFieldAccess(typeName, fieldName, true, parameters); + } + + public static IntPtr ResolveInstanceFieldGet( + IntPtr objRefPtr, + string fieldName, + int parameterCount, + Parameter[] parameters) + { +#if DEBUG + // Compile-time signature check of delegate vs. method + _ = new Delegates.ResolveInstanceFieldGet(ResolveInstanceFieldGet); +#endif + return ResolveInstanceFieldAccess(objRefPtr, fieldName, false, parameters); + + } + + public static IntPtr ResolveInstanceFieldSet( + IntPtr objRefPtr, + string fieldName, + int parameterCount, + Parameter[] parameters) + { +#if DEBUG + // Compile-time signature check of delegate vs. method + _ = new Delegates.ResolveInstanceFieldSet(ResolveInstanceFieldSet); +#endif + return ResolveInstanceFieldAccess(objRefPtr, fieldName, true, parameters); + } + + private static IntPtr ResolveStaticFieldAccess( + string typeName, + string fieldName, + bool isFieldSet, + Parameter[] parameters) + { + var type = Type.GetType(typeName) + ?? throw new ArgumentException($"Type '{typeName}' not found", nameof(typeName)); + return ResolveFieldAccess(type, null, fieldName, isFieldSet, parameters); + } + + private static IntPtr ResolveInstanceFieldAccess( + IntPtr objRefPtr, string fieldName, bool isFieldSet, Parameter[] parameters) + { + var objRef = GetObjectRefFromPtr(objRefPtr); + if (objRef == null) + throw new ArgumentException("Invalid object reference", nameof(objRefPtr)); + var obj = objRef.Target; + var type = obj.GetType(); + + return ResolveFieldAccess(type, obj, fieldName, isFieldSet, parameters); + } + + private static IntPtr ResolveFieldAccess( + Type type, object obj, string fieldName, bool isFieldSet, Parameter[] parameters) + { + var isStatic = (obj == null); + object target = isStatic ? type : obj; + var fieldFlags = BindingFlags.Public + | (isStatic ? BindingFlags.Static : BindingFlags.Instance); + + var fieldAccess = isFieldSet ? MemberAccess.FieldSet : MemberAccess.FieldGet; + + var field = type.GetField(fieldName, fieldFlags) + ?? throw new ArgumentException($"Field not found [{fieldName}]", nameof(fieldName)); + + bool sigOk = + (isFieldSet, isStatic) switch + { + (true, true) => parameters.Length == 2 + && parameters[0].GetParameterType() == typeof(void) + && parameters[1].GetParameterType() == field.FieldType, + (true, false) => parameters.Length == 3 + && parameters[0].GetParameterType() == typeof(void) + && parameters[1].GetParameterType() == typeof(object) + && parameters[2].GetParameterType() == field.FieldType, + (false, true) => parameters.Length == 1 + && parameters[0].GetParameterType() == field.FieldType, + (false, false) => parameters.Length == 2 + && parameters[0].GetParameterType() == field.FieldType + && parameters[1].GetParameterType() == typeof(object), + }; + if (!sigOk) + throw new ArgumentException($"Invalid signature", nameof(parameters)); + + if (TryGetDelegate(target, field, fieldAccess, out var fieldMethod)) + return fieldMethod.FuncPtr; + + var fieldProxy = CodeGenerator.CreateProxyMethodForField(field, isFieldSet, parameters) + ?? throw new InvalidOperationException("Failed to create proxy"); + + var delegateType = CodeGenerator.CreateDelegateTypeForMethod(fieldProxy, parameters) + ?? throw new InvalidOperationException("Failed to generate delegate type"); + + var methodDelegate = Delegate.CreateDelegate(delegateType, fieldProxy, false) + ?? throw new InvalidOperationException("Failed to create delegate"); + + var methodHandle = GCHandle.Alloc(methodDelegate); + var methodFuncPtr = Marshal.GetFunctionPointerForDelegate(methodDelegate); + + var delegateRef = new DelegateRef(methodHandle, methodFuncPtr); + AddDelegateToCache(methodFuncPtr, target, field, fieldAccess, delegateRef); + return methodFuncPtr; + } + } +} diff --git a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Methods.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Methods.cs index 47db8a6..0f54e61 100644 --- a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Methods.cs +++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Methods.cs @@ -36,7 +36,7 @@ namespace Qt.DotNet ?? throw new ArgumentException( $"Method '{methodName}' not found", nameof(methodName)); - if (DelegatesByMethod.TryGetValue((type, method), out var objMethod)) + if (TryGetDelegateForMethod(type, method, out var objMethod)) return objMethod.FuncPtr; var delegateType = CodeGenerator.CreateDelegateTypeForMethod(method, parameters) @@ -49,8 +49,7 @@ namespace Qt.DotNet var methodFuncPtr = Marshal.GetFunctionPointerForDelegate(methodDelegate); var delegateRef = new DelegateRef(methodHandle, methodFuncPtr); - DelegateRefs.TryAdd(methodFuncPtr, (type, method, delegateRef)); - DelegatesByMethod.TryAdd((type, method), delegateRef); + AddMethodDelegateToCache(methodFuncPtr, type, method, delegateRef); return methodFuncPtr; } @@ -93,8 +92,7 @@ namespace Qt.DotNet var methodFuncPtr = Marshal.GetFunctionPointerForDelegate(methodDelegate); var delegateRef = new DelegateRef(methodHandle, methodFuncPtr); - DelegateRefs.TryAdd(methodFuncPtr, (type, ctor, delegateRef)); - DelegatesByMethod.TryAdd((type, ctor), delegateRef); + AddCtorDelegateToCache(methodFuncPtr, type, ctor, delegateRef); return methodFuncPtr; } @@ -125,7 +123,7 @@ namespace Qt.DotNet ?? throw new ArgumentException( $"Method '{methodName}' not found", nameof(methodName)); - if (DelegatesByMethod.TryGetValue((obj, method), out var objMethod)) + if (TryGetDelegateForMethod(obj, method, out var objMethod)) return objMethod.FuncPtr; var delegateType = CodeGenerator.CreateDelegateTypeForMethod(method, parameters) @@ -138,8 +136,7 @@ namespace Qt.DotNet var methodFuncPtr = Marshal.GetFunctionPointerForDelegate(methodDelegate); var delegateRef = new DelegateRef(methodHandle, methodFuncPtr); - DelegateRefs.TryAdd(methodFuncPtr, (obj, method, delegateRef)); - DelegatesByMethod.TryAdd((obj, method), delegateRef); + AddMethodDelegateToCache(methodFuncPtr, obj, method, delegateRef); return methodFuncPtr; } diff --git a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Objects.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Objects.cs index 5377d55..f24030b 100644 --- a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Objects.cs +++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Objects.cs @@ -76,7 +76,7 @@ ADAPTER::FreeObjectRef: WARNING Invalid object reference: 0x{objRefPtr:x16}"); var liveObjects = ObjectRefs.Values.Select(x => x.Target).ToList(); var isLive = liveObjects.Any(x => x.Equals(objRef.Target)); if (!isLive) { - var deadMethods = DelegatesByMethod + var deadMethods = DelegatesByMember .Where(x => x.Key.Target.Equals(objRef.Target)) .Select(x => x.Value.FuncPtr) .ToList(); @@ -106,7 +106,7 @@ ADAPTER::FreeObjectRef: WARNING Invalid object reference: 0x{objRefPtr:x16}"); foreach (var typeRef in typeRefs) FreeObjectRef(typeRef.Key); - var deadMethods = DelegatesByMethod + var deadMethods = DelegatesByMember .Where(x => x.Key.Target.Equals(type)) .Select(x => x.Value.FuncPtr) .ToList(); @@ -121,7 +121,11 @@ ADAPTER::FreeObjectRef: WARNING Invalid object reference: 0x{objRefPtr:x16}"); #endif if (!DelegateRefs.TryRemove(delRefPtr, out var delegateRef)) return; - DelegatesByMethod.TryRemove((delegateRef.Target, delegateRef.Method), out _); + DelegatesByMember + .Where(x => x.Key.Target == delegateRef.Target + && x.Key.Member == delegateRef.Member) + .ToList() + .ForEach(x => DelegatesByMember.TryRemove(x)); delegateRef.Ref.Handle.Free(); } diff --git a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Test.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Test.cs index 983e582..abea3ca 100644 --- a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Test.cs +++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Test.cs @@ -33,7 +33,7 @@ namespace Qt.DotNet { var ctorPtr = ResolveConstructor(1, new[] { new Parameter("FooLib.Foo, FooLib") }); - var ctor = GetMethod(ctorPtr) as ConstructorInfo; + var ctor = GetMember(ctorPtr) as ConstructorInfo; Debug.Assert(ctor != null, nameof(ctor) + " is null"); var objRef = GetRefPtrToObject(ctor.Invoke(Array.Empty<object>())); @@ -51,11 +51,11 @@ namespace Qt.DotNet TestNativeEventHandler); for (int i = 0; i < 1000; ++i) { - var str = GetMethod(getBarPtr) - .Invoke(GetObjectRefFromPtr(objRef).Target, Array.Empty<object>()) as string; + var str = (GetMember(getBarPtr) as MethodBase) + ?.Invoke(GetObjectRefFromPtr(objRef).Target, Array.Empty<object>()) as string; str += "hello"; - GetMethod(setBarPtr) - .Invoke(GetObjectRefFromPtr(objRef).Target, new object[] { str }); + (GetMember(setBarPtr) as MethodBase) + ?.Invoke(GetObjectRefFromPtr(objRef).Target, new object[] { str }); } RemoveAllEventHandlers(objRef); @@ -80,14 +80,14 @@ namespace Qt.DotNet var getTypePtr = ResolveInstanceMethod( argsRef, "GetType", 1, new[] { new Parameter("System.Type") }); if (eventName == "PropertyChanged") { - var typeObj = GetMethod(getTypePtr) - .Invoke(GetObjectRefFromPtr(argsRef).Target, Array.Empty<object>()); + var typeObj = (GetMember(getTypePtr) as MethodBase) + ?.Invoke(GetObjectRefFromPtr(argsRef).Target, Array.Empty<object>()); var typeRef = GetRefPtrToObject(typeObj); var getFullNamePtr = ResolveInstanceMethod( typeRef, "get_FullName", 1, new[] { new Parameter(UnmanagedType.LPWStr) }); - var argsTypeName = GetMethod(getFullNamePtr) - .Invoke(GetObjectRefFromPtr(typeRef).Target, Array.Empty<object>()) + var argsTypeName = (GetMember(getFullNamePtr) as MethodBase) + ?.Invoke(GetObjectRefFromPtr(typeRef).Target, Array.Empty<object>()) as string; if (argsTypeName == "System.ComponentModel.PropertyChangedEventArgs") { @@ -100,15 +100,15 @@ namespace Qt.DotNet { new Parameter(UnmanagedType.LPWStr) }); - var propName = GetMethod(getPropertyNamePtr) - .Invoke(GetObjectRefFromPtr(propChangeRef).Target, Array.Empty<object>()) + var propName = (GetMember(getPropertyNamePtr) as MethodBase) + ?.Invoke(GetObjectRefFromPtr(propChangeRef).Target, Array.Empty<object>()) as string; if (propName == "Bar") { var getBarPtr = ResolveInstanceMethod( senderRef, "get_Bar", 1, new[] { new Parameter(UnmanagedType.LPWStr) }); - var str = GetMethod(getBarPtr) - .Invoke(GetObjectRefFromPtr(senderRef).Target, Array.Empty<object>()) + var str = (GetMember(getBarPtr) as MethodBase) + ?.Invoke(GetObjectRefFromPtr(senderRef).Target, Array.Empty<object>()) as string; Debug.Assert(str != null, nameof(str) + " is null"); Console.WriteLine($"BAR CHANGED!!! [{str.Length / "hello".Length}x hello]"); @@ -121,12 +121,12 @@ namespace Qt.DotNet FreeObjectRef(senderRef); } - private static MethodBase GetMethod(IntPtr funcPtr) + private static MemberInfo GetMember(IntPtr funcPtr) { - var methods = DelegateRefs + var members = DelegateRefs .Where(x => x.Value.Ref.FuncPtr == funcPtr) - .Select(x => x.Value.Method); - return methods.First(); + .Select(x => x.Value.Member); + return members.First(); } #endif diff --git a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.cs index 286c28c..18bcb5a 100644 --- a/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.cs +++ b/src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.cs @@ -123,7 +123,7 @@ namespace Qt.DotNet #endif ObjectRefs.Clear(); DelegateRefs.Clear(); - DelegatesByMethod.Clear(); + DelegatesByMember.Clear(); Events.Clear(); } @@ -151,20 +151,66 @@ namespace Qt.DotNet } } + private enum MemberAccess + { + Constructor = MemberTypes.Constructor, + Method = MemberTypes.Method, + FieldGet = MemberTypes.Field, + FieldSet = -MemberTypes.Field + } + private static ConcurrentDictionary <IntPtr, ObjectRef> ObjectRefs { get; } = new(); private static ConcurrentDictionary - <IntPtr, (object Target, MethodBase Method, DelegateRef Ref)> DelegateRefs + <IntPtr, (object Target, MemberInfo Member, MemberAccess Access, DelegateRef Ref)> + DelegateRefs { get; } = new(); private static ConcurrentDictionary - <(object Target, MethodBase Method), DelegateRef> DelegatesByMethod + <(object Target, MemberInfo Member, MemberAccess Access), DelegateRef> DelegatesByMember { get; } = new(); private static ConcurrentDictionary <(ObjectRef Source, string Name, IntPtr Context), EventRelay> Events { get; } = new(); + + private static void AddDelegateToCache( + IntPtr ptr, object obj, MemberInfo member, MemberAccess access, DelegateRef delegateRef) + { + DelegateRefs.TryAdd(ptr, (obj, member, access, delegateRef)); + DelegatesByMember.TryAdd((obj, member, access), delegateRef); + } + + private static bool TryGetDelegate( + object obj, MemberInfo member, MemberAccess access, out DelegateRef delegateRef) + { + return DelegatesByMember.TryGetValue((obj, member, access), out delegateRef); + } + + private static void AddMethodDelegateToCache( + IntPtr ptr, object obj, MethodInfo method, DelegateRef delegateRef) + { + AddDelegateToCache(ptr, obj, method, MemberAccess.Method, delegateRef); + } + + private static bool TryGetDelegateForMethod( + object obj, MethodInfo method, out DelegateRef delegateRef) + { + return TryGetDelegate(obj, method, MemberAccess.Method, out delegateRef); + } + + private static void AddCtorDelegateToCache( + IntPtr ptr, object obj, ConstructorInfo ctor, DelegateRef delegateRef) + { + AddDelegateToCache(ptr, obj, ctor, MemberAccess.Constructor, delegateRef); + } + + private static bool TryGetDelegateForCtor( + object obj, ConstructorInfo ctor, out DelegateRef delegateRef) + { + return TryGetDelegate(obj, ctor, MemberAccess.Constructor, out delegateRef); + } } } diff --git a/src/Qt.DotNet.Adapter/Qt/DotNet/CodeGenerator.cs b/src/Qt.DotNet.Adapter/Qt/DotNet/CodeGenerator.cs index bb97c60..c1f6df0 100644 --- a/src/Qt.DotNet.Adapter/Qt/DotNet/CodeGenerator.cs +++ b/src/Qt.DotNet.Adapter/Qt/DotNet/CodeGenerator.cs @@ -8,14 +8,16 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Emit; +using System.Reflection.Metadata; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Security.Cryptography; namespace Qt.DotNet { - using DelegateIndex = ConcurrentDictionary<(MethodBase, Parameter[]), Type>; - using ProxyIndex = ConcurrentDictionary<(MethodBase, Parameter[]), MethodInfo>; - using IIndexer = IEqualityComparer<(MethodBase method, Parameter[] parameters)>; + using DelegateIndex = ConcurrentDictionary<(MemberInfo, Parameter[]), Type>; + using ProxyIndex = ConcurrentDictionary<(MemberInfo, Parameter[]), MethodInfo>; + using IIndexer = IEqualityComparer<(MemberInfo member, Parameter[] parameters)>; public class SafeReturn<T> { @@ -377,6 +379,93 @@ namespace Qt.DotNet return delegateType; } + public static MethodInfo CreateProxyMethodForField( + FieldInfo field, bool isFieldSet, Parameter[] parameters) + { +#if TESTS || DEBUG + Debug.Assert(field is not null); +#endif + // Check if already in cache + if (Proxies.TryGetValue((field, parameters), out MethodInfo proxy)) + return proxy; + + var className = UniqueName( + field.DeclaringType.Name, isFieldSet ? "FieldSet" : "FieldGet", field.Name); + var methodName = isFieldSet ? "Set" : "Get"; + var methodType = isFieldSet ? typeof(void) : field.FieldType; + Type[] methodParams = + (isFieldSet, field.IsStatic) switch + { + (true, true) => [field.FieldType], + (true, false) => [typeof(object), field.FieldType], + (false, true) => [], + (false, false) => [typeof(object)] + }; + + // Generate placeholder type for proxy method + var typeGen = ModuleGen.DefineType(className, + TypeAttributes.Sealed | TypeAttributes.Public, typeof(object)); + FieldBuilder fieldInfo = null; + if (field.IsLiteral) { + fieldInfo = typeGen.DefineField("FieldInfo", typeof(FieldInfo), + FieldAttributes.Public | FieldAttributes.Static); + } + + // Generate proxy method + var proxyGen = typeGen.DefineMethod(methodName, + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + methodType, methodParams); + + // Get code generator for proxy method + var code = proxyGen.GetILGenerator(); + + // Implement method + if (isFieldSet) { + if (field.IsStatic) { + if (!field.IsLiteral) { + code.Emit(OpCodes.Ldarg_0); + code.Emit(OpCodes.Stsfld, field); + } + } else { + code.Emit(OpCodes.Ldarg_0); + code.Emit(OpCodes.Ldarg_1); + code.Emit(OpCodes.Stfld, field); + } + } else { + if (field.IsStatic) { + if (field.IsLiteral) { + code.Emit(OpCodes.Ldsfld, fieldInfo); + code.Emit(OpCodes.Ldnull); + code.Emit(OpCodes.Callvirt, + typeof(FieldInfo).GetMethod(nameof(FieldInfo.GetValue))); + if (field.FieldType.IsValueType) + code.Emit(OpCodes.Unbox_Any, field.FieldType); + else + code.Emit(OpCodes.Castclass, field.FieldType); + } else { + code.Emit(OpCodes.Ldsfld, field); + } + } else { + code.Emit(OpCodes.Ldarg_0); + code.Emit(OpCodes.Ldfld, field); + } + } + code.Emit(OpCodes.Ret); + + // Get generated type + var proxyType = typeGen.CreateType() + ?? throw new TypeAccessException("Error creating dynamic get_field proxy"); + if (field.IsLiteral) + proxyType.GetField("FieldInfo")?.SetValue(null, field); + + // Get generated method + proxy = proxyType.GetMethod(methodName); + + // Add to cache and return + Proxies.TryAdd((field, parameters), proxy); + return proxy; + } + /// <summary> /// Generate static method that encapsulates a call to a given constructor. /// </summary> @@ -652,10 +741,10 @@ namespace Qt.DotNet private class Indexer : IIndexer { public bool Equals( - (MethodBase method, Parameter[] parameters) x, - (MethodBase method, Parameter[] parameters) y) + (MemberInfo member, Parameter[] parameters) x, + (MemberInfo member, Parameter[] parameters) y) { - if (x.method != y.method) + if (x.member != y.member) return false; if (x.parameters.Length != y.parameters.Length) return false; @@ -665,9 +754,9 @@ namespace Qt.DotNet return xyParameters.All(xy => xy.First.TypeName == xy.Second.TypeName); } - public int GetHashCode([DisallowNull] (MethodBase method, Parameter[] parameters) obj) + public int GetHashCode([DisallowNull] (MemberInfo member, Parameter[] parameters) obj) { - int hashCode = obj.method.GetHashCode(); + int hashCode = obj.member.GetHashCode(); foreach (var parameter in obj.parameters) hashCode = HashCode.Combine(hashCode, parameter.GetHashCode()); return hashCode; |