aboutsummaryrefslogtreecommitdiffstats
path: root/src/Qt.DotNet.Adapter
diff options
context:
space:
mode:
Diffstat (limited to 'src/Qt.DotNet.Adapter')
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Delegates.cs38
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Fields.cs144
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Methods.cs13
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Objects.cs10
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.Test.cs34
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/Adapter.cs52
-rw-r--r--src/Qt.DotNet.Adapter/Qt/DotNet/CodeGenerator.cs105
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;