mirror of
https://github.com/qmlnet/qmlnet.git
synced 2026-05-21 06:45:32 -06:00
Cached delegates for perf increases.
This commit is contained in:
parent
864b2c4cf8
commit
db701da9ec
11 changed files with 503 additions and 251 deletions
|
|
@ -66,6 +66,23 @@ void NetPropertyInfo::setNotifySignal(QSharedPointer<NetSignalInfo> signal)
|
|||
_notifySignal = std::move(signal);
|
||||
}
|
||||
|
||||
void NetPropertyInfo::addIndexParameter(QString name, QSharedPointer<NetTypeInfo> typeInfo)
|
||||
{
|
||||
_indexParameters.append(QSharedPointer<NetMethodInfoArguement>(new NetMethodInfoArguement(std::move(name), std::move(typeInfo))));
|
||||
}
|
||||
|
||||
int NetPropertyInfo::getIndexParameterCount()
|
||||
{
|
||||
return _indexParameters.size();
|
||||
}
|
||||
|
||||
QSharedPointer<NetMethodInfoArguement> NetPropertyInfo::getIndexParameter(int index)
|
||||
{
|
||||
if(index < 0) return QSharedPointer<NetMethodInfoArguement>(nullptr);
|
||||
if(index >= _indexParameters.length()) return QSharedPointer<NetMethodInfoArguement>(nullptr);
|
||||
return _indexParameters.at(index);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
Q_DECL_EXPORT NetPropertyInfoContainer* property_info_create(NetTypeInfoContainer* parentTypeContainer,
|
||||
|
|
@ -144,6 +161,25 @@ Q_DECL_EXPORT void property_info_setNotifySignal(NetPropertyInfoContainer* conta
|
|||
container->property->setNotifySignal(signalContainer->signal);
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT void property_info_addIndexParameter(NetPropertyInfoContainer* container, LPWCSTR name, NetTypeInfoContainer* typeInfoContainer)
|
||||
{
|
||||
container->property->addIndexParameter(QString::fromUtf16(name), typeInfoContainer->netTypeInfo);
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT int property_info_getIndexParameterCount(NetPropertyInfoContainer* container)
|
||||
{
|
||||
return container->property->getIndexParameterCount();
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT NetMethodInfoArguementContainer* property_info_getIndexParameter(NetPropertyInfoContainer* container, int index)
|
||||
{
|
||||
QSharedPointer<NetMethodInfoArguement> parameter = container->property->getIndexParameter(index);
|
||||
if(parameter == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
NetMethodInfoArguementContainer* result = new NetMethodInfoArguementContainer();
|
||||
result->methodArguement = parameter;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define NET_TYPE_INFO_PROPERTY_H
|
||||
|
||||
#include <QmlNet/types/NetTypeInfo.h>
|
||||
#include <QmlNet/types/NetMethodInfo.h>
|
||||
|
||||
class NetPropertyInfo {
|
||||
public:
|
||||
|
|
@ -19,6 +20,9 @@ public:
|
|||
bool canWrite();
|
||||
QSharedPointer<NetSignalInfo> getNotifySignal();
|
||||
void setNotifySignal(QSharedPointer<NetSignalInfo> signal);
|
||||
void addIndexParameter(QString name, QSharedPointer<NetTypeInfo> typeInfo);
|
||||
int getIndexParameterCount();
|
||||
QSharedPointer<NetMethodInfoArguement> getIndexParameter(int index);
|
||||
private:
|
||||
int _id;
|
||||
QSharedPointer<NetTypeInfo> _parentType;
|
||||
|
|
@ -27,6 +31,7 @@ private:
|
|||
bool _canRead;
|
||||
bool _canWrite;
|
||||
QSharedPointer<NetSignalInfo> _notifySignal;
|
||||
QList<QSharedPointer<NetMethodInfoArguement>> _indexParameters;
|
||||
};
|
||||
|
||||
struct NetPropertyInfoContainer {
|
||||
|
|
|
|||
|
|
@ -93,6 +93,26 @@ namespace Qml.Net.Tests.CodeGen
|
|||
{
|
||||
}
|
||||
|
||||
public virtual void TestObjOfType(RandomType param)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void TestStruct(RandomStruct param)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void TestStructNullable(RandomStruct? param)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void TestEnum(RandomEnum param)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void TestEnumNullable(RandomEnum? param)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void MultipleParams(int param1, long param2)
|
||||
{
|
||||
}
|
||||
|
|
@ -103,6 +123,21 @@ namespace Qml.Net.Tests.CodeGen
|
|||
}
|
||||
}
|
||||
|
||||
public class RandomType
|
||||
{
|
||||
}
|
||||
|
||||
public struct RandomStruct
|
||||
{
|
||||
public int Value;
|
||||
}
|
||||
|
||||
public enum RandomEnum
|
||||
{
|
||||
Value1,
|
||||
Value2
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_call_method_with_bool_parameter()
|
||||
{
|
||||
|
|
@ -192,6 +227,30 @@ namespace Qml.Net.Tests.CodeGen
|
|||
Test(x => x.TestObj(It.Is<object>(v => v.Equals(o))), o);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_call_method_with_typed_object_parameter()
|
||||
{
|
||||
var o = new RandomType();
|
||||
Test(x => x.TestObjOfType(It.Is<RandomType>(v => v.Equals(o))), o);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_call_method_with_struct_parameter()
|
||||
{
|
||||
var o = new RandomStruct { Value = 2 };
|
||||
Test(x => x.TestStruct(It.Is<RandomStruct>(v => v.Equals(o))), o);
|
||||
Test(x => x.TestStructNullable(It.Is<RandomStruct>(v => v.Equals(o))), o);
|
||||
Test(x => x.TestStructNullable(It.Is<RandomStruct?>(v => v == null)), (RandomStruct?)null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_call_method_with_enum()
|
||||
{
|
||||
Test(x => x.TestEnum(It.Is<RandomEnum>(v => v == RandomEnum.Value2)), RandomEnum.Value2);
|
||||
Test(x => x.TestEnumNullable(It.Is<RandomEnum?>(v => v == RandomEnum.Value2)), RandomEnum.Value2);
|
||||
Test(x => x.TestEnumNullable(It.Is<RandomEnum?>(v => v == null)), (RandomEnum?)null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_call_method_with_multiple_parameters()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@ namespace Qml.Net.Tests.CodeGen
|
|||
{
|
||||
public class TestObject
|
||||
{
|
||||
public virtual int this[int index]
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
public virtual bool Bool { get; set; }
|
||||
|
||||
public virtual bool? BoolNullable { get; set; }
|
||||
|
|
@ -51,6 +57,63 @@ namespace Qml.Net.Tests.CodeGen
|
|||
public virtual DateTimeOffset? DateTimeNullable { get; set; }
|
||||
|
||||
public virtual object Obj { get; set; }
|
||||
|
||||
public virtual RandomType ObjTyped { get; set; }
|
||||
|
||||
public virtual RandomStruct Struct { get; set; }
|
||||
|
||||
public virtual RandomStruct? StructNullable { get; set; }
|
||||
|
||||
public virtual RandomEnum Enum { get; set; }
|
||||
|
||||
public virtual RandomEnum? EnumNullable { get; set; }
|
||||
}
|
||||
|
||||
public class RandomType
|
||||
{
|
||||
}
|
||||
|
||||
public struct RandomStruct
|
||||
{
|
||||
public int Value;
|
||||
}
|
||||
|
||||
public enum RandomEnum
|
||||
{
|
||||
Value1,
|
||||
Value2
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_use_indexer()
|
||||
{
|
||||
_mock.SetupGet(x => x[10]).Returns(20);
|
||||
|
||||
var del = (Net.Internal.CodeGen.CodeGen.InvokeMethodDelegate)BuildReadPropertyDelegate("Item");
|
||||
|
||||
using (var netReference = NetReference.CreateForObject(_mock.Object))
|
||||
{
|
||||
using (var result = new NetVariant())
|
||||
{
|
||||
del(netReference, NetVariantList.From(NetVariant.From(10)), result);
|
||||
result.VariantType.Should().Be(NetVariantType.Int);
|
||||
result.Int.Should().Be(20);
|
||||
}
|
||||
}
|
||||
|
||||
_mock.Reset();
|
||||
_mock.SetupSet(x => x[10] = 20);
|
||||
|
||||
del = (Net.Internal.CodeGen.CodeGen.InvokeMethodDelegate)BuildSetPropertyDelegate("Item");
|
||||
|
||||
using (var netReference = NetReference.CreateForObject(_mock.Object))
|
||||
{
|
||||
using (var result = new NetVariant())
|
||||
{
|
||||
del(netReference, NetVariantList.From(NetVariant.From(10), NetVariant.From(20)), null);
|
||||
_mock.VerifySet(x => x[10] = 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -305,18 +368,84 @@ namespace Qml.Net.Tests.CodeGen
|
|||
TestSet(x => x.Obj, NetVariant.From(o), x => x.Obj = o);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_use_prop_typed_object()
|
||||
{
|
||||
var o = new RandomType();
|
||||
TestGet(x => x.ObjTyped, o, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Object);
|
||||
result.Instance.Instance.Should().BeSameAs(o);
|
||||
});
|
||||
TestGet(x => x.ObjTyped, null, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Invalid);
|
||||
});
|
||||
|
||||
TestSet(x => x.ObjTyped, NetVariant.From((RandomType)null), x => x.ObjTyped = null);
|
||||
TestSet(x => x.ObjTyped, NetVariant.From(o), x => x.ObjTyped = o);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_use_prop_struct()
|
||||
{
|
||||
var o = new RandomStruct();
|
||||
o.Value = 3;
|
||||
TestGet(x => x.Struct, o, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Object);
|
||||
result.Instance.Instance.Should().Be(o);
|
||||
});
|
||||
TestGet(x => x.StructNullable, o, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Object);
|
||||
result.Instance.Instance.Should().Be(o);
|
||||
});
|
||||
TestGet(x => x.StructNullable, null, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Invalid);
|
||||
});
|
||||
|
||||
TestSet(x => x.Struct, NetVariant.From(o), x => x.Struct = o);
|
||||
TestSet(x => x.StructNullable, NetVariant.From(o), x => x.StructNullable = o);
|
||||
TestSet(x => x.StructNullable, new NetVariant(), x => x.StructNullable = null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_use_prop_enum()
|
||||
{
|
||||
TestGet(x => x.Enum, RandomEnum.Value2, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Int);
|
||||
result.Int.Should().Be((int)RandomEnum.Value2);
|
||||
});
|
||||
TestGet(x => x.EnumNullable, RandomEnum.Value2, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Int);
|
||||
result.Int.Should().Be((int)RandomEnum.Value2);
|
||||
});
|
||||
TestGet(x => x.EnumNullable, null, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Invalid);
|
||||
});
|
||||
|
||||
TestSet(x => x.Enum, NetVariant.From(RandomEnum.Value2), x => x.Enum = RandomEnum.Value2);
|
||||
TestSet(x => x.EnumNullable, NetVariant.From(RandomEnum.Value2), x => x.EnumNullable = RandomEnum.Value2);
|
||||
TestSet(x => x.EnumNullable, new NetVariant(), x => x.EnumNullable = null);
|
||||
}
|
||||
|
||||
private void TestGet<TProperty>(Expression<Func<TestObject, TProperty>> expression, TProperty value, Action<NetVariant> assert)
|
||||
{
|
||||
_mock.Reset();
|
||||
_mock.SetupGet(expression).Returns(value);
|
||||
|
||||
var del = (Net.Internal.CodeGen.CodeGen.ReadPropertyDelegate)BuildReadPropertyDelegate(((MemberExpression)expression.Body).Member.Name);
|
||||
var del = (Net.Internal.CodeGen.CodeGen.InvokeMethodDelegate)BuildReadPropertyDelegate(((MemberExpression)expression.Body).Member.Name);
|
||||
|
||||
using (var netReference = NetReference.CreateForObject(_mock.Object))
|
||||
{
|
||||
using (var result = new NetVariant())
|
||||
{
|
||||
del(netReference, result);
|
||||
del(netReference, null, result);
|
||||
_mock.VerifyGet(expression);
|
||||
assert(result);
|
||||
}
|
||||
|
|
@ -330,11 +459,15 @@ namespace Qml.Net.Tests.CodeGen
|
|||
_mock.SetupSet(expression);
|
||||
#pragma warning restore CS0618
|
||||
|
||||
var del = (Net.Internal.CodeGen.CodeGen.SetPropertyDelegate)BuildSetPropertyDelegate(((MemberExpression)expression.Body).Member.Name);
|
||||
var del = (Net.Internal.CodeGen.CodeGen.InvokeMethodDelegate)BuildSetPropertyDelegate(((MemberExpression)expression.Body).Member.Name);
|
||||
|
||||
using (var netReference = NetReference.CreateForObject(_mock.Object))
|
||||
{
|
||||
del(netReference, value);
|
||||
using (var list = NetVariantList.From(value))
|
||||
{
|
||||
del(netReference, list, null);
|
||||
}
|
||||
|
||||
_mock.VerifySet(verify, Times.Once);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,30 @@ namespace Qml.Net.Tests.CodeGen
|
|||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual RandomType ReturnTypeObjectTyped()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual RandomStruct ReturnTypeStruct()
|
||||
{
|
||||
return new RandomStruct();
|
||||
}
|
||||
|
||||
public virtual RandomStruct? ReturnTypeStructNullable()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class RandomType
|
||||
{
|
||||
}
|
||||
|
||||
public struct RandomStruct
|
||||
{
|
||||
public int Value1;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -322,6 +346,41 @@ namespace Qml.Net.Tests.CodeGen
|
|||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_return_typed_object()
|
||||
{
|
||||
var o = new RandomType();
|
||||
Test(x => x.ReturnTypeObjectTyped(), o, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Object);
|
||||
result.Instance.Instance.Should().BeSameAs(o);
|
||||
});
|
||||
Test(x => x.ReturnTypeObjectTyped(), null, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Invalid);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_return_struct()
|
||||
{
|
||||
var value = new RandomStruct();
|
||||
Test(x => x.ReturnTypeStruct(), value, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Object);
|
||||
result.Instance.Instance.Should().Be(value);
|
||||
});
|
||||
Test(x => x.ReturnTypeStructNullable(), value, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Object);
|
||||
result.Instance.Instance.Should().Be(value);
|
||||
});
|
||||
Test(x => x.ReturnTypeStructNullable(), null, result =>
|
||||
{
|
||||
result.VariantType.Should().Be(NetVariantType.Invalid);
|
||||
});
|
||||
}
|
||||
|
||||
private void Test<TResult>(Expression<Func<TestObject, TResult>> expression, TResult value, Action<NetVariant> assert)
|
||||
{
|
||||
_mock.Reset();
|
||||
|
|
|
|||
|
|
@ -397,5 +397,29 @@ namespace Qml.Net.Tests.Types
|
|||
|
||||
type1.Id.Should().NotBe(type2.Id);
|
||||
}
|
||||
|
||||
public class TestType19
|
||||
{
|
||||
public object this[int index]
|
||||
{
|
||||
get => null;
|
||||
set { }
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_get_index_parameters()
|
||||
{
|
||||
var type = NetTypeManager.GetTypeInfo<TestType19>();
|
||||
type.EnsureLoaded();
|
||||
|
||||
var prop = type.GetProperty(0);
|
||||
prop.Name.Should().Be("Item");
|
||||
|
||||
var indexParameters = prop.GetAllIndexParameters();
|
||||
indexParameters.Count.Should().Be(1);
|
||||
indexParameters[0].Name.Should().Be("index");
|
||||
indexParameters[0].Type.FullTypeName.Should().Be(typeof(int).AssemblyQualifiedName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -442,15 +442,7 @@ namespace Qml.Net.Internal.CodeGen
|
|||
{
|
||||
using (var variant = list.Get(index))
|
||||
{
|
||||
if (variant == null || variant.VariantType == NetVariantType.Invalid)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var instance = variant.Instance)
|
||||
{
|
||||
return instance?.Instance;
|
||||
}
|
||||
return variant?.AsObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,25 +14,37 @@ namespace Qml.Net.Internal.CodeGen
|
|||
{
|
||||
public delegate void InvokeMethodDelegate(NetReference reference, NetVariantList parameters, NetVariant result);
|
||||
|
||||
public delegate void ReadPropertyDelegate(NetReference reference, NetVariant result);
|
||||
|
||||
public delegate void SetPropertyDelegate(NetReference reference, NetVariant value);
|
||||
|
||||
public static InvokeMethodDelegate BuildInvokeMethodDelegate(NetMethodInfo methodInfo)
|
||||
{
|
||||
var invokeType = Type.GetType(methodInfo.ParentType.FullTypeName);
|
||||
var invokeMethod = invokeType.GetMethod(methodInfo.MethodName);
|
||||
var parameterTypes = new List<Type>();
|
||||
for (var x = 0; x < methodInfo.ParameterCount; x++)
|
||||
{
|
||||
using (var p = methodInfo.GetParameter(x))
|
||||
using (var t = p.Type)
|
||||
{
|
||||
parameterTypes.Add(Type.GetType(t.FullTypeName));
|
||||
}
|
||||
}
|
||||
var invokeMethod = invokeType.GetMethod(methodInfo.MethodName, parameterTypes.ToArray());
|
||||
return BuildInvokeMethodDelegate(invokeMethod);
|
||||
}
|
||||
|
||||
public static InvokeMethodDelegate BuildInvokeMethodDelegate(MethodInfo methodInfo)
|
||||
{
|
||||
var invokeType = methodInfo.DeclaringType;
|
||||
|
||||
var dynamicMethod = new DynamicMethod(
|
||||
"method",
|
||||
typeof(void),
|
||||
new[] { typeof(NetReference), typeof(NetVariantList), typeof(NetVariant) });
|
||||
|
||||
if (invokeMethod.ReturnType != null && invokeMethod.ReturnType != typeof(void))
|
||||
bool box = false;
|
||||
if (methodInfo.ReturnType != null && methodInfo.ReturnType != typeof(void))
|
||||
{
|
||||
MethodInfo returnMethod = null;
|
||||
var isNullable = false;
|
||||
switch (GetPrefVariantType(invokeMethod.ReturnType, ref isNullable))
|
||||
switch (GetPrefVariantType(methodInfo.ReturnType, ref isNullable))
|
||||
{
|
||||
case NetVariantType.Bool:
|
||||
returnMethod = isNullable ? LoadMethods.LoadBoolNullableMethod : LoadMethods.LoadBoolMethod;
|
||||
|
|
@ -66,6 +78,7 @@ namespace Qml.Net.Internal.CodeGen
|
|||
break;
|
||||
case NetVariantType.Object:
|
||||
returnMethod = LoadMethods.LoadObjectMethod;
|
||||
box = methodInfo.ReturnType.IsValueType;
|
||||
break;
|
||||
case NetVariantType.JsValue:
|
||||
throw new NotImplementedException();
|
||||
|
|
@ -82,9 +95,15 @@ namespace Qml.Net.Internal.CodeGen
|
|||
il.Emit(OpCodes.Callvirt, GenericMethods.InstanceProperty.GetMethod);
|
||||
il.Emit(OpCodes.Castclass, invokeType);
|
||||
|
||||
InvokeParameters(il, invokeMethod);
|
||||
InvokeParameters(il, methodInfo);
|
||||
|
||||
il.Emit(OpCodes.Callvirt, methodInfo);
|
||||
|
||||
if (box)
|
||||
{
|
||||
il.Emit(OpCodes.Box, methodInfo.ReturnType);
|
||||
}
|
||||
|
||||
il.Emit(OpCodes.Callvirt, invokeMethod);
|
||||
il.Emit(OpCodes.Call, returnMethod);
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
|
@ -96,9 +115,9 @@ namespace Qml.Net.Internal.CodeGen
|
|||
il.Emit(OpCodes.Callvirt, GenericMethods.InstanceProperty.GetMethod);
|
||||
il.Emit(OpCodes.Castclass, invokeType);
|
||||
|
||||
InvokeParameters(il, invokeMethod);
|
||||
InvokeParameters(il, methodInfo);
|
||||
|
||||
il.Emit(OpCodes.Callvirt, invokeMethod);
|
||||
il.Emit(OpCodes.Callvirt, methodInfo);
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
|
@ -106,141 +125,42 @@ namespace Qml.Net.Internal.CodeGen
|
|||
return (InvokeMethodDelegate)dynamicMethod.CreateDelegate(typeof(InvokeMethodDelegate));
|
||||
}
|
||||
|
||||
public static ReadPropertyDelegate BuildReadPropertyDelegate(NetPropertyInfo propertyInfo)
|
||||
public static InvokeMethodDelegate BuildReadPropertyDelegate(NetPropertyInfo propertyInfo)
|
||||
{
|
||||
var invokeType = Type.GetType(propertyInfo.ParentType.FullTypeName);
|
||||
var invokeProperty = invokeType.GetProperty(propertyInfo.Name);
|
||||
|
||||
MethodInfo loadMethod = null;
|
||||
var isNullable = false;
|
||||
switch (GetPrefVariantType(invokeProperty.PropertyType, ref isNullable))
|
||||
var parameterTypes = new List<Type>();
|
||||
for (var x = 0; x < propertyInfo.IndexParameterCount; x++)
|
||||
{
|
||||
case NetVariantType.Bool:
|
||||
loadMethod = isNullable ? LoadMethods.LoadBoolNullableMethod : LoadMethods.LoadBoolMethod;
|
||||
break;
|
||||
case NetVariantType.Char:
|
||||
loadMethod = isNullable ? LoadMethods.LoadCharNullableMethod : LoadMethods.LoadCharMethod;
|
||||
break;
|
||||
case NetVariantType.Int:
|
||||
loadMethod = isNullable ? LoadMethods.LoadIntNullableMethod : LoadMethods.LoadIntMethod;
|
||||
break;
|
||||
case NetVariantType.UInt:
|
||||
loadMethod = isNullable ? LoadMethods.LoadUIntNullableMethod : LoadMethods.LoadUIntMethod;
|
||||
break;
|
||||
case NetVariantType.Long:
|
||||
loadMethod = isNullable ? LoadMethods.LoadLongNullableMethod : LoadMethods.LoadLongMethod;
|
||||
break;
|
||||
case NetVariantType.ULong:
|
||||
loadMethod = isNullable ? LoadMethods.LoadULongNullableMethod : LoadMethods.LoadULongMethod;
|
||||
break;
|
||||
case NetVariantType.Float:
|
||||
loadMethod = isNullable ? LoadMethods.LoadFloatNullableMethod : LoadMethods.LoadFloatMethod;
|
||||
break;
|
||||
case NetVariantType.Double:
|
||||
loadMethod = isNullable ? LoadMethods.LoadDoubleNullableMethod : LoadMethods.LoadDoubleMethod;
|
||||
break;
|
||||
case NetVariantType.String:
|
||||
loadMethod = LoadMethods.LoadStringMethod;
|
||||
break;
|
||||
case NetVariantType.DateTime:
|
||||
loadMethod = isNullable ? LoadMethods.LoadDateTimeNullableMethod : LoadMethods.LoadDateTimeMethod;
|
||||
break;
|
||||
case NetVariantType.Object:
|
||||
loadMethod = LoadMethods.LoadObjectMethod;
|
||||
break;
|
||||
case NetVariantType.JsValue:
|
||||
throw new NotImplementedException();
|
||||
case NetVariantType.Invalid:
|
||||
throw new Exception("invalid type");
|
||||
default:
|
||||
throw new Exception("unknown type");
|
||||
using (var p = propertyInfo.GetIndexParameter(x))
|
||||
using (var t = p.Type)
|
||||
{
|
||||
parameterTypes.Add(Type.GetType(t.FullTypeName));
|
||||
}
|
||||
}
|
||||
|
||||
var dynamicMethod = new DynamicMethod(
|
||||
"method",
|
||||
typeof(void),
|
||||
new[] { typeof(NetReference), typeof(NetVariant) });
|
||||
var invokeProperty = invokeType.GetProperty(propertyInfo.Name);
|
||||
|
||||
var il = dynamicMethod.GetILGenerator();
|
||||
|
||||
il.Emit(OpCodes.Ldarg_1); // result
|
||||
il.Emit(OpCodes.Ldarg_0); // net reference
|
||||
il.Emit(OpCodes.Callvirt, GenericMethods.InstanceProperty.GetMethod);
|
||||
il.Emit(OpCodes.Castclass, invokeType);
|
||||
il.Emit(OpCodes.Callvirt, invokeProperty.GetMethod);
|
||||
il.Emit(OpCodes.Call, loadMethod);
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
return (ReadPropertyDelegate)dynamicMethod.CreateDelegate(typeof(ReadPropertyDelegate));
|
||||
return BuildInvokeMethodDelegate(invokeProperty.GetMethod);
|
||||
}
|
||||
|
||||
public static SetPropertyDelegate BuildSetPropertyDelegate(NetPropertyInfo propertyInfo)
|
||||
public static InvokeMethodDelegate BuildSetPropertyDelegate(NetPropertyInfo propertyInfo)
|
||||
{
|
||||
var invokeType = Type.GetType(propertyInfo.ParentType.FullTypeName);
|
||||
var invokeProperty = invokeType.GetProperty(propertyInfo.Name);
|
||||
|
||||
MethodInfo getMethod = null;
|
||||
var isNullable = false;
|
||||
switch (GetPrefVariantType(invokeProperty.PropertyType, ref isNullable))
|
||||
var parameterTypes = new List<Type>();
|
||||
for (var x = 0; x < propertyInfo.IndexParameterCount; x++)
|
||||
{
|
||||
case NetVariantType.Bool:
|
||||
getMethod = isNullable ? GetMethods.BoolNullableMethod : GetMethods.BoolMethod;
|
||||
break;
|
||||
case NetVariantType.Char:
|
||||
getMethod = isNullable ? GetMethods.CharNullableMethod : GetMethods.CharMethod;
|
||||
break;
|
||||
case NetVariantType.Int:
|
||||
getMethod = isNullable ? GetMethods.IntNullableMethod : GetMethods.IntMethod;
|
||||
break;
|
||||
case NetVariantType.UInt:
|
||||
getMethod = isNullable ? GetMethods.UIntNullableMethod : GetMethods.UIntMethod;
|
||||
break;
|
||||
case NetVariantType.Long:
|
||||
getMethod = isNullable ? GetMethods.LongNullableMethod : GetMethods.LongMethod;
|
||||
break;
|
||||
case NetVariantType.ULong:
|
||||
getMethod = isNullable ? GetMethods.ULongNullableMethod : GetMethods.LongMethod;
|
||||
break;
|
||||
case NetVariantType.Float:
|
||||
getMethod = isNullable ? GetMethods.FloatNullableMethod : GetMethods.FloatMethod;
|
||||
break;
|
||||
case NetVariantType.Double:
|
||||
getMethod = isNullable ? GetMethods.DoubleNullableMethod : GetMethods.DoubleMethod;
|
||||
break;
|
||||
case NetVariantType.String:
|
||||
getMethod = GetMethods.StringMethod;
|
||||
break;
|
||||
case NetVariantType.DateTime:
|
||||
getMethod = isNullable ? GetMethods.DateTimeNullableMethod : GetMethods.DateTimeMethod;
|
||||
break;
|
||||
case NetVariantType.Object:
|
||||
getMethod = GetMethods.ObjMethod;
|
||||
break;
|
||||
case NetVariantType.JsValue:
|
||||
throw new NotImplementedException();
|
||||
case NetVariantType.Invalid:
|
||||
throw new Exception("invalid type");
|
||||
default:
|
||||
throw new Exception("unknown type");
|
||||
using (var p = propertyInfo.GetIndexParameter(x))
|
||||
using (var t = p.Type)
|
||||
{
|
||||
parameterTypes.Add(Type.GetType(t.FullTypeName));
|
||||
}
|
||||
}
|
||||
|
||||
var dynamicMethod = new DynamicMethod(
|
||||
"method",
|
||||
typeof(void),
|
||||
new[] { typeof(NetReference), typeof(NetVariant) });
|
||||
var invokeProperty = invokeType.GetProperty(propertyInfo.Name);
|
||||
|
||||
var il = dynamicMethod.GetILGenerator();
|
||||
|
||||
il.Emit(OpCodes.Ldarg_0); // net reference
|
||||
il.Emit(OpCodes.Callvirt, GenericMethods.InstanceProperty.GetMethod);
|
||||
il.Emit(OpCodes.Castclass, invokeType);
|
||||
il.Emit(OpCodes.Ldarg_1); // variant
|
||||
il.Emit(OpCodes.Call, getMethod);
|
||||
il.Emit(OpCodes.Callvirt, invokeProperty.SetMethod);
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
return (SetPropertyDelegate)dynamicMethod.CreateDelegate(typeof(SetPropertyDelegate));
|
||||
return BuildInvokeMethodDelegate(invokeProperty.SetMethod);
|
||||
}
|
||||
|
||||
private static void InvokeParameters(ILGenerator il, MethodInfo methodInfo)
|
||||
|
|
@ -253,6 +173,7 @@ namespace Qml.Net.Internal.CodeGen
|
|||
|
||||
MethodInfo returnMethod = null;
|
||||
var isNullable = false;
|
||||
var unbox = false;
|
||||
switch (GetPrefVariantType(parameter.ParameterType, ref isNullable))
|
||||
{
|
||||
case NetVariantType.Bool:
|
||||
|
|
@ -287,6 +208,7 @@ namespace Qml.Net.Internal.CodeGen
|
|||
break;
|
||||
case NetVariantType.Object:
|
||||
returnMethod = ListMethods.ObjectAtMethod;
|
||||
unbox = parameter.ParameterType.IsValueType;
|
||||
break;
|
||||
case NetVariantType.JsValue:
|
||||
throw new NotImplementedException();
|
||||
|
|
@ -297,6 +219,12 @@ namespace Qml.Net.Internal.CodeGen
|
|||
}
|
||||
|
||||
il.Emit(OpCodes.Call, returnMethod);
|
||||
|
||||
if (unbox)
|
||||
{
|
||||
il.Emit(OpCodes.Unbox_Any, parameter.ParameterType);
|
||||
}
|
||||
|
||||
parameterIndex++;
|
||||
}
|
||||
}
|
||||
|
|
@ -333,6 +261,11 @@ namespace Qml.Net.Internal.CodeGen
|
|||
// ReSharper restore TailRecursiveCall
|
||||
}
|
||||
|
||||
if (typeInfo.IsEnum)
|
||||
{
|
||||
return GetPrefVariantType(Enum.GetUnderlyingType(typeInfo), ref isNullable);
|
||||
}
|
||||
|
||||
return NetVariantType.Object;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ namespace Qml.Net.Internal
|
|||
{
|
||||
internal class DefaultCallbacks : ICallbacks
|
||||
{
|
||||
private Dictionary<int, CodeGen.CodeGen.InvokeMethodDelegate> _cachedReadProperties = new Dictionary<int, CodeGen.CodeGen.InvokeMethodDelegate>();
|
||||
private Dictionary<int, CodeGen.CodeGen.InvokeMethodDelegate> _cachedSetProperties = new Dictionary<int, CodeGen.CodeGen.InvokeMethodDelegate>();
|
||||
private Dictionary<int, CodeGen.CodeGen.InvokeMethodDelegate> _cachedInvokeMethods = new Dictionary<int, CodeGen.CodeGen.InvokeMethodDelegate>();
|
||||
|
||||
public bool IsTypeValid(string typeName)
|
||||
{
|
||||
var t = Type.GetType(typeName);
|
||||
|
|
@ -174,6 +178,11 @@ namespace Qml.Net.Internal
|
|||
propertyInfo.CanWrite,
|
||||
notifySignal))
|
||||
{
|
||||
foreach (var indexParameter in propertyInfo.GetIndexParameters())
|
||||
{
|
||||
property.AddIndexParameter(indexParameter.Name, NetTypeManager.GetTypeInfo(indexParameter.ParameterType));
|
||||
}
|
||||
|
||||
type.AddProperty(property);
|
||||
}
|
||||
}
|
||||
|
|
@ -221,22 +230,21 @@ namespace Qml.Net.Internal
|
|||
using (var indexParameter = ip != IntPtr.Zero ? new NetVariant(ip) : null)
|
||||
using (var result = new NetVariant(r))
|
||||
{
|
||||
var o = target.Instance;
|
||||
|
||||
var propertInfo = o.GetType()
|
||||
.GetProperty(property.Name, BindingFlags.Instance | BindingFlags.Public);
|
||||
if (propertInfo == null)
|
||||
throw new InvalidOperationException($"Invalid property {property.Name}");
|
||||
|
||||
if (indexParameter != null)
|
||||
CodeGen.CodeGen.InvokeMethodDelegate del;
|
||||
if (!_cachedReadProperties.TryGetValue(property.Id, out del))
|
||||
{
|
||||
object indexParameterValue = null;
|
||||
Helpers.Unpackvalue(ref indexParameterValue, indexParameter);
|
||||
Helpers.PackValue(propertInfo.GetValue(o, new[] { indexParameterValue }), result);
|
||||
del = CodeGen.CodeGen.BuildReadPropertyDelegate(property);
|
||||
_cachedReadProperties[property.Id] = del;
|
||||
}
|
||||
else
|
||||
|
||||
using (var list = indexParameter != null ? new NetVariantList() : null)
|
||||
{
|
||||
Helpers.PackValue(propertInfo.GetValue(o), result);
|
||||
if (indexParameter != null)
|
||||
{
|
||||
list.Add(indexParameter);
|
||||
}
|
||||
|
||||
del(target, list, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -248,29 +256,22 @@ namespace Qml.Net.Internal
|
|||
using (var indexParameter = ip != IntPtr.Zero ? new NetVariant(ip) : null)
|
||||
using (var value = new NetVariant(v))
|
||||
{
|
||||
var o = target.Instance;
|
||||
|
||||
var propertInfo = o.GetType()
|
||||
.GetProperty(property.Name, BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
if (propertInfo == null)
|
||||
throw new InvalidOperationException($"Invalid property {property.Name}");
|
||||
|
||||
object newValue = null;
|
||||
Helpers.Unpackvalue(ref newValue, value);
|
||||
|
||||
if (indexParameter != null)
|
||||
CodeGen.CodeGen.InvokeMethodDelegate del;
|
||||
if (!_cachedSetProperties.TryGetValue(property.Id, out del))
|
||||
{
|
||||
// TODO: Type coercion?
|
||||
object indexParameterValue = null;
|
||||
Helpers.Unpackvalue(ref indexParameterValue, indexParameter);
|
||||
propertInfo.SetValue(o, newValue, new[] { indexParameterValue });
|
||||
del = CodeGen.CodeGen.BuildSetPropertyDelegate(property);
|
||||
_cachedSetProperties[property.Id] = del;
|
||||
}
|
||||
else
|
||||
|
||||
using (var list = new NetVariantList())
|
||||
{
|
||||
var propertyType = propertInfo.PropertyType;
|
||||
var underlyingType = Nullable.GetUnderlyingType(propertyType);
|
||||
propertInfo.SetValue(o, Converter.To(newValue, underlyingType ?? propertyType));
|
||||
if (indexParameter != null)
|
||||
{
|
||||
list.Add(indexParameter);
|
||||
}
|
||||
|
||||
list.Add(value);
|
||||
del(target, list, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -282,84 +283,14 @@ namespace Qml.Net.Internal
|
|||
using (var parameters = new NetVariantList(p))
|
||||
using (var result = r != IntPtr.Zero ? new NetVariant(r) : null)
|
||||
{
|
||||
var instance = target.Instance;
|
||||
|
||||
List<object> methodParameters = null;
|
||||
|
||||
if (parameters.Count > 0)
|
||||
CodeGen.CodeGen.InvokeMethodDelegate del;
|
||||
if (!_cachedInvokeMethods.TryGetValue(method.Id, out del))
|
||||
{
|
||||
methodParameters = new List<object>();
|
||||
var parameterCount = parameters.Count;
|
||||
for (var x = 0; x < parameterCount; x++)
|
||||
{
|
||||
object v = null;
|
||||
Helpers.Unpackvalue(ref v, parameters.Get(x));
|
||||
methodParameters.Add(v);
|
||||
}
|
||||
del = CodeGen.CodeGen.BuildInvokeMethodDelegate(method);
|
||||
_cachedInvokeMethods[method.Id] = del;
|
||||
}
|
||||
|
||||
MethodInfo methodInfo = null;
|
||||
var methodName = method.MethodName;
|
||||
var methods = instance.GetType()
|
||||
.GetMethods(BindingFlags.Instance | BindingFlags.Public)
|
||||
.Where(x => x.Name == methodName)
|
||||
.ToList();
|
||||
|
||||
if (methods.Count == 1)
|
||||
{
|
||||
methodInfo = methods[0];
|
||||
}
|
||||
else if (methods.Count > 1)
|
||||
{
|
||||
// This is an overload.
|
||||
|
||||
// TODO: Make this more performant. https://github.com/pauldotknopf/Qml.Net/issues/39
|
||||
|
||||
// Get all the parameters for the method we are invoking.
|
||||
var parameterTypes = method.GetAllParameters().Select(x => x.Type.FullTypeName).ToList();
|
||||
|
||||
// And find a good method to invoke.
|
||||
foreach (var potentialMethod in methods)
|
||||
{
|
||||
var potentialMethodParameters = potentialMethod.GetParameters();
|
||||
if (potentialMethodParameters.Length != parameterTypes.Count)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool valid = true;
|
||||
for (var x = 0; x < potentialMethodParameters.Length; x++)
|
||||
{
|
||||
if (potentialMethodParameters[x].ParameterType.AssemblyQualifiedName != parameterTypes[x])
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid)
|
||||
{
|
||||
methodInfo = potentialMethod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (methodInfo == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid method name {method.MethodName}");
|
||||
}
|
||||
|
||||
var returnObject = methodInfo.Invoke(instance, methodParameters?.ToArray());
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
// this method doesn't have return type
|
||||
}
|
||||
else
|
||||
{
|
||||
Helpers.PackValue(returnObject, result);
|
||||
}
|
||||
del(target, parameters, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -140,6 +140,45 @@ namespace Qml.Net.Internal.Qml
|
|||
Interop.NetVariant.Clear(Handle);
|
||||
}
|
||||
|
||||
public object AsObject()
|
||||
{
|
||||
switch (VariantType)
|
||||
{
|
||||
case NetVariantType.Invalid:
|
||||
return null;
|
||||
case NetVariantType.Bool:
|
||||
return Bool;
|
||||
case NetVariantType.Char:
|
||||
return Char;
|
||||
case NetVariantType.Int:
|
||||
return Int;
|
||||
case NetVariantType.UInt:
|
||||
return UInt;
|
||||
case NetVariantType.Long:
|
||||
return Long;
|
||||
case NetVariantType.ULong:
|
||||
return ULong;
|
||||
case NetVariantType.Float:
|
||||
return Float;
|
||||
case NetVariantType.Double:
|
||||
return Double;
|
||||
case NetVariantType.String:
|
||||
return String;
|
||||
case NetVariantType.DateTime:
|
||||
return DateTime;
|
||||
case NetVariantType.Object:
|
||||
using (var instance = Instance)
|
||||
{
|
||||
return instance.Instance;
|
||||
}
|
||||
|
||||
case NetVariantType.JsValue:
|
||||
return JsValue.AsDynamic();
|
||||
default:
|
||||
throw new NotImplementedException($"unhandled type {VariantType}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DisposeUnmanaged(IntPtr ptr)
|
||||
{
|
||||
Interop.NetVariant.Destroy(ptr);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Qml.Net.Internal.Types
|
||||
|
|
@ -70,6 +71,31 @@ namespace Qml.Net.Internal.Types
|
|||
}
|
||||
}
|
||||
|
||||
public void AddIndexParameter(string name, NetTypeInfo type)
|
||||
{
|
||||
Interop.NetPropertyInfo.AddIndexParameter(Handle, name, type.Handle);
|
||||
}
|
||||
|
||||
public int IndexParameterCount => Interop.NetPropertyInfo.GetIndexParameterCount(Handle);
|
||||
|
||||
public NetMethodInfoParameter GetIndexParameter(int index)
|
||||
{
|
||||
var result = Interop.NetPropertyInfo.GetIndexParameter(Handle, index);
|
||||
if (result == IntPtr.Zero) return null;
|
||||
return new NetMethodInfoParameter(result);
|
||||
}
|
||||
|
||||
public List<NetMethodInfoParameter> GetAllIndexParameters()
|
||||
{
|
||||
var result = new List<NetMethodInfoParameter>();
|
||||
var count = IndexParameterCount;
|
||||
for (var x = 0; x < count; x++)
|
||||
{
|
||||
result.Add(GetIndexParameter(x));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override void DisposeUnmanaged(IntPtr ptr)
|
||||
{
|
||||
Interop.NetPropertyInfo.Destroy(ptr);
|
||||
|
|
@ -133,5 +159,20 @@ namespace Qml.Net.Internal.Types
|
|||
public SetNotifySignalDel SetNotifySignal { get; set; }
|
||||
|
||||
public delegate void SetNotifySignalDel(IntPtr property, IntPtr signal);
|
||||
|
||||
[NativeSymbol(Entrypoint = "property_info_addIndexParameter")]
|
||||
public AddIndexParameterDel AddIndexParameter { get; set; }
|
||||
|
||||
public delegate void AddIndexParameterDel(IntPtr method, [MarshalAs(UnmanagedType.LPWStr)]string name, IntPtr type);
|
||||
|
||||
[NativeSymbol(Entrypoint = "property_info_getIndexParameterCount")]
|
||||
public GetIndexParameterCountDel GetIndexParameterCount { get; set; }
|
||||
|
||||
public delegate int GetIndexParameterCountDel(IntPtr method);
|
||||
|
||||
[NativeSymbol(Entrypoint = "property_info_getIndexParameter")]
|
||||
public GetIndexParameterDel GetIndexParameter { get; set; }
|
||||
|
||||
public delegate IntPtr GetIndexParameterDel(IntPtr method, int index);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue