From eea7fc8a510db32f5e408bf2f681da5ffb66d2e3 Mon Sep 17 00:00:00 2001 From: Henna Myllys Date: Thu, 23 Mar 2023 10:40:14 +0100 Subject: [PATCH 01/11] Persistent storage implement syncPeerHasChanged to receive PU change notifications Signed-off-by: Henna Myllys Change-Id: I55bdf64cbd0228033529195db93da438a3b24ee3 --- .../bridge/declarative_frontend/engine/stateMgmt.js | 8 ++++++++ .../state_mgmt/src/lib/sdk/persistent_storage.ts | 13 ++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index 2b072a6f222..2742f436509 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -1176,6 +1176,7 @@ class PersistentStorage { */ static ConfigureBackend(storage) { PersistentStorage.Storage_ = storage; + } /** * private, use static functions! @@ -1337,6 +1338,13 @@ class PersistentStorage { this.write(); } + syncPeerHasChanged(eventSource) { + + this.write(); + } + propertyHasBeenReadPU(eventSource) { + // not needed + } // public required by the interface, use the static method instead! aboutToBeDeleted() { diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/persistent_storage.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/persistent_storage.ts index ac7e903c722..d346c0f3c17 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/persistent_storage.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/persistent_storage.ts @@ -21,7 +21,8 @@ * since 9 */ -class PersistentStorage implements IMultiPropertiesChangeSubscriber { +class PersistentStorage implements IMultiPropertiesChangeSubscriber, + PropertyEventsReceiverPU { private static Storage_: IStorage; private static Instance_: PersistentStorage = undefined; @@ -38,6 +39,7 @@ class PersistentStorage implements IMultiPropertiesChangeSubscriber { */ public static ConfigureBackend(storage: IStorage): void { PersistentStorage.Storage_ = storage; + stateMgmtConsole.debug(`PersistentStorage backend configured`); } /** @@ -237,6 +239,15 @@ class PersistentStorage implements IMultiPropertiesChangeSubscriber { this.write(); } + public syncPeerHasChanged(eventSource: ObservedPropertyAbstractPU) { + stateMgmtConsole.debug(`PersistentStorage: sync peer ${eventSource.info()} has changed`); + this.write(); + } + + public propertyHasBeenReadPU(eventSource: ObservedPropertyAbstractPU) { + // not needed + } + // public required by the interface, use the static method instead! public aboutToBeDeleted(): void { stateMgmtConsole.debug("PersistentStorage: about to be deleted"); -- Gitee From c476372d65e5e342749e2a7e158509e80003d198 Mon Sep 17 00:00:00 2001 From: Henna Myllys Date: Thu, 23 Mar 2023 10:42:26 +0100 Subject: [PATCH 02/11] Clear internal storage Signed-off-by: Henna Myllys Change-Id: I54bbf10f9dabddb646de329c591442e6d0112deb --- frameworks/bridge/declarative_frontend/engine/stateMgmt.js | 4 +++- .../state_mgmt/src/lib/sdk/local_storage.ts | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index 2742f436509..ac157894ac6 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -362,7 +362,9 @@ var p = this.storage_.get(propName); p.aboutToBeDeleted(); } - + this.storage_.clear(); + + return true; } /** * Subscribe to value change notifications of named property diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/local_storage.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/local_storage.ts index 7a1dcaaab97..c8f02c976f7 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/local_storage.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/local_storage.ts @@ -349,6 +349,7 @@ class LocalStorage extends NativeLocalStorage { var p: ObservedPropertyAbstract = this.storage_.get(propName); p.aboutToBeDeleted(); } + this.storage_.clear(); stateMgmtConsole.debug(`${this.constructor.name}.deleteAll: success`); } -- Gitee From bcf67f712d756a5a6ee88d2a791e54cc8e16fe8d Mon Sep 17 00:00:00 2001 From: Henna Myllys Date: Thu, 23 Mar 2023 11:49:13 +0100 Subject: [PATCH 03/11] Make Array handling a bit safer Signed-off-by: Henna Myllys Change-Id: Id0f812358eaad0580dbf66ca04673cac8a82b7c6 --- frameworks/bridge/declarative_frontend/engine/stateMgmt.js | 3 ++- .../state_mgmt/src/lib/common/observed_object.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index ac157894ac6..40fd39bb799 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -1859,10 +1859,11 @@ class SubscribableArrayHandler extends SubscribableHandler { if (this.arrFunctions.includes(property.toString()) && typeof ret === "function" && target["length"] > 0) { const self = this; + const prop = property.toString(); return function () { // execute original function with given arguments ret.apply(this, arguments); - self.notifyObjectPropertyHasChanged(property.toString(), this[0]); + self.notifyObjectPropertyHasChanged(prop, this); }.bind(target); // bind "this" to target inside the function } return ret; diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts index a0f39441d7c..ece85c6a48a 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts @@ -203,10 +203,11 @@ class SubscribableArrayHandler extends SubscribableHandler { if (this.arrFunctions.includes(property.toString()) && typeof ret === "function" && target["length"] > 0) { const self = this; + const prop = property.toString(); return function() { // execute original function with given arguments ret.apply(this, arguments); - self.notifyObjectPropertyHasChanged(property.toString(), this[0]); + self.notifyObjectPropertyHasChanged(prop, this); }.bind(target) // bind "this" to target inside the function } -- Gitee From ba11786f33a99ba013c87c0998d78fc59763b564 Mon Sep 17 00:00:00 2001 From: Henna Myllys Date: Fri, 24 Feb 2023 19:28:01 +0100 Subject: [PATCH 04/11] Observed decorator revisited to create ObservedObject proxy for all needed objects. Signed-off-by: Henna Myllys Change-Id: I3e2a3d1b29e2060265689c09ff3aa34f8cdee3e6 --- .../declarative_frontend/engine/stateMgmt.js | 47 +++++++++---------- .../src/lib/common/observed_object.ts | 30 ++++++------ 2 files changed, 36 insertions(+), 41 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index 40fd39bb799..53f42be3669 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -1712,20 +1712,17 @@ class DistributedStorage { * obsObj = ObservedObject.createNew([]) */ const Observed = function () { - let object_creation_ongoing__ = 0; return function Observed(target) { + const IS_PROXIED = Symbol('___is_proxied___'); const Observed = class extends target { constructor(...args) { - object_creation_ongoing__ += 1; super(...args); - object_creation_ongoing__ -= 1; - if (object_creation_ongoing__ == 0) { - return ObservedObject.createNew(this, null); - } - else { - return this; - } + let isProxied = this.IS_PROXIED; + Object.defineProperty(this, IS_PROXIED, { value: true }); + return isProxied + ? this + : ObservedObject.createNew(this, null); } }; return Observed; @@ -2099,7 +2096,7 @@ class ObservedPropertyAbstract extends SubscribedAbstractProperty { this.subscribers_.delete(subscriberId); } notifyHasChanged(newValue) { - + /* */ this.subscribers_.forEach((subscribedId) => { var subscriber = SubscriberManager.Find(subscribedId); if (subscriber) { @@ -2122,7 +2119,7 @@ class ObservedPropertyAbstract extends SubscribedAbstractProperty { }); } notifyPropertyRead() { - + /* */ this.subscribers_.forEach((subscribedId) => { var subscriber = SubscriberManager.Find(subscribedId); if (subscriber) { @@ -2313,7 +2310,7 @@ class ObservedPropertyObject extends ObservedPropertyObjectAbstract { return true; } get() { - + /* */ this.notifyPropertyRead(); return this.wrappedValue_; } @@ -2985,11 +2982,11 @@ class ObservedPropertyAbstractPU extends ObservedPropertyAbstract { this.dependentElementIds_ = new Set(); } notifyPropertyRead() { - stateMgmtConsole.error(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: \ + stateMgmtConsole.error(`ObservedPropertyAbstractPU[${this.id__()}, '${this.info() || "unknown"}']: \ notifyPropertyRead, DO NOT USE with PU. Use notifyPropertryHasBeenReadPU`); } notifyPropertryHasBeenReadPU() { - + /* */ this.subscribers_.forEach((subscribedId) => { var subscriber = SubscriberManager.Find(subscribedId); if (subscriber) { @@ -3001,7 +2998,7 @@ class ObservedPropertyAbstractPU extends ObservedPropertyAbstract { this.recordDependentUpdate(); } notifyPropertryHasChangedPU() { - + /* */ this.subscribers_.forEach((subscribedId) => { var subscriber = SubscriberManager.Find(subscribedId); if (subscriber) { @@ -3212,7 +3209,7 @@ class ObservedPropertyObjectPU extends ObservedPropertyObjectAbstractPU { */ setValueInternal(newValue) { if (typeof newValue !== 'object') { - stateMgmtConsole.error(`ObservedPropertyObject[${this.id__()}, '${this.info() || "unknown"}'] new value is NOT an object. Application error. Ignoring set.`); + stateMgmtConsole.error(`ObservedPropertyObjectPU[${this.id__()}, '${this.info() || "unknown"}'] new value is NOT an object. Application error. Ignoring set.`); return false; } if (newValue == this.wrappedValue_) { @@ -3242,8 +3239,8 @@ class ObservedPropertyObjectPU extends ObservedPropertyObjectAbstractPU { return this.wrappedValue_; } getUnmonitored() { - - // unmonitored get access , no call to otifyPropertyRead ! + /* */ + // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return this.wrappedValue_; } set(newValue) { @@ -3321,7 +3318,7 @@ class ObservedPropertySimplePU extends ObservedPropertySimpleAbstractPU { } getUnmonitored() { - // unmonitored get access , no call to otifyPropertyRead ! + // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return this.wrappedValue_; } get() { @@ -3614,13 +3611,13 @@ class SynchedPropertyObjectTwoWayPU extends ObservedPropertyObjectAbstractPU { this.notifyPropertryHasChangedPU(); } getUnmonitored() { - - // unmonitored get access , no call to otifyPropertyRead ! + /* */ + // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return (this.source_ ? this.source_.getUnmonitored() : undefined); } // get 'read through` from the ObservedProperty get() { - + /* */ this.notifyPropertryHasBeenReadPU(); return this.getUnmonitored(); } @@ -3718,7 +3715,7 @@ class SynchedPropertySimpleOneWayPU extends ObservedPropertySimpleAbstractPU { } getUnmonitored() { - // unmonitored get access , no call to otifyPropertyRead ! + // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return this.wrappedValue_; } // get 'read through` from the ObservedProperty @@ -3886,8 +3883,8 @@ class SynchedPropertyNesedObjectPU extends ObservedPropertyObjectAbstractPU { this.notifyPropertryHasChangedPU(); } getUnmonitored() { - // - // unmonitored get access , no call to otifyPropertyRead ! + /* */ + // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return this.obsObject_; } // get 'read through` from the ObservedProperty diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts index ece85c6a48a..1f16301e6b9 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts @@ -43,23 +43,21 @@ interface Type extends Function { * obsObj = ObservedObject.createNew([]) */ const Observed: (obj) => any = function () { - let object_creation_ongoing__ = 0; - return function Observed(target: any): any { - stateMgmtConsole.debug(`@Observed: define ${target.name} extended`); - const Observed = class extends target { - constructor(...args) { - object_creation_ongoing__ += 1; - super(...args); - object_creation_ongoing__ -= 1; - if (object_creation_ongoing__ == 0) { - return ObservedObject.createNew(this, null); - } else { - return this; + return function Observed(target: any): any { + const IS_PROXIED = Symbol('___is_proxied___'); + stateMgmtConsole.debug(`@Observed: define ${target.name} extended`); + const Observed = class extends target { + constructor(...args) { + super(...args); + let isProxied = this.IS_PROXIED; + Object.defineProperty(this, IS_PROXIED, {value: true}); + return isProxied + ? this + : ObservedObject.createNew(this, null); } - } - }; - return Observed; - } + }; + return Observed; + } }() -- Gitee From a940770dfcb5dcf49248185e49a8d288f5ad130d Mon Sep 17 00:00:00 2001 From: tomppo Date: Mon, 20 Feb 2023 14:17:16 +0200 Subject: [PATCH 05/11] Remove some of debug traces to avoid circular event loop when using debug version of stateMgmt.js Change-Id: I30d1520c6b5eb5ddadb4b751d2bab5ebe607d31f Signed-off-by: tomppo --- .../declarative_frontend/engine/stateMgmt.js | 9 --------- .../lib/common/observed_property_abstract.ts | 2 -- .../full_update/fu_observed_property_object.ts | 1 - .../pu_observed_property_abstract.ts | 4 +--- .../pu_observed_property_object.ts | 17 ++++++++--------- .../pu_observed_property_simple.ts | 2 +- .../pu_synced_property_object_nested.ts | 3 +-- .../pu_synced_property_object_two_way.ts | 4 +--- .../pu_synced_property_simple_one_way.ts | 2 +- 9 files changed, 13 insertions(+), 31 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index 53f42be3669..1cc485db572 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -2096,7 +2096,6 @@ class ObservedPropertyAbstract extends SubscribedAbstractProperty { this.subscribers_.delete(subscriberId); } notifyHasChanged(newValue) { - /* */ this.subscribers_.forEach((subscribedId) => { var subscriber = SubscriberManager.Find(subscribedId); if (subscriber) { @@ -2119,7 +2118,6 @@ class ObservedPropertyAbstract extends SubscribedAbstractProperty { }); } notifyPropertyRead() { - /* */ this.subscribers_.forEach((subscribedId) => { var subscriber = SubscriberManager.Find(subscribedId); if (subscriber) { @@ -2310,7 +2308,6 @@ class ObservedPropertyObject extends ObservedPropertyObjectAbstract { return true; } get() { - /* */ this.notifyPropertyRead(); return this.wrappedValue_; } @@ -2986,7 +2983,6 @@ class ObservedPropertyAbstractPU extends ObservedPropertyAbstract { notifyPropertyRead, DO NOT USE with PU. Use notifyPropertryHasBeenReadPU`); } notifyPropertryHasBeenReadPU() { - /* */ this.subscribers_.forEach((subscribedId) => { var subscriber = SubscriberManager.Find(subscribedId); if (subscriber) { @@ -2998,7 +2994,6 @@ class ObservedPropertyAbstractPU extends ObservedPropertyAbstract { this.recordDependentUpdate(); } notifyPropertryHasChangedPU() { - /* */ this.subscribers_.forEach((subscribedId) => { var subscriber = SubscriberManager.Find(subscribedId); if (subscriber) { @@ -3239,7 +3234,6 @@ class ObservedPropertyObjectPU extends ObservedPropertyObjectAbstractPU { return this.wrappedValue_; } getUnmonitored() { - /* */ // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return this.wrappedValue_; } @@ -3611,13 +3605,11 @@ class SynchedPropertyObjectTwoWayPU extends ObservedPropertyObjectAbstractPU { this.notifyPropertryHasChangedPU(); } getUnmonitored() { - /* */ // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return (this.source_ ? this.source_.getUnmonitored() : undefined); } // get 'read through` from the ObservedProperty get() { - /* */ this.notifyPropertryHasBeenReadPU(); return this.getUnmonitored(); } @@ -3883,7 +3875,6 @@ class SynchedPropertyNesedObjectPU extends ObservedPropertyObjectAbstractPU { this.notifyPropertryHasChangedPU(); } getUnmonitored() { - /* */ // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return this.obsObject_; } diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_property_abstract.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_property_abstract.ts index 084ea33622b..3e534d2d5d2 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_property_abstract.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_property_abstract.ts @@ -91,7 +91,6 @@ abstract class ObservedPropertyAbstract extends SubscribedAbstractProperty } protected notifyHasChanged(newValue: T) { - stateMgmtConsole.debug(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: notifyHasChanged, notifying.`); this.subscribers_.forEach((subscribedId) => { var subscriber: IPropertySubscriber = SubscriberManager.Find(subscribedId) if (subscriber) { @@ -115,7 +114,6 @@ abstract class ObservedPropertyAbstract extends SubscribedAbstractProperty } protected notifyPropertyRead() { - stateMgmtConsole.debug(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: propertyRead.`) this.subscribers_.forEach((subscribedId) => { var subscriber: IPropertySubscriber = SubscriberManager.Find(subscribedId) if (subscriber) { diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/full_update/fu_observed_property_object.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/full_update/fu_observed_property_object.ts index 302662212c2..8bdab969a26 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/full_update/fu_observed_property_object.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/full_update/fu_observed_property_object.ts @@ -89,7 +89,6 @@ class ObservedPropertyObject extends ObservedPropertyObjectAbs } public get(): T { - stateMgmtConsole.debug(`ObservedPropertyObject[${this.id__()}, '${this.info() || "unknown"}']: get`); this.notifyPropertyRead(); return this.wrappedValue_; } diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_abstract.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_abstract.ts index 11368e09866..057f393a5e2 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_abstract.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_abstract.ts @@ -29,12 +29,11 @@ abstract class ObservedPropertyAbstractPU extends ObservedPropertyAbstract } protected notifyPropertyRead() { - stateMgmtConsole.error(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: \ + stateMgmtConsole.error(`ObservedPropertyAbstractPU[${this.id__()}, '${this.info() || "unknown"}']: \ notifyPropertyRead, DO NOT USE with PU. Use notifyPropertryHasBeenReadPU`); } protected notifyPropertryHasBeenReadPU() { - stateMgmtConsole.debug(`ObservedPropertyAbstractPU[${this.id__()}, '${this.info() || "unknown"}']: propertyHasBeenReadPU.`) this.subscribers_.forEach((subscribedId) => { var subscriber: IPropertySubscriber = SubscriberManager.Find(subscribedId) if (subscriber) { @@ -47,7 +46,6 @@ abstract class ObservedPropertyAbstractPU extends ObservedPropertyAbstract } protected notifyPropertryHasChangedPU() { - stateMgmtConsole.debug(`ObservedPropertyAbstractPU[${this.id__()}, '${this.info() || "unknown"}']: notifyPropertryHasChangedPU.`) this.subscribers_.forEach((subscribedId) => { var subscriber: IPropertySubscriber = SubscriberManager.Find(subscribedId) if (subscriber) { diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_object.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_object.ts index ba78afdba09..e6ecd0b6b53 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_object.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_object.ts @@ -50,7 +50,7 @@ class ObservedPropertyObjectPU extends ObservedPropertyObjectA * @param eventSource */ syncPeerHasChanged(eventSource : ObservedPropertyAbstractPU) { - stateMgmtConsole.debug(`ObservedPropertyObject[${this.id__()}, '${this.info() || "unknown"}']: syncPeerHasChanged peer '${eventSource.info()}'.`); + stateMgmtConsole.debug(`ObservedPropertyObjectPU[${this.id__()}, '${this.info() || "unknown"}']: syncPeerHasChanged peer '${eventSource.info()}'.`); this.notifyPropertryHasChangedPU(); } @@ -60,7 +60,7 @@ class ObservedPropertyObjectPU extends ObservedPropertyObjectA * @param changedPropertyName */ public objectPropertyHasChangedPU(souceObject: ObservedObject, changedPropertyName : string) { - stateMgmtConsole.debug(`ObservedPropertyObject[${this.id__()}, '${this.info() || "unknown"}']: \ + stateMgmtConsole.debug(`ObservedPropertyObjectPU[${this.id__()}, '${this.info() || "unknown"}']: \ objectPropertyHasChangedPU: contained ObservedObject property '${changedPropertyName}' has changed.`) this.notifyPropertryHasChangedPU(); } @@ -82,27 +82,27 @@ class ObservedPropertyObjectPU extends ObservedPropertyObjectA */ private setValueInternal(newValue: T): boolean { if (typeof newValue !== 'object') { - stateMgmtConsole.error(`ObservedPropertyObject[${this.id__()}, '${this.info() || "unknown"}'] new value is NOT an object. Application error. Ignoring set.`); + stateMgmtConsole.error(`ObservedPropertyObjectPU[${this.id__()}, '${this.info() || "unknown"}'] new value is NOT an object. Application error. Ignoring set.`); return false; } if (newValue == this.wrappedValue_){ - stateMgmtConsole.debug(`ObservedPropertyObject[${this.id__()}, '${this.info() || "unknown"}'] newValue unchanged`); + stateMgmtConsole.debug(`ObservedPropertyObjectPU[${this.id__()}, '${this.info() || "unknown"}'] newValue unchanged`); return false; } this.unsubscribeWrappedObject(); if (ObservedObject.IsObservedObject(newValue)) { - stateMgmtConsole.debug(`ObservedPropertyObject[${this.id__()}, '${this.info() || "unknown"}'] new value is an ObservedObject already`); + stateMgmtConsole.debug(`ObservedPropertyObjectPU[${this.id__()}, '${this.info() || "unknown"}'] new value is an ObservedObject already`); ObservedObject.addOwningProperty(newValue, this); this.wrappedValue_ = newValue; } else if (newValue instanceof SubscribableAbstract) { - stateMgmtConsole.debug(`ObservedPropertyObject[${this.id__()}, '${this.info() || "unknown"}'] new value is an SubscribaleAbstract, subscribiung to it.`); + stateMgmtConsole.debug(`ObservedPropertyObjectPU[${this.id__()}, '${this.info() || "unknown"}'] new value is an SubscribaleAbstract, subscribiung to it.`); this.wrappedValue_ = newValue; (this.wrappedValue_ as unknown as SubscribableAbstract).addOwningProperty(this); } else { - stateMgmtConsole.debug(`ObservedPropertyObject[${this.id__()}, '${this.info() || "unknown"}'] new value is an Object, needs to be wrapped in an ObservedObject.`); + stateMgmtConsole.debug(`ObservedPropertyObjectPU[${this.id__()}, '${this.info() || "unknown"}'] new value is an Object, needs to be wrapped in an ObservedObject.`); this.wrappedValue_ = ObservedObject.createNew(newValue, this); } return true; @@ -115,8 +115,7 @@ class ObservedPropertyObjectPU extends ObservedPropertyObjectA } public getUnmonitored(): T { - stateMgmtConsole.debug(`ObservedPropertyObject[${this.id__()}, '${this.info() || "unknown"}']: getUnmonitored returns '${JSON.stringify(this.wrappedValue_)}' .`); - // unmonitored get access , no call to otifyPropertyRead ! + // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return this.wrappedValue_; } diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_simple.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_simple.ts index a32d369de48..7e899d6cf51 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_simple.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_observed_property_simple.ts @@ -70,7 +70,7 @@ class ObservedPropertySimplePU extends ObservedPropertySimpleAbstractPU { public getUnmonitored(): T { stateMgmtConsole.debug(`ObservedPropertySimple[${this.id__()}, '${this.info() || "unknown"}']: getUnmonitored returns '${JSON.stringify(this.wrappedValue_)}' .`); - // unmonitored get access , no call to otifyPropertyRead ! + // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return this.wrappedValue_; } diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_nested.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_nested.ts index 5389422b210..b6e2de65922 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_nested.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_nested.ts @@ -66,8 +66,7 @@ class SynchedPropertyNesedObjectPU } public getUnmonitored(): C { - // stateMgmtConsole.debug(`SynchedPropertyNesedObject[${this.id()}, '${this.info() || "unknown"}']: getUnmonitored returns '${JSON.stringify(this.wrappedValue_)}' .`); - // unmonitored get access , no call to otifyPropertyRead ! + // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return this.obsObject_; } diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_two_way.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_two_way.ts index f130214bf08..4010f62cdac 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_two_way.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_two_way.ts @@ -82,14 +82,12 @@ class SynchedPropertyObjectTwoWayPU public getUnmonitored(): C { - stateMgmtConsole.debug(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: getUnmonitored returns '${(this.source_ ? JSON.stringify(this.source_.getUnmonitored()) : "undefined")}' .`); - // unmonitored get access , no call to otifyPropertyRead ! + // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return (this.source_ ? this.source_.getUnmonitored() : undefined); } // get 'read through` from the ObservedProperty public get(): C { - stateMgmtConsole.debug(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: get`) this.notifyPropertryHasBeenReadPU() return this.getUnmonitored(); } diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_simple_one_way.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_simple_one_way.ts index 1c8e4f7ea93..8613ea268fe 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_simple_one_way.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_simple_one_way.ts @@ -78,7 +78,7 @@ class SynchedPropertySimpleOneWayPU extends ObservedPropertySimpleAbstractPU< public getUnmonitored(): T { stateMgmtConsole.debug(`SynchedPropertySimpleOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: getUnmonitored returns '${JSON.stringify(this.wrappedValue_)}' .`); - // unmonitored get access , no call to otifyPropertyRead ! + // unmonitored get access , no call to notifyPropertryHasBeenReadPU ! return this.wrappedValue_; } -- Gitee From bad162ea78babdfdd1e9d56d99c3bea2d2def583 Mon Sep 17 00:00:00 2001 From: Henna Myllys Date: Mon, 6 Mar 2023 18:14:05 +0100 Subject: [PATCH 06/11] Deep copy fix - catch circular reference Signed-off-by: Henna Myllys Change-Id: Iffbf4cca14c649d405fdd232dc48980c4786b62c --- .../declarative_frontend/engine/stateMgmt.js | 77 ++++++++++++------- .../src/lib/common/observed_object.ts | 63 ++++++++++----- .../pu_synced_property_object_one_way.ts | 21 ++--- 3 files changed, 105 insertions(+), 56 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index 1cc485db572..7a068dbd89f 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -1980,39 +1980,62 @@ class ObservedObject extends ExtendableProxy { * this rule applies for each individual object or array found in the recursive process * subscriber info will not be copied from the source object to its copy. * @param obj object, array of simple type data item to be deep copied + * @param variable Variable name of the object to be copied * @returns deep copied object, optionally wrapped inside an ObservedObject */ - static GetDeepCopyOfObject(obj) { - - if (obj === null || typeof obj !== 'object') { + static GetDeepCopyOfObject(obj, variable) { + if (!obj || typeof obj !== 'object') { return obj; } - let copy = Array.isArray(obj) ? [] : !obj.constructor ? {} : new obj.constructor(); - Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); - if (obj instanceof Set) { - for (let setKey of obj.keys()) { - copy.add(ObservedObject.GetDeepCopyOfObject(setKey)); + let stack = new Array(); + let copiedObjects = new Map(); + return GetDeepCopyOfObjectRecursive(obj); + function GetDeepCopyOfObjectRecursive(obj) { + if (!obj || typeof obj !== 'object') { + return obj; } - } - else if (obj instanceof Map) { - for (let mapKey of obj.keys()) { - copy.set(mapKey, ObservedObject.GetDeepCopyOfObject(obj.get(mapKey))); + const alreadyCopiedObject = copiedObjects.get(obj); + if (alreadyCopiedObject) { + let msg = `@Prop DeepCopyObject: Found reference to already copied object: Path ${variable ? variable : 'unknown variable'}`; + stack.forEach(stackItem => msg += ` - ${stackItem.name}`); + stateMgmtConsole.warn(msg); + return alreadyCopiedObject; } - } - else if (obj instanceof Object) { - for (let objKey of Object.keys(obj)) { - copy[objKey] = ObservedObject.GetDeepCopyOfObject(obj[objKey]); + let copy; + if (obj instanceof Set) { + copy = new Set(); + for (const setKey of obj.keys()) { + stack.push({ name: setKey }); + copiedObjects.set(obj, copy); + copy.add(GetDeepCopyOfObjectRecursive(setKey)); + stack.pop(); + } } - } - else if (obj instanceof Date) { - copy.setTime(obj.getTime()); - } - for (let key in obj) { - if (obj.hasOwnProperty(key)) { - copy[key] = ObservedObject.GetDeepCopyOfObject(obj[key]); + else if (obj instanceof Map) { + copy = new Map(); + for (const mapKey of obj.keys()) { + stack.push({ name: mapKey }); + copiedObjects.set(obj, copy); + copy.set(mapKey, GetDeepCopyOfObjectRecursive(obj.get(mapKey))); + stack.pop(); + } + } + else if (obj instanceof Date) { + copy = new Date(); + copy.setTime(obj.getTime()); + } + else if (obj instanceof Object) { + copy = Array.isArray(obj) ? [] : {}; + Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); + for (const objKey of Object.keys(obj)) { + stack.push({ name: objKey }); + copiedObjects.set(obj, copy); + Reflect.set(copy, objKey, GetDeepCopyOfObjectRecursive(obj[objKey])); + stack.pop(); + } } + return ObservedObject.IsObservedObject(obj) ? ObservedObject.createNew(copy, null) : copy; } - return ObservedObject.IsObservedObject(obj) ? ObservedObject.createNew(copy, null) : copy; } /** * Create a new ObservableObject and subscribe its owner to propertyHasChanged @@ -3493,7 +3516,7 @@ class SynchedPropertyObjectOneWayPU extends ObservedPropertyObjectAbstractPU { this.source_.set(sourceChangedValue); } else { - stateMgmtConsole.error(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: reset from '${JSON.stringify(this.localCopyObservedObject)}' to '${JSON.stringify(sourceChangedValue)}' No source_. Internal error!`); + stateMgmtConsole.error(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: reset --- No source_. Internal error!`); } } /* @@ -3510,7 +3533,7 @@ class SynchedPropertyObjectOneWayPU extends ObservedPropertyObjectAbstractPU { stateMgmtConsole.error(`SynchedPropertyOneWayObjectPU[${this.id__()}]: setLocalValue new value must be an Object. Not setting.`); return false; } - // unsubscribe from old wappedValue ObservedOject + // unsubscribe from old wrappedValue ObservedOject ObservedObject.removeOwningProperty(this.localCopyObservedObject, this); if (newObservedObjectValue == undefined) { // case: newObservedObjectValue undefined @@ -3520,7 +3543,7 @@ class SynchedPropertyObjectOneWayPU extends ObservedPropertyObjectAbstractPU { // deep copy value // needed whenever newObservedObjectValue comes from source // not needed on a local set (aka when called from set() method) - let copy = needDeepCopy ? ObservedObject.GetDeepCopyOfObject(newObservedObjectValue) : newObservedObjectValue; + let copy = needDeepCopy ? ObservedObject.GetDeepCopyOfObject(newObservedObjectValue, this.info_) : newObservedObjectValue; if (ObservedObject.IsObservedObject(copy)) { // case: new ObservedObject this.localCopyObservedObject = copy; diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts index 1f16301e6b9..1e66ad7aeeb 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts @@ -343,40 +343,65 @@ class ObservedObject extends ExtendableProxy { * this rule applies for each individual object or array found in the recursive process * subscriber info will not be copied from the source object to its copy. * @param obj object, array of simple type data item to be deep copied + * @param variable Variable name of the object to be copied * @returns deep copied object, optionally wrapped inside an ObservedObject */ - public static GetDeepCopyOfObject(obj: any): any { - stateMgmtConsole.debug(`GetDeepCopyOfObject obj ${JSON.stringify(obj)}`); - if (obj === null || typeof obj !== 'object') { + public static GetDeepCopyOfObject(obj: any, variable?: string): any { + if (!obj || typeof obj !== 'object') { + return obj; + } + + let stack = new Array<{ name: string}>(); + let copiedObjects = new Map(); + + return GetDeepCopyOfObjectRecursive(obj); + + function GetDeepCopyOfObjectRecursive(obj: any): any { + if (!obj || typeof obj !== 'object') { return obj; - } + } - let copy = Array.isArray(obj) ? [] : !obj.constructor ? {} : new obj.constructor(); - Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); + const alreadyCopiedObject = copiedObjects.get(obj); + if (alreadyCopiedObject) { + let msg = `@Prop DeepCopyObject: Found reference to already copied object: Path ${variable ? variable : 'unknown variable'}`; + stack.forEach(stackItem => msg += ` - ${stackItem.name}`) + stateMgmtConsole.warn(msg); + return alreadyCopiedObject; + } + let copy; if (obj instanceof Set) { - for (let setKey of obj.keys()) { - copy.add(ObservedObject.GetDeepCopyOfObject(setKey)); + copy = new Set(); + for (const setKey of obj.keys()) { + stack.push({ name: setKey }); + copiedObjects.set(obj, copy); + copy.add(GetDeepCopyOfObjectRecursive(setKey)); + stack.pop(); } } else if (obj instanceof Map) { - for (let mapKey of obj.keys()) { - copy.set(mapKey, ObservedObject.GetDeepCopyOfObject(obj.get(mapKey))); - } - } else if (obj instanceof Object) { - for (let objKey of Object.keys(obj)) { - copy[objKey] = ObservedObject.GetDeepCopyOfObject(obj[objKey]); + copy = new Map(); + for (const mapKey of obj.keys()) { + stack.push({ name: mapKey }); + copiedObjects.set(obj, copy); + copy.set(mapKey, GetDeepCopyOfObjectRecursive(obj.get(mapKey))); + stack.pop(); } } else if (obj instanceof Date) { + copy = new Date() copy.setTime(obj.getTime()); - } - - for (let key in obj) { - if (obj.hasOwnProperty(key)) { - copy[key] = ObservedObject.GetDeepCopyOfObject(obj[key]); + } else if (obj instanceof Object) { + copy = Array.isArray(obj) ? [] : {}; + Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); + for (const objKey of Object.keys(obj)) { + stack.push({ name: objKey }); + copiedObjects.set(obj, copy); + Reflect.set(copy, objKey, GetDeepCopyOfObjectRecursive(obj[objKey])); + stack.pop(); } } return ObservedObject.IsObservedObject(obj) ? ObservedObject.createNew(copy, null) : copy; } + } /** * Create a new ObservableObject and subscribe its owner to propertyHasChanged diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_one_way.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_one_way.ts index 21b9f45b94c..6da66888f02 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_one_way.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_synced_property_object_one_way.ts @@ -93,7 +93,7 @@ class SynchedPropertyObjectOneWayPU } this.resetLocalValue(this.source_.get(), /* needDeepCopy */ true); - stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: constructor ready with localCopyObservedObject '${JSON.stringify(this.localCopyObservedObject)}'.`); + stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: constructor ready with local copy.`); } /* @@ -148,15 +148,16 @@ class SynchedPropertyObjectOneWayPU } public getUnmonitored(): C { - stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: getUnmonitored returns '${JSON.stringify(this.localCopyObservedObject)}'.`); + stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: getUnmonitored.`); // unmonitored get access , no call to notifyPropertyRead ! return this.localCopyObservedObject; } // get 'read through` from the ObservedObject public get(): C { - stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: get returning ${JSON.stringify(this.localCopyObservedObject)}.`) - this.notifyPropertryHasBeenReadPU() + stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: get.`); + + this.notifyPropertryHasBeenReadPU(); return this.localCopyObservedObject; } @@ -164,11 +165,11 @@ class SynchedPropertyObjectOneWayPU // set 'writes through` to the ObservedObject public set(newValue: C): void { if (this.localCopyObservedObject == newValue) { - stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}IP, '${this.info() || "unknown"}']: set with unchanged value '${JSON.stringify(newValue)}'- ignoring.`); + stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}IP, '${this.info() || "unknown"}']: set with unchanged value - ignoring.`); return; } - stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: set to newValue: '${JSON.stringify(newValue)}'.`); + stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: set to newValue.`); if (this.resetLocalValue(newValue, /* needDeepCopy */ false)) { this.notifyPropertryHasChangedPU(); @@ -177,12 +178,12 @@ class SynchedPropertyObjectOneWayPU public reset(sourceChangedValue: C): void { - stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: reset from '${JSON.stringify(this.localCopyObservedObject)}' to '${JSON.stringify(sourceChangedValue)}'.`); + stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: reset.`); if (this.source_ !== undefined) { // if set causes an actual change, then, ObservedPropertyObject source_ will call syncPeerHasChanged this.source_.set(sourceChangedValue); } else { - stateMgmtConsole.error(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: reset from '${JSON.stringify(this.localCopyObservedObject)}' to '${JSON.stringify(sourceChangedValue)}' No source_. Internal error!`); + stateMgmtConsole.error(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: reset --- No source_. Internal error!`); } } @@ -202,7 +203,7 @@ class SynchedPropertyObjectOneWayPU return false; } - // unsubscribe from old wappedValue ObservedOject + // unsubscribe from old wrappedValue ObservedOject ObservedObject.removeOwningProperty(this.localCopyObservedObject, this); @@ -215,7 +216,7 @@ class SynchedPropertyObjectOneWayPU // deep copy value // needed whenever newObservedObjectValue comes from source // not needed on a local set (aka when called from set() method) - let copy = needDeepCopy ? ObservedObject.GetDeepCopyOfObject(newObservedObjectValue) : newObservedObjectValue; + let copy = needDeepCopy ? ObservedObject.GetDeepCopyOfObject(newObservedObjectValue, this.info_) : newObservedObjectValue; if (ObservedObject.IsObservedObject(copy)) { // case: new ObservedObject -- Gitee From 1a3af1cee6b8de6718550e56ae3b26ea0dc12003 Mon Sep 17 00:00:00 2001 From: Henna Myllys Date: Mon, 13 Mar 2023 11:28:32 +0100 Subject: [PATCH 07/11] Created new SubscribableArrayHandler to properly handle functions that modify arrays in-place Signed-off-by: Henna Myllys Change-Id: Ibdcf44c887781e81a523a7883c2d79fe21295e45 --- .../declarative_frontend/engine/stateMgmt.js | 35 +---------------- .../src/lib/common/observed_object.ts | 38 +------------------ 2 files changed, 4 insertions(+), 69 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index 7a068dbd89f..de80edf9a8d 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -1856,45 +1856,15 @@ class SubscribableArrayHandler extends SubscribableHandler { if (this.arrFunctions.includes(property.toString()) && typeof ret === "function" && target["length"] > 0) { const self = this; - const prop = property.toString(); return function () { // execute original function with given arguments ret.apply(this, arguments); - self.notifyObjectPropertyHasChanged(prop, this); + self.notifyObjectPropertyHasChanged(property.toString(), this[0]); }.bind(target); // bind "this" to target inside the function } return ret; } } -class SubscribableDateHandler extends SubscribableHandler { - constructor(owningProperty) { - super(owningProperty); - } - /** - * Get trap for Date type proxy - * Functions that modify Date in-place are intercepted and replaced with a function - * that executes the original function and notifies the handler of a change. - * @param target Original Date object - * @param property - * @returns - */ - get(target, property) { - let ret = super.get(target, property); - if (typeof ret === "function" && property.toString() && - property.toString().startsWith('set')) { - const self = this; - return function () { - // execute original function with given arguments - ret.apply(this, arguments); - self.notifyObjectPropertyHasChanged(property.toString(), this); - }.bind(target); // bind "this" to target inside the function - } - else if (typeof ret === "function") { - ret = ret.bind(target); - } - return ret; - } -} class ExtendableProxy { constructor(obj, handler) { return new Proxy(obj, handler); @@ -2048,8 +2018,7 @@ class ObservedObject extends ExtendableProxy { throw new Error("Invalid constructor argument error: ObservableObject contructor called with an ObservedObject as parameer"); } let handler = Array.isArray(obj) ? new SubscribableArrayHandler(objectOwningProperty) - : (obj instanceof Date) ? new SubscribableDateHandler(objectOwningProperty) - : new SubscribableHandler(objectOwningProperty); + : new SubscribableHandler(objectOwningProperty); super(obj, handler); if (ObservedObject.IsObservedObject(obj)) { stateMgmtConsole.error("ObservableOject constructor: INTERNAL ERROR: after jsObj is observedObject already"); diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts index 1e66ad7aeeb..bd72abf2883 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts @@ -201,11 +201,10 @@ class SubscribableArrayHandler extends SubscribableHandler { if (this.arrFunctions.includes(property.toString()) && typeof ret === "function" && target["length"] > 0) { const self = this; - const prop = property.toString(); return function() { // execute original function with given arguments ret.apply(this, arguments); - self.notifyObjectPropertyHasChanged(prop, this); + self.notifyObjectPropertyHasChanged(property.toString(), this[0]); }.bind(target) // bind "this" to target inside the function } @@ -214,38 +213,6 @@ class SubscribableArrayHandler extends SubscribableHandler { } -class SubscribableDateHandler extends SubscribableHandler { - - constructor(owningProperty: IPropertySubscriber) { - super(owningProperty); - } - - /** - * Get trap for Date type proxy - * Functions that modify Date in-place are intercepted and replaced with a function - * that executes the original function and notifies the handler of a change. - * @param target Original Date object - * @param property - * @returns - */ - public get(target: Object, property: PropertyKey): any { - let ret = super.get(target, property); - if (typeof ret === "function" && property.toString() && - property.toString().startsWith('set')) { - const self = this; - return function() { - // execute original function with given arguments - ret.apply(this, arguments); - self.notifyObjectPropertyHasChanged(property.toString(), this); - }.bind(target) // bind "this" to target inside the function - } else if (typeof ret === "function") { - ret = ret.bind(target); - } - return ret; - } -} - - class ExtendableProxy { constructor(obj: Object, handler: SubscribableHandler) { return new Proxy(obj, handler); @@ -414,8 +381,7 @@ class ObservedObject extends ExtendableProxy { throw new Error("Invalid constructor argument error: ObservableObject contructor called with an ObservedObject as parameer"); } let handler = Array.isArray(obj) ? new SubscribableArrayHandler(objectOwningProperty) - : (obj instanceof Date) ? new SubscribableDateHandler(objectOwningProperty) - : new SubscribableHandler(objectOwningProperty); + : new SubscribableHandler(objectOwningProperty); super(obj, handler); if (ObservedObject.IsObservedObject(obj)) { -- Gitee From 959d4068907577281aa9cd8be9a1c25cfdd6318d Mon Sep 17 00:00:00 2001 From: Henna Myllys Date: Wed, 15 Mar 2023 10:32:36 +0100 Subject: [PATCH 08/11] Added missing return value to EnvProps Signed-off-by: Henna Myllys Change-Id: Ic4370bb3229ae6a46870adf77789714cb49d5857 --- frameworks/bridge/declarative_frontend/engine/stateMgmt.js | 1 + .../declarative_frontend/state_mgmt/src/lib/sdk/environment.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index de80edf9a8d..665f697cfe5 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -1448,6 +1448,7 @@ class Environment { prop = AppStorage.SetAndProp(key, tmp); this.props_.set(key, prop); + return true; } envProps(properties) { properties.forEach(property => { diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/environment.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/environment.ts index 84e56b0e39d..fb15d77fc6f 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/environment.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/sdk/environment.ts @@ -102,6 +102,7 @@ class Environment { prop = AppStorage.SetAndProp(key, tmp); this.props_.set(key, prop); stateMgmtConsole.debug(`Environment: envProp for '${key}' done.`); + return true; } private envProps(properties: { -- Gitee From 71de8d605a91ab4230e28dc75d6d863a53ad139d Mon Sep 17 00:00:00 2001 From: Henna Myllys Date: Thu, 16 Mar 2023 08:37:59 +0100 Subject: [PATCH 09/11] Created a new SubscribableDateHandler to handle functions that modify Date in-place Signed-off-by: Henna Myllys Change-Id: I351b080da6572d3493af3c97105a89cc32f5fda5 --- .../declarative_frontend/engine/stateMgmt.js | 32 ++++++++++++++++- .../src/lib/common/observed_object.ts | 35 ++++++++++++++++++- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index 665f697cfe5..8fb249ee135 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -1866,6 +1866,35 @@ class SubscribableArrayHandler extends SubscribableHandler { return ret; } } +class SubscribableDateHandler extends SubscribableHandler { + constructor(owningProperty) { + super(owningProperty); + } + /** + * Get trap for Date type proxy + * Functions that modify Date in-place are intercepted and replaced with a function + * that executes the original function and notifies the handler of a change. + * @param target Original Date object + * @param property + * @returns + */ + get(target, property) { + let ret = super.get(target, property); + if (typeof ret === "function" && property.toString() && + property.toString().startsWith('set')) { + const self = this; + return function () { + // execute original function with given arguments + ret.apply(this, arguments); + self.notifyObjectPropertyHasChanged(property.toString(), this); + }.bind(target); // bind "this" to target inside the function + } + else if (typeof ret === "function") { + ret = ret.bind(target); + } + return ret; + } +} class ExtendableProxy { constructor(obj, handler) { return new Proxy(obj, handler); @@ -2019,7 +2048,8 @@ class ObservedObject extends ExtendableProxy { throw new Error("Invalid constructor argument error: ObservableObject contructor called with an ObservedObject as parameer"); } let handler = Array.isArray(obj) ? new SubscribableArrayHandler(objectOwningProperty) - : new SubscribableHandler(objectOwningProperty); + : (obj instanceof Date) ? new SubscribableDateHandler(objectOwningProperty) + : new SubscribableHandler(objectOwningProperty); super(obj, handler); if (ObservedObject.IsObservedObject(obj)) { stateMgmtConsole.error("ObservableOject constructor: INTERNAL ERROR: after jsObj is observedObject already"); diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts index bd72abf2883..eebebc337b7 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts @@ -213,6 +213,38 @@ class SubscribableArrayHandler extends SubscribableHandler { } +class SubscribableDateHandler extends SubscribableHandler { + + constructor(owningProperty: IPropertySubscriber) { + super(owningProperty); + } + + /** + * Get trap for Date type proxy + * Functions that modify Date in-place are intercepted and replaced with a function + * that executes the original function and notifies the handler of a change. + * @param target Original Date object + * @param property + * @returns + */ + public get(target: Object, property: PropertyKey): any { + let ret = super.get(target, property); + if (typeof ret === "function" && property.toString() && + property.toString().startsWith('set')) { + const self = this; + return function() { + // execute original function with given arguments + ret.apply(this, arguments); + self.notifyObjectPropertyHasChanged(property.toString(), this); + }.bind(target) // bind "this" to target inside the function + } else if (typeof ret === "function") { + ret = ret.bind(target); + } + return ret; + } +} + + class ExtendableProxy { constructor(obj: Object, handler: SubscribableHandler) { return new Proxy(obj, handler); @@ -381,7 +413,8 @@ class ObservedObject extends ExtendableProxy { throw new Error("Invalid constructor argument error: ObservableObject contructor called with an ObservedObject as parameer"); } let handler = Array.isArray(obj) ? new SubscribableArrayHandler(objectOwningProperty) - : new SubscribableHandler(objectOwningProperty); + : (obj instanceof Date) ? new SubscribableDateHandler(objectOwningProperty) + : new SubscribableHandler(objectOwningProperty); super(obj, handler); if (ObservedObject.IsObservedObject(obj)) { -- Gitee From 68b691339baa965a07a91e42391966809f16a6e5 Mon Sep 17 00:00:00 2001 From: Henna Myllys Date: Wed, 29 Mar 2023 09:31:44 +0200 Subject: [PATCH 10/11] Try to make Array handling even more safe Signed-off-by: Henna Myllys Change-Id: If69c3b2e512ebfa224564da5aa9a38615fde48f8 --- .../bridge/declarative_frontend/engine/stateMgmt.js | 11 +++++++++-- .../state_mgmt/src/lib/common/observed_object.ts | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index 8fb249ee135..d39fd919c9a 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -1854,13 +1854,20 @@ class SubscribableArrayHandler extends SubscribableHandler { */ get(target, property) { let ret = super.get(target, property); - if (this.arrFunctions.includes(property.toString()) && + const prop = property.toString(); + if (this.arrFunctions.includes(prop) && typeof ret === "function" && target["length"] > 0) { const self = this; return function () { + if (!this || !ret) { + // Something has gone really wrong! + return; + } // execute original function with given arguments ret.apply(this, arguments); - self.notifyObjectPropertyHasChanged(property.toString(), this[0]); + if (self) { + self.notifyObjectPropertyHasChanged(prop, this); + } }.bind(target); // bind "this" to target inside the function } return ret; diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts index eebebc337b7..a0095bb44a9 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts @@ -198,13 +198,20 @@ class SubscribableArrayHandler extends SubscribableHandler { */ public get(target: Object, property: PropertyKey): any { let ret = super.get(target, property); - if (this.arrFunctions.includes(property.toString()) && + const prop = property.toString(); + if (this.arrFunctions.includes(prop) && typeof ret === "function" && target["length"] > 0) { const self = this; return function() { + if (!this || !ret) { + // Something has gone really wrong! + return; + } // execute original function with given arguments ret.apply(this, arguments); - self.notifyObjectPropertyHasChanged(property.toString(), this[0]); + if (self) { + self.notifyObjectPropertyHasChanged(prop, this); + } }.bind(target) // bind "this" to target inside the function } -- Gitee From df169a8295cb092ae1d1d26aabfc65b326e88f65 Mon Sep 17 00:00:00 2001 From: tomppo Date: Wed, 29 Mar 2023 18:15:20 +0300 Subject: [PATCH 11/11] Fix subscribable array. Sort() returned undefined in some cases. Change-Id: I68a9888bf272ee5dadc7b3cfbfef2272ba9b582d Signed-off-by: tomppo --- .../declarative_frontend/engine/stateMgmt.js | 7 ++-- .../src/lib/common/observed_object.ts | 36 +++++++++---------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js index d39fd919c9a..8e690a7ddf6 100644 --- a/frameworks/bridge/declarative_frontend/engine/stateMgmt.js +++ b/frameworks/bridge/declarative_frontend/engine/stateMgmt.js @@ -1855,8 +1855,8 @@ class SubscribableArrayHandler extends SubscribableHandler { get(target, property) { let ret = super.get(target, property); const prop = property.toString(); - if (this.arrFunctions.includes(prop) && - typeof ret === "function" && target["length"] > 0) { + if (this.arrFunctions.includes(prop) && typeof ret === "function" && + Reflect.get(target, "length") > 0) { const self = this; return function () { if (!this || !ret) { @@ -1864,10 +1864,11 @@ class SubscribableArrayHandler extends SubscribableHandler { return; } // execute original function with given arguments - ret.apply(this, arguments); + const val = ret.apply(this, arguments); if (self) { self.notifyObjectPropertyHasChanged(prop, this); } + return val; }.bind(target); // bind "this" to target inside the function } return ret; diff --git a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts index a0095bb44a9..90f58b6d4af 100644 --- a/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts +++ b/frameworks/bridge/declarative_frontend/state_mgmt/src/lib/common/observed_object.ts @@ -196,36 +196,37 @@ class SubscribableArrayHandler extends SubscribableHandler { * @param property * @returns */ + public get(target: Object, property: PropertyKey): any { let ret = super.get(target, property); const prop = property.toString(); - if (this.arrFunctions.includes(prop) && - typeof ret === "function" && target["length"] > 0) { - const self = this; - return function() { - if (!this || !ret) { - // Something has gone really wrong! - return; - } - // execute original function with given arguments - ret.apply(this, arguments); - if (self) { - self.notifyObjectPropertyHasChanged(prop, this); - } + if (this.arrFunctions.includes(prop) && typeof ret === "function" && + Reflect.get(target, "length") > 0) { + const self = this; + return function () { + if (!this || !ret) { + // Something has gone really wrong! + return; + } + // execute original function with given arguments + const val = ret.apply(this, arguments); + if (self) { + self.notifyObjectPropertyHasChanged(prop, this); + } + return val; }.bind(target) // bind "this" to target inside the function } - + return ret; } } - class SubscribableDateHandler extends SubscribableHandler { constructor(owningProperty: IPropertySubscriber) { super(owningProperty); } - + /** * Get trap for Date type proxy * Functions that modify Date in-place are intercepted and replaced with a function @@ -251,7 +252,6 @@ class SubscribableDateHandler extends SubscribableHandler { } } - class ExtendableProxy { constructor(obj: Object, handler: SubscribableHandler) { return new Proxy(obj, handler); @@ -356,7 +356,7 @@ class ObservedObject extends ExtendableProxy { if (!obj || typeof obj !== 'object') { return obj; } - + let stack = new Array<{ name: string}>(); let copiedObjects = new Map(); -- Gitee