[GH-ISSUE #71] Objects are not surviving re-entry. #43

Closed
opened 2026-05-05 11:00:59 -06:00 by gitea-mirror · 3 comments
Owner

Originally created by @pauldotknopf on GitHub (Sep 24, 2018).
Original GitHub issue: https://github.com/qmlnet/qmlnet/issues/71

I checked in a repro in the sandbox project here: 92b9e20726

This is the output

test
file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null
test
file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null
test
file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null
test
file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null
test
file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null
test
file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null
test

Use QV4_MM_AGGRESSIVE_GC=1 to reproduce quicker.

Originally created by @pauldotknopf on GitHub (Sep 24, 2018). Original GitHub issue: https://github.com/qmlnet/qmlnet/issues/71 I checked in a repro in the sandbox project here: https://github.com/qmlnet/qmlnet/commit/92b9e20726fd62f74a4d6edf96f35f1cd43e50bb This is the output ``` test file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null test file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null test file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null test file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null test file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null test file:///Users/pknopf/git/net-core-qml/src/net/Qml.Net.Sandbox/main.qml:15: TypeError: Cannot call method 'method' of null test ``` Use ```QV4_MM_AGGRESSIVE_GC=1``` to reproduce quicker.
Author
Owner

@pauldotknopf commented on GitHub (Sep 24, 2018):

It seems as if we made some wrong assumptions about how JavaScriptOwnership works.

When QML tracks a QObject with JavaScriptOwnership, it tracks it within the current scope. If that same QObject is entered into QML from somewhere else, it doesn't know about the other instance.

We made an assumption that QML knows that there is more than one instance of the QObject present, so only delete the QObject when all scoped usages are done.

I'm currently thinking of another approach.

<!-- gh-comment-id:423859314 --> @pauldotknopf commented on GitHub (Sep 24, 2018): It seems as if we made some wrong assumptions about how ```JavaScriptOwnership``` works. When QML tracks a ```QObject``` with ```JavaScriptOwnership```, it tracks it within the current scope. If that same ```QObject``` is entered into QML from somewhere else, it doesn't know about the other instance. We made an assumption that QML *knows* that there is more than one instance of the ```QObject``` present, so only delete the ```QObject``` when all scoped usages are done. I'm currently thinking of another approach.
Author
Owner

@pauldotknopf commented on GitHub (Sep 24, 2018):

Ok, so this is what I'm thinking.

A 1-N of NetValueRoot->NetValue.

  • When creating a NetValue, a NetValueRoot will be created if not already.
  • When enter/reentry of .NET into QML, a new NetValue will be created with JavaScriptOwnership.
  • All NetValue instances will point to the same NetValueRoot.
  • When the final NetValue is destroyed, NetValueRoot will be deleted as well, releasing the .NET reference for garbage collection.
  • Override connectNotify/disconnectNotify/metaCall on NetValue to proxy signals/slots to the inner NetValueRoot. This will allow each NetValue instance that is alive in QML to seem as if it is the same exact QObject that we are interacting with.
  • When raising signals from .NET, we will find the NetValueRoot and activate the signal on it. All the NetValue types will be listening to the signals of the inner NetValueRoot.
<!-- gh-comment-id:423860378 --> @pauldotknopf commented on GitHub (Sep 24, 2018): Ok, so this is what I'm thinking. A 1-N of ```NetValueRoot```->```NetValue```. * When creating a ```NetValue```, a ```NetValueRoot``` will be created if not already. * When enter/reentry of .NET into QML, a new ```NetValue``` will be created with ```JavaScriptOwnership```. * All ```NetValue``` instances will point to the same ```NetValueRoot```. * When the final ```NetValue``` is destroyed, ```NetValueRoot``` will be deleted as well, releasing the .NET reference for garbage collection. * Override ```connectNotify```/```disconnectNotify```/```metaCall``` on ```NetValue``` to proxy signals/slots to the inner ```NetValueRoot```. This will allow each ```NetValue``` instance that is alive in QML to *seem* as if it is the same exact ```QObject``` that we are interacting with. * When raising signals from .NET, we will find the ```NetValueRoot``` and activate the signal on it. All the ```NetValue``` types will be listening to the signals of the inner ```NetValueRoot```.
Author
Owner

@pauldotknopf commented on GitHub (Sep 24, 2018):

Or, we could leave things as they are, with some slight modifications, and some features removed (that probably won't matter much).

  • Create multiple NetValue types for all QML scopes.
  • Store every NetValue created for each .NET reference id.
  • When NetValue::~NetValue() is called, clean up it's reference from the static list of current NetValue instances for the given .NET reference id.
  • When raising signals from .NET, iterate through each instance of NetValue that is present and activate the signal.

What do we lose? Any QML attached signal handler won't survive reentry. For example, considering the following sequence:

function method1() {
    var o = netService.netSharedNetObject()
    o.signal.connect(function() {
        console.log('signal raised')
    })
}
function method2() {
    var o = netService.netSharedNetObject()
    // This won't raise the handler attached from "method1()".
    o.signal()
}
method1()
method2()

In this case, the attached signal handler will not get raised. However, the following will work:

function method1() {
    var o = netService.netSharedNetObject()
    o.signal.connect(function() {
        console.log('signal raised')
    })
    // This will raise the signal handler
    o.signal()
}
method1()

In this case, the signal handler will get called, because we are interacting with the same js reference.

<!-- gh-comment-id:423861824 --> @pauldotknopf commented on GitHub (Sep 24, 2018): Or, we could leave things as they are, with some slight modifications, and some features removed (that probably won't matter much). * Create multiple ```NetValue``` types for all QML scopes. * Store every ```NetValue``` created for each .NET reference id. * When ```NetValue::~NetValue()``` is called, clean up it's reference from the static list of current ```NetValue``` instances for the given .NET reference id. * When raising signals from .NET, iterate through each instance of ```NetValue``` that is present and activate the signal. What do we lose? Any QML attached signal handler won't survive reentry. For example, considering the following sequence: ```js function method1() { var o = netService.netSharedNetObject() o.signal.connect(function() { console.log('signal raised') }) } function method2() { var o = netService.netSharedNetObject() // This won't raise the handler attached from "method1()". o.signal() } method1() method2() ``` In this case, the attached signal handler will *not* get raised. However, the following will work: ```js function method1() { var o = netService.netSharedNetObject() o.signal.connect(function() { console.log('signal raised') }) // This will raise the signal handler o.signal() } method1() ``` In this case, the signal handler will get called, because we are interacting with the *same js reference*.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: github-starred/qmlnet#43
No description provided.