mirror of
https://github.com/qmlnet/qmlnet.git
synced 2026-05-21 06:45:32 -06:00
NetReference gets created for each .Net ref
(instad of for each .Net object) Object Tags get passed as objectIds and this id is used to find a NetValue (which represents the .Net instance) In contrast to before now a GCHandle gets created every time a .Net object enters the QML boundary
This commit is contained in:
parent
ee7765dfe8
commit
177aec0537
12 changed files with 63 additions and 59 deletions
|
|
@ -5,10 +5,10 @@
|
|||
|
||||
NetValue::~NetValue()
|
||||
{
|
||||
auto hit = netValues.find(instance.data());
|
||||
if(hit != netValues.end())
|
||||
auto hit = objectIdNetValuesMap.find(instance->getObjectId());
|
||||
if(hit != objectIdNetValuesMap.end())
|
||||
{
|
||||
netValues.erase(hit);
|
||||
objectIdNetValuesMap.erase(hit);
|
||||
}
|
||||
qDebug("NetValue deleted: %s", qPrintable(instance->getTypeInfo()->getClassName()));
|
||||
if(instance != nullptr) {
|
||||
|
|
@ -78,9 +78,10 @@ bool NetValue::activateSignal(QString signalName, QSharedPointer<NetVariantList>
|
|||
|
||||
NetValue* NetValue::forInstance(QSharedPointer<NetReference> instance, bool autoCreate)
|
||||
{
|
||||
if(netValues.find(instance.data()) != netValues.end())
|
||||
auto objectId = instance->getObjectId();
|
||||
if(objectIdNetValuesMap.find(objectId) != objectIdNetValuesMap.end())
|
||||
{
|
||||
return netValues.at(instance.data());
|
||||
return objectIdNetValuesMap.at(objectId);
|
||||
}
|
||||
if(!autoCreate)
|
||||
{
|
||||
|
|
@ -96,8 +97,8 @@ NetValue::NetValue(QSharedPointer<NetReference> instance, QObject *parent)
|
|||
{
|
||||
valueMeta = new NetValueMetaObject(this, instance);
|
||||
setParent(parent);
|
||||
netValues[instance.data()] = this;
|
||||
objectIdNetValuesMap[instance->getObjectId()] = this;
|
||||
qDebug("NetValue created: %s", qPrintable(instance->getTypeInfo()->getClassName()));
|
||||
}
|
||||
|
||||
std::map<NetReference*, NetValue*> NetValue::netValues = std::map<NetReference*, NetValue*>();
|
||||
std::map<uint64_t, NetValue*> NetValue::objectIdNetValuesMap = std::map<uint64_t, NetValue*>();
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ private:
|
|||
QSharedPointer<NetReference> instance;
|
||||
NetValueMetaObject* valueMeta;
|
||||
|
||||
static std::map<NetReference*, NetValue*> netValues;
|
||||
static std::map<uint64_t, NetValue*> objectIdNetValuesMap;
|
||||
};
|
||||
|
||||
#endif // NETVALUE_H
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
#include <QmlNet/qml/NetValue.h>
|
||||
#include <QDebug>
|
||||
|
||||
NetReference::NetReference(NetGCHandle* gcHandle, QSharedPointer<NetTypeInfo> typeInfo) :
|
||||
NetReference::NetReference(NetGCHandle* gcHandle, uint64_t objectId, QSharedPointer<NetTypeInfo> typeInfo) :
|
||||
gcHandle(gcHandle),
|
||||
objectId(objectId),
|
||||
typeInfo(typeInfo)
|
||||
{
|
||||
qDebug("NetReference created: %s", qPrintable(typeInfo->getClassName()));
|
||||
|
|
@ -20,6 +21,11 @@ NetGCHandle* NetReference::getGCHandle()
|
|||
return gcHandle;
|
||||
}
|
||||
|
||||
uint64_t NetReference::getObjectId()
|
||||
{
|
||||
return objectId;
|
||||
}
|
||||
|
||||
QSharedPointer<NetTypeInfo> NetReference::getTypeInfo()
|
||||
{
|
||||
return typeInfo;
|
||||
|
|
@ -38,9 +44,9 @@ void NetReference::release()
|
|||
|
||||
extern "C" {
|
||||
|
||||
Q_DECL_EXPORT NetReferenceContainer* net_instance_create(NetGCHandle* handle, NetTypeInfoContainer* typeContainer) {
|
||||
Q_DECL_EXPORT NetReferenceContainer* net_instance_create(NetGCHandle* handle, uint64_t objectId, NetTypeInfoContainer* typeContainer) {
|
||||
NetReferenceContainer* result = new NetReferenceContainer();
|
||||
result->instance = QSharedPointer<NetReference>(new NetReference(handle, typeContainer->netTypeInfo));
|
||||
result->instance = QSharedPointer<NetReference>(new NetReference(handle, objectId, typeContainer->netTypeInfo));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -57,6 +63,10 @@ Q_DECL_EXPORT NetGCHandle* net_instance_getHandle(NetReferenceContainer* contain
|
|||
return container->instance->getGCHandle();
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT uint64_t net_instance_getObjectId(NetReferenceContainer* container) {
|
||||
return container->instance->getObjectId();
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT bool net_instance_activateSignal(NetReferenceContainer* container, LPWSTR signalName, NetVariantListContainer* parametersContainer) {
|
||||
NetValue* existing = NetValue::forInstance(container->instance);
|
||||
if(existing == NULL) {
|
||||
|
|
|
|||
|
|
@ -6,14 +6,16 @@
|
|||
class NetReference
|
||||
{
|
||||
public:
|
||||
NetReference(NetGCHandle* gcHandle, QSharedPointer<NetTypeInfo> typeInfo);
|
||||
NetReference(NetGCHandle* gcHandle, uint64_t objectId, QSharedPointer<NetTypeInfo> typeInfo);
|
||||
~NetReference();
|
||||
NetGCHandle* getGCHandle();
|
||||
uint64_t getObjectId();
|
||||
QSharedPointer<NetTypeInfo> getTypeInfo();
|
||||
|
||||
void release();
|
||||
private:
|
||||
NetGCHandle* gcHandle;
|
||||
uint64_t objectId;
|
||||
QSharedPointer<NetTypeInfo> typeInfo;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace Qml.Net.Tests.Qml
|
|||
var testObject = new TestObject();
|
||||
var variant = new NetVariant();
|
||||
variant.Instance.Should().BeNull();
|
||||
variant.Instance = NetReference.GetForObject(testObject);
|
||||
variant.Instance = NetReference.CreateForObject(testObject);
|
||||
variant.Instance.Should().NotBeNull();
|
||||
variant.Instance.Instance.Should().Be(testObject);
|
||||
variant.VariantType.Should().Be(NetVariantType.Object);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace Qml.Net.Tests.Types
|
|||
{
|
||||
var o = new TestObject();
|
||||
reference = new WeakReference(o);
|
||||
instance = NetReference.GetForObject(o);
|
||||
instance = NetReference.CreateForObject(o);
|
||||
}).Wait();
|
||||
|
||||
// NetReference is still alive, so the weak reference must be alive as well.
|
||||
|
|
@ -67,7 +67,7 @@ namespace Qml.Net.Tests.Types
|
|||
var o = new TestObject();
|
||||
var type = NetTypeManager.GetTypeInfo<TestObject>();
|
||||
var method = type.GetMethod(0);
|
||||
var instance = NetReference.GetForObject(o);
|
||||
var instance = NetReference.CreateForObject(o);
|
||||
|
||||
// This will jump to native, to then call the .NET delegate (round trip).
|
||||
// The purpose is to simulate Qml invoking a method, sending .NET instance back.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Qml.Net.Tests.Types
|
|||
public void Can_create_net_instance()
|
||||
{
|
||||
var o = new TestObject();
|
||||
var instance = NetReference.GetForObject(o);
|
||||
var instance = NetReference.CreateForObject(o);
|
||||
|
||||
var returnedInstance = instance.Instance;
|
||||
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ namespace Qml.Net.Internal
|
|||
var typeCreator = NetReference.TypeCreator;
|
||||
var instance = typeCreator != null ? typeCreator.Create(typeInfo) : Activator.CreateInstance(typeInfo);
|
||||
|
||||
var netReference = NetReference.GetForObject(instance);
|
||||
var netReference = NetReference.CreateForObject(instance);
|
||||
// When .NET collects this NetReference, we don't want it to delete this
|
||||
// handle. Ownership has been passed to the caller.
|
||||
return Interop.NetReference.Clone(netReference.Handle);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace Qml.Net.Internal
|
|||
destination.DateTime = (DateTime)source;
|
||||
else
|
||||
{
|
||||
destination.Instance = NetReference.GetForObject(source);
|
||||
destination.Instance = NetReference.CreateForObject(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ namespace Qml.Net.Internal
|
|||
{
|
||||
}
|
||||
|
||||
private ObjectId(ulong id)
|
||||
private ObjectId(UInt64 id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public ulong Id { get; private set; }
|
||||
public UInt64 Id { get; private set; }
|
||||
|
||||
public static implicit operator ulong(ObjectId oId)
|
||||
public static implicit operator UInt64(ObjectId oId)
|
||||
{
|
||||
return oId.Id;
|
||||
}
|
||||
|
|
@ -32,23 +32,23 @@ namespace Qml.Net.Internal
|
|||
|
||||
#region Id management
|
||||
|
||||
private static ulong NextId = 1;
|
||||
private static HashSet<ulong> UsedIds = new HashSet<ulong>();
|
||||
private static UInt64 NextId = 1;
|
||||
private static HashSet<UInt64> UsedIds = new HashSet<UInt64>();
|
||||
|
||||
private static ulong TakeNextFreeId()
|
||||
private static UInt64 TakeNextFreeId()
|
||||
{
|
||||
ulong nextId = NextId;
|
||||
UInt64 nextId = NextId;
|
||||
UsedIds.Add(nextId);
|
||||
NextId = CalculateNextFreeId(nextId);
|
||||
return nextId;
|
||||
}
|
||||
|
||||
private static ulong CalculateNextFreeId(ulong nextId)
|
||||
private static UInt64 CalculateNextFreeId(UInt64 nextId)
|
||||
{
|
||||
bool firstPass = true;
|
||||
while (UsedIds.Contains(nextId))
|
||||
{
|
||||
if(nextId == ulong.MaxValue)
|
||||
if(nextId == UInt64.MaxValue)
|
||||
{
|
||||
if(!firstPass)
|
||||
{
|
||||
|
|
@ -65,7 +65,7 @@ namespace Qml.Net.Internal
|
|||
return nextId;
|
||||
}
|
||||
|
||||
private static void FreeId(ulong id)
|
||||
private static void FreeId(UInt64 id)
|
||||
{
|
||||
UsedIds.Remove(id);
|
||||
}
|
||||
|
|
@ -82,7 +82,7 @@ namespace Qml.Net.Internal
|
|||
{
|
||||
private static readonly ConditionalWeakTable<object, ObjectId> ObjectIdRefs = new ConditionalWeakTable<object, ObjectId>();
|
||||
|
||||
public static ulong GetOrCreateTag(this object obj)
|
||||
public static UInt64 GetOrCreateTag(this object obj)
|
||||
{
|
||||
var result = GetTag(obj);
|
||||
if(result.HasValue)
|
||||
|
|
@ -94,7 +94,7 @@ namespace Qml.Net.Internal
|
|||
return newObjId;
|
||||
}
|
||||
|
||||
public static ulong? GetTag(this object obj)
|
||||
public static UInt64? GetTag(this object obj)
|
||||
{
|
||||
if (ObjectIdRefs.TryGetValue(obj, out var objId))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace Qml.Net
|
|||
{
|
||||
public static bool ActivateSignal(this object instance, string signalName, params object[] args)
|
||||
{
|
||||
var existing = NetReference.GetForObject(instance, false /*don't create one if doesn't exist*/);
|
||||
var existing = NetReference.CreateForObject(instance);
|
||||
return existing != null && existing.ActivateSignal(signalName, args);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Serialization;
|
||||
using AdvancedDLSupport;
|
||||
using Qml.Net.Internal;
|
||||
using Qml.Net.Qml;
|
||||
|
|
@ -9,8 +10,8 @@ namespace Qml.Net.Types
|
|||
{
|
||||
public class NetReference : BaseDisposable
|
||||
{
|
||||
private NetReference(IntPtr gcHandle, NetTypeInfo type, bool ownsHandle = true)
|
||||
:base(Interop.NetReference.Create(gcHandle, type.Handle), ownsHandle)
|
||||
private NetReference(IntPtr gcHandle, UInt64 objectId, NetTypeInfo type, bool ownsHandle = true)
|
||||
:base(Interop.NetReference.Create(gcHandle, objectId, type.Handle), ownsHandle)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -29,6 +30,14 @@ namespace Qml.Net.Types
|
|||
}
|
||||
}
|
||||
|
||||
public ulong ObjectId
|
||||
{
|
||||
get
|
||||
{
|
||||
return Interop.NetReference.GetObjectId(Handle);
|
||||
}
|
||||
}
|
||||
|
||||
public NetReference Clone()
|
||||
{
|
||||
return new NetReference(Interop.NetReference.Clone(Handle));
|
||||
|
|
@ -74,37 +83,17 @@ namespace Qml.Net.Types
|
|||
return type;
|
||||
}
|
||||
|
||||
private static readonly ConditionalWeakTable<object, NetReference> ObjectNetReferenceConnections = new ConditionalWeakTable<object, NetReference>();
|
||||
|
||||
public static bool ExistsForObject(object value)
|
||||
{
|
||||
return ObjectNetReferenceConnections.TryGetValue(value, out NetReference NetReference);
|
||||
}
|
||||
|
||||
public static NetReference GetForObject(object value, bool autoCreate = true)
|
||||
public static NetReference CreateForObject(object value)
|
||||
{
|
||||
if (value == null) return null;
|
||||
var alreadyExists = false;
|
||||
if (ObjectNetReferenceConnections.TryGetValue(value, out var NetReference))
|
||||
{
|
||||
alreadyExists = true;
|
||||
if (GCHandle.FromIntPtr(NetReference.Handle).IsAllocated)
|
||||
{
|
||||
return NetReference;
|
||||
}
|
||||
}
|
||||
|
||||
if (!autoCreate) return null;
|
||||
|
||||
var typeInfo = NetTypeManager.GetTypeInfo(GetUnproxiedType(value.GetType()).AssemblyQualifiedName);
|
||||
if(typeInfo == null) throw new InvalidOperationException($"Couldn't create type info from {value.GetType().AssemblyQualifiedName}");
|
||||
var handle = GCHandle.Alloc(value);
|
||||
var newNetReference = new NetReference(GCHandle.ToIntPtr(handle), typeInfo);
|
||||
if(alreadyExists)
|
||||
{
|
||||
ObjectNetReferenceConnections.Remove(value);
|
||||
}
|
||||
ObjectNetReferenceConnections.Add(value, newNetReference);
|
||||
|
||||
var objectId = value.GetOrCreateTag();
|
||||
var newNetReference = new NetReference(GCHandle.ToIntPtr(handle), objectId, typeInfo);
|
||||
|
||||
return newNetReference;
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +103,7 @@ namespace Qml.Net.Types
|
|||
public interface INetReferenceInterop
|
||||
{
|
||||
[NativeSymbol(Entrypoint = "net_instance_create")]
|
||||
IntPtr Create(IntPtr handle, IntPtr type);
|
||||
IntPtr Create(IntPtr handle, UInt64 objectId, IntPtr type);
|
||||
[NativeSymbol(Entrypoint = "net_instance_destroy")]
|
||||
void Destroy(IntPtr instance);
|
||||
[NativeSymbol(Entrypoint = "net_instance_clone")]
|
||||
|
|
@ -122,6 +111,8 @@ namespace Qml.Net.Types
|
|||
|
||||
[NativeSymbol(Entrypoint = "net_instance_getHandle")]
|
||||
IntPtr GetHandle(IntPtr instance);
|
||||
[NativeSymbol(Entrypoint = "net_instance_getObjectId")]
|
||||
UInt64 GetObjectId(IntPtr instance);
|
||||
[NativeSymbol(Entrypoint = "net_instance_activateSignal")]
|
||||
bool ActivateSignal(IntPtr instance, [MarshalAs(UnmanagedType.LPWStr)]string signalName, IntPtr variants);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue