1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15/** 16 * ConfigureStateMgmt keeps track if V2 @Observed and @Track are used. 17 * If yes, it enables object deep observation mechanisms need with ObservedV3. 18 */ 19class ConfigureStateMgmt { 20 constructor() { 21 this.v2ObservedTrackInUse_ = false; 22 this.puObservedTrackInUse_ = false; 23 } 24 static get instance() { 25 return ConfigureStateMgmt.instance__ 26 ? ConfigureStateMgmt.instance__ 27 : (ConfigureStateMgmt.instance__ = new ConfigureStateMgmt()); 28 } 29 /** 30 * framework code call this function when it sees use of a stateMgmt V2 @Observed @Track 31 * 32 * @param feature specify feature separately from context of use, so that in future decision can be made 33 * for individual features, not use permit either use of V2 or V3. 34 * @param contextOfUse purely for error messages. Give enough info that use is able to local the feature use in source code. 35 * @returns true if no mix of features detected, false if mix is detected 36 */ 37 usingV2ObservedTrack(feature, contextOfUse = '') { 38 this.v2ObservedTrackInUse_ = true; 39 40 } 41 /** 42 * framework code call this function when it sees use of a stateMgmt PU Observed / @Track 43 * 44 * @param feature specify feature separately from context of use, so that in future decision can be made 45 * for individual features, not use permit either use of V2 or V3. 46 * @param contextOfUse purely for error messages. Give enough info that use is able to local the feature use in source code. 47 * @returns true if no mix of features detected, false if mix is detected 48 */ 49 usingPUObservedTrack(feature, contextOfUse = '') { 50 this.puObservedTrackInUse_ = true; 51 52 } 53 /** 54 * Return true if object deep observation mechanisms need to be enabled 55 * that is when seen V3 @observe, @track, or @monitor decorator used in at least one class 56 * (we could but we do not check for class object instance creation for performance reasons) 57 * @returns 58 */ 59 needsV2Observe() { 60 return this.v2ObservedTrackInUse_; 61 } 62} // ConfigureStateMgmt 63ConfigureStateMgmt.HOW_TO_SAY = `Your application uses both state management V2 and V3 features! - It is strongly recommended not to mix V2 and V3. Consult the rules how state management V2 and V3 can be mixed in the same app.`; 64/* 65 * Copyright (c) 2023 Huawei Device Co., Ltd. 66 * Licensed under the Apache License, Version 2.0 (the "License"); 67 * you may not use this file except in compliance with the License. 68 * You may obtain a copy of the License at 69 * 70 * http://www.apache.org/licenses/LICENSE-2.0 71 * 72 * Unless required by applicable law or agreed to in writing, software 73 * distributed under the License is distributed on an "AS IS" BASIS, 74 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 75 * See the License for the specific language governing permissions and 76 * limitations under the License. 77 */ 78class stateMgmtProfiler { 79 static begin(blockName) { 80 var _a; 81 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.begin(blockName); 82 } 83 static end() { 84 var _a; 85 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.end(); 86 } 87 static report() { 88 var _a; 89 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.report(); 90 } 91 static clear() { 92 var _a; 93 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.clear(); 94 } 95 static init(instance) { 96 stateMgmtProfiler.instance = instance; 97 } 98} 99stateMgmtProfiler.instance = undefined; 100/* 101 * Copyright (c) 2021 Huawei Device Co., Ltd. 102 * Licensed under the Apache License, Version 2.0 (the "License"); 103 * you may not use this file except in compliance with the License. 104 * You may obtain a copy of the License at 105 * 106 * http://www.apache.org/licenses/LICENSE-2.0 107 * 108 * Unless required by applicable law or agreed to in writing, software 109 * distributed under the License is distributed on an "AS IS" BASIS, 110 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 111 * See the License for the specific language governing permissions and 112 * limitations under the License. 113 */ 114/* 115 * Copyright (c) 2021 Huawei Device Co., Ltd. 116 * Licensed under the Apache License, Version 2.0 (the "License"); 117 * you may not use this file except in compliance with the License. 118 * You may obtain a copy of the License at 119 * 120 * http://www.apache.org/licenses/LICENSE-2.0 121 * 122 * Unless required by applicable law or agreed to in writing, software 123 * distributed under the License is distributed on an "AS IS" BASIS, 124 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 125 * See the License for the specific language governing permissions and 126 * limitations under the License. 127 */ 128/* 129 * Copyright (c) 2023 Huawei Device Co., Ltd. 130 * Licensed under the Apache License, Version 2.0 (the "License"); 131 * you may not use this file except in compliance with the License. 132 * You may obtain a copy of the License at 133 * 134 * http://www.apache.org/licenses/LICENSE-2.0 135 * 136 * Unless required by applicable law or agreed to in writing, software 137 * distributed under the License is distributed on an "AS IS" BASIS, 138 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139 * See the License for the specific language governing permissions and 140 * limitations under the License. 141 */ 142/* 143 * Copyright (c) 2021 Huawei Device Co., Ltd. 144 * Licensed under the Apache License, Version 2.0 (the "License"); 145 * you may not use this file except in compliance with the License. 146 * You may obtain a copy of the License at 147 * 148 * http://www.apache.org/licenses/LICENSE-2.0 149 * 150 * Unless required by applicable law or agreed to in writing, software 151 * distributed under the License is distributed on an "AS IS" BASIS, 152 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 153 * See the License for the specific language governing permissions and 154 * limitations under the License. 155 */ 156/* 157 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 158 * Licensed under the Apache License, Version 2.0 (the "License"); 159 * you may not use this file except in compliance with the License. 160 * You may obtain a copy of the License at 161 * 162 * http://www.apache.org/licenses/LICENSE-2.0 163 * 164 * Unless required by applicable law or agreed to in writing, software 165 * distributed under the License is distributed on an "AS IS" BASIS, 166 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 167 * See the License for the specific language governing permissions and 168 * limitations under the License. 169 */ 170/** 171 * 172 * LocalStorage 173 * 174 * Class implements a Map of ObservableObjectBase UI state variables. 175 * Instances can be created to manage UI state within a limited "local" 176 * access, and life cycle as defined by the app. 177 * AppStorage singleton is sub-class of LocalStorage for 178 * UI state of app-wide access and same life cycle as the app. 179 * 180 * @since 9 181 */ 182class LocalStorage extends NativeLocalStorage { 183 /** 184 * Construct new instance of LocalStorage 185 * initialzie with all properties and their values that Object.keys(params) returns 186 * Property values must not be undefined for API 9 and lower, undefined allowed for API10 187 * @param initializingProperties Object containing keys and values. @see set() for valid values 188 * 189 * @since 9 190 */ 191 constructor(initializingProperties = {}) { 192 // This is edited for the statibility issue that "construtor is false", which meaning that the super() is not callable 193 // It is just the debug log using ArkTools print. 194 try { 195 super(); 196 } 197 catch (error) { 198 stateMgmtConsole.error(`An error occurred in the constructor of LocalStorage ${error.message}`); 199 ArkTools.print("NativeLocalStorage", NativeLocalStorage); 200 throw error; 201 } 202 203 this.storage_ = new Map(); 204 if (Object.keys(initializingProperties).length) { 205 this.initializeProps(initializingProperties); 206 } 207 } 208 /* 209 get access to provded LocalStorage instance thru Stake model 210 @StageModelOnly 211 @form 212 @since 10 213 */ 214 static getShared() { 215 return LocalStorage.GetShared(); 216 } 217 /** 218 * clear storage and init with given properties 219 * @param initializingProperties 220 * 221 * not a public / sdk function 222 */ 223 initializeProps(initializingProperties = {}) { 224 225 this.storage_.clear(); 226 Object.keys(initializingProperties) 227 .filter((propName) => (initializingProperties[propName] != null || Utils.isApiVersionEQAbove(12))) 228 .forEach((propName) => this.addNewPropertyInternal(propName, initializingProperties[propName])); 229 } 230 /** 231 * Use before deleting owning Ability, window, or service UI 232 * (letting it go out of scope). 233 * 234 * This method orderly closes down a LocalStorage instance by calling @see clear(). 235 * This requires that no property is left with one or more subscribers. 236 * @see clear() and @see delete() 237 * @returns true if all properties could be removed from storage 238 */ 239 aboutToBeDeleted() { 240 return this.clear(); 241 } 242 /** 243 * Check if LocalStorage has a property with given name 244 * return true if prooperty with given name exists 245 * same as ES6 Map.prototype.has() 246 * @param propName searched property 247 * @returns true if property with such name exists in LocalStorage 248 * 249 * @since 9 250 */ 251 has(propName) { 252 return this.storage_.has(propName); 253 } 254 /** 255 * Provide names of all properties in LocalStorage 256 * same as ES6 Map.prototype.keys() 257 * @returns return a Map Iterator 258 * 259 * @since 9 260 */ 261 keys() { 262 return this.storage_.keys(); 263 } 264 /** 265 * Returns number of properties in LocalStorage 266 * same as Map.prototype.size() 267 * @param propName 268 * @returns return number of properties 269 * 270 * @since 9 271 */ 272 size() { 273 return this.storage_.size; 274 } 275 /** 276 * Returns value of given property 277 * return undefined if no property with this name 278 * @param propName 279 * @returns property value if found or undefined 280 * 281 * @since 9 282 */ 283 get(propName) { 284 let p = this.storage_.get(propName); 285 return (p) ? p.get() : undefined; 286 } 287 /** 288 * Set value of given property in LocalStorage 289 * Methosd sets nothing and returns false if property with this name does not exist 290 * or if newValue is `undefined` or `null` (`undefined`, `null` value are not allowed for state variables). 291 * @param propName 292 * @param newValue must be of type T and must not be undefined or null 293 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 294 * 295 * @since 9 296 */ 297 set(propName, newValue) { 298 299 if (newValue === undefined && !Utils.isApiVersionEQAbove(12)) { 300 301 302 return false; 303 } 304 var p = this.storage_.get(propName); 305 if (p === undefined) { 306 307 308 return false; 309 } 310 p.set(newValue); 311 312 return true; 313 } 314 /** 315 * Set value of given property, if it exists, @see set() . 316 * Add property if no property with given name and initialize with given value. 317 * Do nothing and return false if newValuue is undefined or null 318 * (undefined, null value is not allowed for state variables) 319 * @param propName 320 * @param newValue must be of type T and must not be undefined or null 321 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 322 * 323 * @since 9 324 */ 325 setOrCreate(propName, newValue) { 326 327 if (newValue == undefined && !Utils.isApiVersionEQAbove(12)) { 328 329 330 return false; 331 } 332 let p = this.storage_.get(propName); 333 if (p) { 334 335 p.set(newValue); 336 } 337 else { 338 339 this.addNewPropertyInternal(propName, newValue); 340 } 341 342 return true; 343 } 344 /** 345 * Obtain a handle or an alias to LocalStorage property with given name. 346 * 347 * @param propName LocalStorage property name 348 * @returns AbstractProperty object is property with given name exists 349 * undefined otherwise 350 */ 351 ref(propName) { 352 return this.storage_.get(propName); 353 } 354 /** 355 * Obtain a handle or an alias to LocalStorage property with given name. 356 * 357 * If property does not exist in LocalStorage, create it with given default value. 358 * 359 * @param propName LocalStorage property name 360 * @param defaultValue If property does not exist in LocalStorage, 361 * create it with given default value. 362 * @returns AbstractProperty object 363 */ 364 setAndRef(propName, defaultValue) { 365 if (!this.has(propName)) { 366 this.addNewPropertyInternal(propName, defaultValue); 367 } 368 return this.storage_.get(propName); 369 } 370 /** 371 * Internal use helper function to create and initialize a new property. 372 * caller needs to be all the checking beforehand 373 * @param propName 374 * @param value 375 * 376 * Not a public / sdk method. 377 */ 378 addNewPropertyInternal(propName, value) { 379 let newProp; 380 if (ViewStackProcessor.UsesNewPipeline()) { 381 newProp = new ObservedPropertyPU(value, undefined, propName); 382 } 383 else { 384 newProp = (typeof value === 'object') ? 385 new ObservedPropertyObject(value, undefined, propName) 386 : new ObservedPropertySimple(value, undefined, propName); 387 } 388 this.storage_.set(propName, newProp); 389 return newProp; 390 } 391 /** 392 * create and return a two-way sync "(link") to named property 393 * @param propName name of source property in LocalStorage 394 * @param linkUser IPropertySubscriber to be notified when source changes, 395 * @param subscribersName optional, the linkUser (subscriber) uses this name for the property 396 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 397 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 398 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 399 * return undefiend if named property does not already exist in LocalStorage 400 * Apps can use SDK functions of base class SubscribedPropertyAbstract<S> 401 * return undefiend if named property does not already exist in LocalStorage 402 * 403 * @since 9 404 */ 405 link(propName, linkUser, subscribersName) { 406 407 var p = this.storage_.get(propName); 408 if (p == undefined) { 409 410 411 return undefined; 412 } 413 let linkResult; 414 if (ViewStackProcessor.UsesNewPipeline()) { 415 linkResult = new SynchedPropertyTwoWayPU(p, linkUser, propName); 416 } 417 else { 418 linkResult = p.createLink(linkUser, propName); 419 } 420 linkResult.setInfo(subscribersName); 421 422 return linkResult; 423 } 424 /** 425 * Like @see link(), but will create and initialize a new source property in LocalStorge if missing 426 * @param propName name of source property in LocalStorage 427 * @param defaultValue value to be used for initializing if new creating new property in LocalStorage 428 * default value must be of type S, must not be undefined or null. 429 * @param linkUser IPropertySubscriber to be notified when return 'link' changes, 430 * @param subscribersName the linkUser (subscriber) uses this name for the property 431 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 432 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 433 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 434 * 435 * @since 9 436 */ 437 setAndLink(propName, defaultValue, linkUser, subscribersName) { 438 439 var p = this.storage_.get(propName); 440 if (!p) { 441 this.setOrCreate(propName, defaultValue); 442 } 443 const link = this.link(propName, linkUser, subscribersName); 444 445 return link; 446 } 447 /** 448 * create and return a one-way sync ('prop') to named property 449 * @param propName name of source property in LocalStorage 450 * @param propUser IPropertySubscriber to be notified when source changes, 451 * @param subscribersName the linkUser (subscriber) uses this name for the property 452 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 453 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 454 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 455 * return undefiend if named property does not already exist in LocalStorage. 456 * Apps can use SDK functions of base class SubscribedPropertyAbstract<S> 457 * return undefiend if named property does not already exist in LocalStorage. 458 * @since 9 459 */ 460 prop(propName, propUser, subscribersName) { 461 462 var p = this.storage_.get(propName); 463 if (p == undefined) { 464 465 466 return undefined; 467 } 468 let propResult; 469 if (ViewStackProcessor.UsesNewPipeline()) { 470 propResult = new SynchedPropertyOneWayPU(p, propUser, propName); 471 } 472 else { 473 propResult = p.createProp(propUser, propName); 474 } 475 propResult.setInfo(subscribersName); 476 477 return propResult; 478 } 479 /** 480 * Like @see prop(), will create and initialize a new source property in LocalStorage if missing 481 * @param propName name of source property in LocalStorage 482 * @param defaultValue value to be used for initializing if new creating new property in LocalStorage. 483 * default value must be of type S, must not be undefined or null. 484 * @param propUser IPropertySubscriber to be notified when returned 'prop' changes, 485 * @param subscribersName the propUser (subscriber) uses this name for the property 486 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 487 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 488 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 489 * @since 9 490 */ 491 setAndProp(propName, defaultValue, propUser, subscribersName) { 492 493 let p = this.storage_.get(propName); 494 if (!p) { 495 this.setOrCreate(propName, defaultValue); 496 } 497 const prop = this.prop(propName, propUser, subscribersName); 498 499 return prop; 500 } 501 /** 502 * Delete property from StorageBase 503 * Use with caution: 504 * Before deleting a prop from LocalStorage all its subscribers need to 505 * unsubscribe from the property. 506 * This method fails and returns false if given property still has subscribers 507 * Another reason for failing is unkmown property. 508 * 509 * Developer advise: 510 * Subscribers are created with @see link(), @see prop() 511 * and also via @LocalStorageLink and @LocalStorageProp state variable decorators. 512 * That means as long as their is a @Component instance that uses such decorated variable 513 * or a sync relationship with a SubscribedAbstractProperty variable the property can nit 514 * (and also should not!) be deleted from LocalStorage. 515 * 516 * @param propName 517 * @returns false if method failed 518 * 519 * @since 9 520 */ 521 delete(propName) { 522 523 let p = this.storage_.get(propName); 524 if (p) { 525 if (p.numberOfSubscrbers()) { 526 stateMgmtConsole.error(`${this.constructor.name}: Attempt to delete property ${propName} that has \ 527 ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion.`); 528 529 return false; 530 } 531 p.aboutToBeDeleted(); 532 this.storage_.delete(propName); 533 534 return true; 535 } 536 else { 537 538 539 return false; 540 } 541 } 542 /** 543 * delete all properties from the LocalStorage instance 544 * @see delete(). 545 * precondition is that there are no subscribers. 546 * method returns false and deletes no poperties if there is any property 547 * that still has subscribers 548 * 549 * @since 9 550 */ 551 clear() { 552 553 for (let propName of this.keys()) { 554 var p = this.storage_.get(propName); 555 if (p.numberOfSubscrbers()) { 556 stateMgmtConsole.error(`${this.constructor.name}.deleteAll: Attempt to delete property ${propName} that \ 557 has ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion. 558 Any @Component instance with a @StorageLink/Prop or @LocalStorageLink/Prop is a subscriber.`); 559 560 return false; 561 } 562 } 563 for (let propName of this.keys()) { 564 var p = this.storage_.get(propName); 565 p.aboutToBeDeleted(); 566 } 567 this.storage_.clear(); 568 569 570 return true; 571 } 572 /** 573 * Subscribe to value change notifications of named property 574 * Any object implementing ISinglePropertyChangeSubscriber interface 575 * and registerign itself to SubscriberManager can register 576 * Caution: do remember to unregister, otherwise the property will block 577 * cleanup, @see delete() and @see clear() 578 * 579 * @param propName property in LocalStorage to subscribe to 580 * @param subscriber object that implements ISinglePropertyChangeSubscriber interface 581 * @returns false if named property does not exist 582 * 583 * @since 9 584 */ 585 subscribeToChangesOf(propName, subscriber) { 586 var p = this.storage_.get(propName); 587 if (p) { 588 p.addSubscriber(subscriber); 589 return true; 590 } 591 return false; 592 } 593 /** 594 * inverse of @see subscribeToChangesOf 595 * @param propName property in LocalStorage to subscribe to 596 * @param subscriberId id of the subscrber passed to @see subscribeToChangesOf 597 * @returns false if named property does not exist 598 * 599 * @since 9 600 */ 601 unsubscribeFromChangesOf(propName, subscriberId) { 602 var p = this.storage_.get(propName); 603 if (p) { 604 p.removeSubscriber(null, subscriberId); 605 return true; 606 } 607 return false; 608 } 609 __createSync(storagePropName, defaultValue, factoryFunc) { 610 let p = this.storage_.get(storagePropName); 611 if (p == undefined) { 612 // property named 'storagePropName' not yet in storage 613 // add new property to storage 614 // We do not want to add undefined to older API verions, but null is added 615 if (defaultValue === undefined && !Utils.isApiVersionEQAbove(12)) { 616 stateMgmtConsole.error(`${this.constructor.name}.__createSync(${storagePropName}, non-existing property and undefined default value. ERROR.`); 617 return undefined; 618 } 619 p = this.addNewPropertyInternal(storagePropName, defaultValue); 620 } 621 return factoryFunc(p); 622 } 623} 624/* 625 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 626 * Licensed under the Apache License, Version 2.0 (the "License"); 627 * you may not use this file except in compliance with the License. 628 * You may obtain a copy of the License at 629 * 630 * http://www.apache.org/licenses/LICENSE-2.0 631 * 632 * Unless required by applicable law or agreed to in writing, software 633 * distributed under the License is distributed on an "AS IS" BASIS, 634 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 635 * See the License for the specific language governing permissions and 636 * limitations under the License. 637 */ 638/** 639 * 640 * AppStorage 641 * 642 * Class implements a Map of ObservableObjectBase UI state variables. 643 * AppStorage singleton is sub-class of @see LocalStorage for 644 * UI state of app-wide access and same life cycle as the app. 645 * 646 * @since 7 647 */ 648class AppStorage extends LocalStorage { 649 /** singleton class, app can not create instances 650 * 651 * not a public / sdk function 652 */ 653 constructor(initializingProperties) { 654 super(initializingProperties); 655 } 656 /** 657 * create and initialize singleton 658 * initialzie with all properties and their values that Object.keys(params) returns 659 * Property values must not be undefined. 660 * 661 * not a public / sdk function 662 */ 663 static createSingleton(initializingPropersties) { 664 if (!AppStorage.instance_) { 665 666 AppStorage.instance_ = new AppStorage(initializingPropersties); 667 } 668 else { 669 stateMgmtConsole.error('AppStorage.createNewInstance(..): instance exists already, internal error!'); 670 } 671 } 672 /** 673 * Obtain a handle or an alias to AppStorage property with given name. 674 * 675 * @param propName AppStorage property name 676 * @returns AbstractProperty object is property with given name exists 677 * undefined otherwise 678 * 679 * @since 12 680 */ 681 static ref(propName) { 682 return AppStorage.getOrCreate().ref(propName); 683 } 684 /** 685 * Obtain a handle or an alias to AppStorage property with given name. 686 * 687 * If property does not exist in AppStorage, create it with given default value. 688 * 689 * @param propName LocalStorage property name 690 * @param defaultValue If property does not exist in AppStorage, 691 * create it with given default value. 692 * @returns AbstractProperty object 693 * 694 * @since 12 695 */ 696 static setAndRef(propName, defaultValue) { 697 return AppStorage.getOrCreate().setAndRef(propName, defaultValue); 698 } 699 /** 700 * create and return a two-way sync "(link") to named property 701 * 702 * Same as @see LocalStorage.link() 703 * 704 * @param propName name of source property in AppStorage 705 * @param linkUser IPropertySubscriber to be notified when source changes, 706 * @param subscribersName the linkUser (subscriber) uses this name for the property 707 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 708 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 709 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 710 * return undefiend if named property does not already exist in AppStorage 711 * 712 * @since 10 713 */ 714 static link(key, linkUser, subscribersName) { 715 return AppStorage.getOrCreate().link(key, linkUser, subscribersName); 716 } 717 /** 718 * @see link 719 * @since 7 720 * @deprecated 721 */ 722 static Link(key, linkUser, subscribersName) { 723 return AppStorage.getOrCreate().link(key, linkUser, subscribersName); 724 } 725 /** 726 * Like @see link(), but will create and initialize a new source property in LocalStorage if missing 727 * 728 * Same as @see LocalStorage.setAndLink() 729 * 730 * @param propName name of source property in AppStorage 731 * @param defaultValue value to be used for initializing if new creating new property in AppStorage 732 * default value must be of type S, must not be undefined or null. 733 * @param linkUser IPropertySubscriber to be notified when return 'link' changes, 734 * @param subscribersName the linkUser (subscriber) uses this name for the property 735 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 736 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 737 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 738 * 739 * @since 10 740 */ 741 static setAndLink(key, defaultValue, linkUser, subscribersName) { 742 return AppStorage.getOrCreate().setAndLink(key, defaultValue, linkUser, subscribersName); 743 } 744 /** 745 * @see setAndLink 746 * @since 7 747 * @deprecated 748 */ 749 static SetAndLink(key, defaultValue, linkUser, subscribersName) { 750 return AppStorage.getOrCreate().setAndLink(key, defaultValue, linkUser, subscribersName); 751 } 752 /** 753 * create and return a one-way sync ('prop') to named property 754 * 755 * Same as @see LocalStorage.prop() 756 * 757 * @param propName name of source property in AppStorage 758 * @param propUser IPropertySubscriber to be notified when source changes, 759 * @param subscribersName the linkUser (subscriber) uses this name for the property 760 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 761 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 762 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 763 * return undefiend if named property does not already exist in AppStorage. 764 * @since 10 765 */ 766 static prop(propName, propUser, subscribersName) { 767 return AppStorage.getOrCreate().prop(propName, propUser, subscribersName); 768 } 769 /** 770 * @see prop 771 * @since 7 772 * @deprecated 773 */ 774 static Prop(propName, propUser, subscribersName) { 775 return AppStorage.getOrCreate().prop(propName, propUser, subscribersName); 776 } 777 /** 778 * Like @see prop(), will create and initialize a new source property in AppStorage if missing 779 * 780 * Same as @see LocalStorage.setAndProp() 781 * 782 * @param propName name of source property in AppStorage 783 * @param defaultValue value to be used for initializing if new creating new property in AppStorage. 784 * default value must be of type S, must not be undefined or null. 785 * @param propUser IPropertySubscriber to be notified when returned 'prop' changes, 786 * @param subscribersName the propUser (subscriber) uses this name for the property 787 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 788 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 789 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 790 * 791 * @since 10 792 */ 793 static setAndProp(key, defaultValue, propUser, subscribersName) { 794 return AppStorage.getOrCreate().setAndProp(key, defaultValue, propUser, subscribersName); 795 } 796 /** 797 * @see setAndProp 798 * @since 7 799 * @deprecated 800 */ 801 static SetAndProp(key, defaultValue, propUser, subscribersName) { 802 return AppStorage.getOrCreate().setAndProp(key, defaultValue, propUser, subscribersName); 803 } 804 /** 805 * Check if AppStorage has a property with given name 806 * return true if property with given name exists 807 * same as ES6 Map.prototype.has() 808 * 809 * Same as @see LocalStorage.has() 810 * 811 * @param propName searched property 812 * @returns true if property with such name exists in AppStorage 813 * 814 * @since 10 815 */ 816 static has(key) { 817 return AppStorage.getOrCreate().has(key); 818 } 819 /** 820 * @see has() 821 * @since 7 822 * @deprecated 823 */ 824 static Has(key) { 825 return AppStorage.getOrCreate().has(key); 826 } 827 /** 828 * Returns value of given property 829 * return undefined if no property with this name 830 * 831 * @Same as see LocalStorage.get() 832 * 833 * @param propName 834 * @returns property value if found or undefined 835 * 836 * @since 10 837 * 838 */ 839 static get(key) { 840 return AppStorage.getOrCreate().get(key); 841 } 842 /** 843 * @see get 844 * @since 7 845 * @deprecated 846 * 847 */ 848 static Get(key) { 849 return AppStorage.getOrCreate().get(key); 850 } 851 /** 852 * Set value of given property in AppStorage 853 * Method sets nothing and returns false if property with this name does not exist 854 * or if newValue is `undefined` or `null` (`undefined`, `null` value are not allowed for state variables). 855 * 856 * Same as @see LocalStorage.set 857 * 858 * @param propName 859 * @param newValue must be of type T and must not be undefined or null 860 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 861 * 862 * @since 10 863 */ 864 static set(key, newValue) { 865 return AppStorage.getOrCreate().set(key, newValue); 866 } 867 /** 868 * @see set 869 * @since 7 870 * @deprecated 871 */ 872 static Set(key, newValue) { 873 return AppStorage.getOrCreate().set(key, newValue); 874 } 875 /** 876 * Set value of given property, if it exists, @see set() . 877 * Add property if no property with given name and initialize with given value. 878 * Do nothing and return false if newValuue is undefined or null 879 * (undefined, null value is not allowed for state variables) 880 * 881 * @see LocalStorage.setOrCreate() 882 * 883 * @param propName 884 * @param newValue must be of type T and must not be undefined or null 885 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 886 * 887 * @since 10 888 */ 889 static setOrCreate(key, newValue) { 890 AppStorage.getOrCreate().setOrCreate(key, newValue); 891 } 892 /** 893 * @see setOrCreate 894 * @since 7 895 * @deprecated 896 */ 897 static SetOrCreate(key, newValue) { 898 AppStorage.getOrCreate().setOrCreate(key, newValue); 899 } 900 /** 901 * Delete property from StorageBase 902 * Use with caution: 903 * Before deleting a prop from AppStorage all its subscribers need to 904 * unsubscribe from the property. 905 * This method fails and returns false if given property still has subscribers 906 * Another reason for failing is unkmown property. 907 * 908 * Developer advise: 909 * Subscribers are created with @see link(), @see prop() 910 * and also via @LocalStorageLink and @LocalStorageProp state variable decorators. 911 * That means as long as their is a @Component instance that uses such decorated variable 912 * or a sync relationship with a SubscribedAbstractProperty variable the property can nit 913 * (and also should not!) be deleted from AppStorage. 914 * 915 * Same as @see LocalStorage.delete() 916 * 917 * @param propName 918 * @returns false if method failed 919 * 920 * @since 10 921 */ 922 static delete(key) { 923 return AppStorage.getOrCreate().delete(key); 924 } 925 /** 926 * @see delete 927 * @since 7 928 * @deprecated 929 */ 930 static Delete(key) { 931 return AppStorage.getOrCreate().delete(key); 932 } 933 /** 934 * Provide names of all properties in AppStorage 935 * same as ES6 Map.prototype.keys() 936 * 937 * Same as @see LocalStorage.keys() 938 * 939 * @returns return a Map Iterator 940 * 941 * @since 10 942 */ 943 static keys() { 944 return AppStorage.getOrCreate().keys(); 945 } 946 /** 947 * @see keys 948 * @since 7 949 * @deprecated 950 */ 951 static Keys() { 952 return AppStorage.getOrCreate().keys(); 953 } 954 /** 955 * Returns number of properties in AppStorage 956 * same as Map.prototype.size() 957 * 958 * Same as @see LocalStorage.size() 959 * 960 * @param propName 961 * @returns return number of properties 962 * 963 * @since 10 964 */ 965 static size() { 966 return AppStorage.getOrCreate().size(); 967 } 968 /** 969 * @see size 970 * @since 7 971 * @deprecated 972 */ 973 static Size() { 974 return AppStorage.getOrCreate().size(); 975 } 976 /** 977 * delete all properties from the AppStorage 978 * 979 * @see delete(), same as @see LocalStorage.clear() 980 * 981 * precondition is that there are no subscribers. 982 * method returns false and deletes no poperties if there is any property 983 * that still has subscribers 984 * 985 * @since 10 986 */ 987 static clear() { 988 return AppStorage.getOrCreate().clear(); 989 } 990 /** 991 * @see clear 992 * @since 7 993 * @deprecated 994 */ 995 static Clear() { 996 return AppStorage.getOrCreate().clear(); 997 } 998 /** 999 * Same as @see clear(). 1000 * 1001 * @since 7, deprecated, used clear() instead! 1002 * 1003 */ 1004 static StaticClear() { 1005 return AppStorage.clear(); 1006 } 1007 /** 1008 * not a public / sdk function 1009 */ 1010 static aboutToBeDeleted() { 1011 AppStorage.getOrCreate().aboutToBeDeleted(); 1012 } 1013 /** 1014 * Subscribe to value change notifications of named property 1015 * Any object implementing ISinglePropertyChangeSubscriber interface 1016 * and registerign itself to SubscriberManager can register 1017 * Caution: do remember to unregister, otherwise the property will block 1018 * cleanup, @see delete() and @see clear() 1019 * 1020 * Same as @see LocalStorage.subscribeToChangesOf() 1021 * 1022 * @param propName property in AppStorage to subscribe to 1023 * @param subscriber object that implements ISinglePropertyChangeSubscriber interface 1024 * @returns false if named property does not exist 1025 * 1026 * @since 10 1027 */ 1028 static subscribeToChangesOf(propName, subscriber) { 1029 return AppStorage.getOrCreate().subscribeToChangesOf(propName, subscriber); 1030 } 1031 /** 1032 * @see subscribeToChangesOf 1033 * @since 7 1034 * @deprecated 1035 */ 1036 static SubscribeToChangesOf(propName, subscriber) { 1037 return AppStorage.getOrCreate().subscribeToChangesOf(propName, subscriber); 1038 } 1039 /** 1040 * inverse of @see SubscribeToChangesOf, 1041 * same as @see LocalStorage.subscribeToChangesOf() 1042 * 1043 * @param propName property in AppStorage to subscribe to 1044 * @param subscriberId id of the subscrber passed to @see subscribeToChangesOf 1045 * @returns false if named property does not exist 1046 * 1047 * @since 10 1048 */ 1049 static unsubscribeFromChangesOf(propName, subscriberId) { 1050 return AppStorage.getOrCreate().unsubscribeFromChangesOf(propName, subscriberId); 1051 } 1052 /** 1053 * @see unsubscribeFromChangesOf 1054 * @since 7 1055 * @deprecated 1056 */ 1057 static UnsubscribeFromChangesOf(propName, subscriberId) { 1058 return AppStorage.getOrCreate().unsubscribeFromChangesOf(propName, subscriberId); 1059 } 1060 /** 1061 * Unimplemented, currently all properties of AppStorage are mutable. 1062 * 1063 * @since 7, deprecated 1064 */ 1065 static IsMutable(key) { 1066 return true; 1067 } 1068 /** 1069 * not a public / sdk function 1070 */ 1071 static __createSync(storagePropName, defaultValue, factoryFunc) { 1072 return AppStorage.getOrCreate().__createSync(storagePropName, defaultValue, factoryFunc); 1073 } 1074 /** 1075 * not a public / sdk function 1076 */ 1077 static getOrCreate() { 1078 if (!AppStorage.instance_) { 1079 AppStorage.instance_ = new AppStorage({}); 1080 } 1081 return AppStorage.instance_; 1082 } 1083} 1084// instance functions below: 1085// Should all be protected, but TS lang does not allow access from static member to protected member 1086AppStorage.instance_ = undefined; 1087/* 1088 * Copyright (c) 2022 Huawei Device Co., Ltd. 1089 * Licensed under the Apache License, Version 2.0 (the "License"); 1090 * you may not use this file except in compliance with the License. 1091 * You may obtain a copy of the License at 1092 * 1093 * http://www.apache.org/licenses/LICENSE-2.0 1094 * 1095 * Unless required by applicable law or agreed to in writing, software 1096 * distributed under the License is distributed on an "AS IS" BASIS, 1097 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1098 * See the License for the specific language governing permissions and 1099 * limitations under the License. 1100 */ 1101/** 1102 * Singleton class SubscriberManager implements IPropertySubscriberLookup 1103 * public API to manage IPropertySubscriber 1104 */ 1105class SubscriberManager { 1106 /** 1107 * SubscriberManager is a singleton created by the framework 1108 * do not use 1109 * 1110 * internal method 1111 */ 1112 constructor() { 1113 this.subscriberById_ = new Map(); 1114 1115 } 1116 /** 1117 * check subscriber is known 1118 * same as ES6 Map.prototype.has() 1119 * 1120 * @since 9 1121 */ 1122 static Has(id) { 1123 return SubscriberManager.GetInstance().has(id); 1124 } 1125 /** 1126 * 1127 * retrieve subscriber by id 1128 * same as ES6 Map.prototype.get() 1129 * 1130 * @since 9 1131 */ 1132 static Find(id) { 1133 return SubscriberManager.GetInstance().get(id); 1134 } 1135 /** 1136 * unregister a subscriber 1137 * same as ES6 Map.prototype.delete() 1138 * @return boolean success or failure to delete 1139 * 1140 * @since 9 1141 */ 1142 static Delete(id) { 1143 return SubscriberManager.GetInstance().delete(id); 1144 } 1145 /** 1146 * add a new subscriber. 1147 * The subscriber must have a new (unused) id (@see MakeId() ) 1148 * for add() to succeed. 1149 * same as Map.prototype.set() 1150 * 1151 * @since 9 1152 */ 1153 static Add(newSubsriber) { 1154 return SubscriberManager.GetInstance().add(newSubsriber); 1155 } 1156 /** 1157 * Update recycle custom node element id. 1158 */ 1159 static UpdateRecycleElmtId(oldId, newId) { 1160 return SubscriberManager.GetInstance().updateRecycleElmtId(oldId, newId); 1161 } 1162 /** 1163 * 1164 * @returns a globally unique id to be assigned to a IPropertySubscriber objet 1165 * Use MakeId() to assign a IPropertySubscriber object an id before calling @see add() . 1166 * 1167 * @since 9 1168 */ 1169 static MakeId() { 1170 return SubscriberManager.GetInstance().makeId(); 1171 } 1172 /** 1173 * 1174 * @returns a global unique id for state variables. 1175 * Unlike MakeId, no need to get id from native side. 1176 * 1177 * @since 12 1178 */ 1179 static MakeStateVariableId() { 1180 return SubscriberManager.nextId_--; 1181 } 1182 /** 1183 * Check number of registered Subscriber / registered IDs. 1184 * @returns number of registered unique ids. 1185 * 1186 * @since 9 1187 */ 1188 static NumberOfSubscribers() { 1189 return SubscriberManager.GetInstance().numberOfSubscribers(); 1190 } 1191 /** 1192 * 1193 * internal (non-SDK) methods below 1194 * 1195 */ 1196 /** 1197 * Get singleton, create it on first call 1198 * @returns SubscriberManager singleton 1199 * 1200 * internal function 1201 * This function will be removed soon, use static functions instead! 1202 * Note: Fnction gets used by transpiler output for both full update and partial update 1203 */ 1204 static Get() { 1205 if (!SubscriberManager.instance_) { 1206 SubscriberManager.instance_ = new SubscriberManager(); 1207 } 1208 return SubscriberManager.instance_; 1209 } 1210 /** 1211 * Get singleton, create it on first call 1212 * @returns SubscriberManager singleton 1213 * 1214 * internal function 1215 */ 1216 static GetInstance() { 1217 if (!SubscriberManager.instance_) { 1218 SubscriberManager.instance_ = new SubscriberManager(); 1219 } 1220 return SubscriberManager.instance_; 1221 } 1222 /** 1223 * for debug purposes dump all known subscriber's info to comsole 1224 * 1225 * not a public / sdk function 1226 */ 1227 static DumpSubscriberInfo() { 1228 SubscriberManager.GetInstance().dumpSubscriberInfo(); 1229 } 1230 /** 1231 * not a public / sdk function 1232 * @see Has 1233 */ 1234 has(id) { 1235 return this.subscriberById_.has(id); 1236 } 1237 /** 1238 * not a public / sdk function 1239 * @see Get 1240 */ 1241 get(id) { 1242 return this.subscriberById_.get(id); 1243 } 1244 /** 1245 * not a public / sdk function 1246 * @see Delete 1247 */ 1248 delete(id) { 1249 if (!this.has(id)) { 1250 stateMgmtConsole.warn(`SubscriberManager.delete unknown id ${id} `); 1251 return false; 1252 } 1253 return this.subscriberById_.delete(id); 1254 } 1255 /** 1256 * not a public / sdk function 1257 * @see Add 1258 */ 1259 add(newSubsriber) { 1260 if (this.has(newSubsriber.id__())) { 1261 return false; 1262 } 1263 this.subscriberById_.set(newSubsriber.id__(), newSubsriber); 1264 return true; 1265 } 1266 updateRecycleElmtId(oldId, newId) { 1267 if (!this.has(oldId)) { 1268 return false; 1269 } 1270 const subscriber = this.get(oldId); 1271 this.subscriberById_.delete(oldId); 1272 this.subscriberById_.set(newId, subscriber); 1273 return true; 1274 } 1275 /** 1276 * Method for testing purposes 1277 * @returns number of subscribers 1278 * 1279 * not a public / sdk function 1280 */ 1281 numberOfSubscribers() { 1282 return this.subscriberById_.size; 1283 } 1284 /** 1285 * for debug purposes dump all known subscriber's info to comsole 1286 * 1287 * not a public / sdk function 1288 */ 1289 dumpSubscriberInfo() { 1290 1291 for (let [id, subscriber] of this.subscriberById_) { 1292 1293 } 1294 1295 } 1296 /** 1297 * 1298 * @returns a globally unique id to be assigned to a Subscriber 1299 */ 1300 makeId() { 1301 return ViewStackProcessor.MakeUniqueId(); 1302 } 1303} 1304SubscriberManager.nextId_ = 0; 1305/* 1306 * Copyright (c) 2022 Huawei Device Co., Ltd. 1307 * Licensed under the Apache License, Version 2.0 (the "License"); 1308 * you may not use this file except in compliance with the License. 1309 * You may obtain a copy of the License at 1310 * 1311 * http://www.apache.org/licenses/LICENSE-2.0 1312 * 1313 * Unless required by applicable law or agreed to in writing, software 1314 * distributed under the License is distributed on an "AS IS" BASIS, 1315 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1316 * See the License for the specific language governing permissions and 1317 * limitations under the License. 1318 */ 1319/** 1320 * 1321 * SubscribedAbstractProperty is base class of ObservedPropertyAbstract 1322 * and includes these 3 functions that are part of the SDK. 1323 * 1324 * SubscribedAbstractProperty<T> is the return type of 1325 * - AppStorage static functions Link(), Prop(), SetAndLink(), and SetAndProp() 1326 * - LocalStorage methods link(), prop(), setAndLink(), and setAndProp() 1327 * 1328 * 'T' can be boolean, string, number or custom class. 1329 * 1330 * Main functions 1331 * @see get() reads the linked AppStorage/LocalStorage property value, 1332 * @see set(newValue) write a new value to the synched AppStorage/LocalStorage property value 1333 * @see aboutToBeDeleted() ends the sync relationship with the AppStorage/LocalStorage property 1334 * The app must call this function before the SubscribedAbstractProperty<T> object 1335 * goes out of scope. 1336 * 1337 * @since 7 1338*/ 1339class SubscribedAbstractProperty { 1340} 1341/* 1342 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 1343 * Licensed under the Apache License, Version 2.0 (the "License"); 1344 * you may not use this file except in compliance with the License. 1345 * You may obtain a copy of the License at 1346 * 1347 * http://www.apache.org/licenses/LICENSE-2.0 1348 * 1349 * Unless required by applicable law or agreed to in writing, software 1350 * distributed under the License is distributed on an "AS IS" BASIS, 1351 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1352 * See the License for the specific language governing permissions and 1353 * limitations under the License. 1354 */ 1355/** 1356 * 1357 * SubscribableAbstract 1358 * 1359 * This class is part of the SDK. 1360 * @since 10 1361 * 1362 * SubscribableAbstract is an abstract class that manages subscribers 1363 * to value changes. These subscribers are the implementation of 1364 * @State, @Link, @Provide, @Consume decorated variables inside the 1365 * framework. Each using @State, @Link, etc., decorated variable in 1366 * a @Component will make its own subscription. When the component 1367 * is created the subscription is added, and before the component 1368 * is deleted it unsubscribes 1369 * 1370 * An application may extend SubscribableAbstract for a custom class 1371 * that manages state data. @State, @Link, @Provide, @Consume 1372 * decorated variables can hold an Object that is instance of 1373 * SubscribableAbstract. 1374 * 1375 * About lifecycle: It is legal use for two @Components with two @State 1376 * decorated variables to share the same SubscribableAbstract object. 1377 * Each such decorated variable implementation makes its own 1378 * subscription to the SubscribableAbstract object. Hence, when both variables 1379 * have unsubscribed the SubscribableAbstract custom class may do its own 1380 * de-initialization, e.g. release held external resources. 1381 * 1382 * How to extend: 1383 * A subclass manages the get and set to one or several properties on its own. 1384 * The subclass needs to notify all relevant value changes to the framework for the 1385 * UI to be updated. Notification should only be given for class properties that 1386 * are used to generate the UI. 1387 * 1388 * A subclass must call super() in its constructor to let this base class 1389 * initialize itself. 1390 * 1391 * A subclass must call 'notifyPropertyHasChanged*(' after the relevant property 1392 * has changes. The framework will notify all dependent components to re-render. 1393 * 1394 * A sub-class may overwrite the 'addOwningProperty' function to add own 1395 * functionality, but it must call super.addowningOwningProperty(..). E.g. 1396 * the sub-class could connect to external resources upon the first subscriber. 1397 * 1398 * A sub-class may also overwrite the 'removeOwningProperty' function or 1399 * 'removeOwningPropertyById' function to add own functionality, 1400 * but it must call super.removeOwningProperty(..). 1401 * E.g. the sub-class could release held external resources upon loosing the 1402 * last subscriber. 1403 * 1404 */ 1405class SubscribableAbstract { 1406 /** 1407 * make sure to call super() from subclass constructor! 1408 * 1409 * @since 10 1410 */ 1411 constructor() { 1412 this.owningProperties_ = new Set(); 1413 1414 } 1415 /** 1416 * A subsclass must call this function whenever one of its properties has 1417 * changed that is used to construct the UI. 1418 * @param propName name of the change property 1419 * @param newValue the property value after the change 1420 * 1421 * @since 10 1422 */ 1423 notifyPropertyHasChanged(propName, newValue) { 1424 1425 this.owningProperties_.forEach((subscribedId) => { 1426 let owningProperty = SubscriberManager.Find(subscribedId); 1427 if (!owningProperty) { 1428 stateMgmtConsole.error(`SubscribableAbstract: notifyHasChanged: unknown subscriber.'${subscribedId}' error!.`); 1429 return; 1430 } 1431 // PU code path 1432 if ('onTrackedObjectPropertyCompatModeHasChangedPU' in owningProperty) { 1433 owningProperty.onTrackedObjectPropertyCompatModeHasChangedPU(this, propName); 1434 } 1435 // FU code path 1436 if ('hasChanged' in owningProperty) { 1437 owningProperty.hasChanged(newValue); 1438 } 1439 if ('propertyHasChanged' in owningProperty) { 1440 owningProperty.propertyHasChanged(propName); 1441 } 1442 }); 1443 } 1444 /** 1445 * Provides the current number of subscribers. 1446 * Application may use this function to determine a shared object has no more remaining subscribers and can be deleted. 1447 * @returns number of current subscribers 1448 * 1449 * @since 10 1450 */ 1451 numberOfSubscribers() { 1452 return this.owningProperties_.size; 1453 } 1454 /** 1455 * Method used by the framework to add subscribing decorated variables 1456 * Subclass may overwrite this function but must call the function of the base 1457 * class from its own implementation. 1458 * @param subscriber new subscriber that implements ISinglePropertyChangeSubscriber 1459 * and/or IMultiPropertiesChangeSubscriber interfaces 1460 * 1461 * @since 10 1462 */ 1463 addOwningProperty(subscriber) { 1464 1465 this.owningProperties_.add(subscriber.id__()); 1466 } 1467 /** 1468 * Method used by the framework to unsubscribing decorated variables 1469 * Subclass may overwrite this function but must call the function of the base 1470 * class from its own implementation. 1471 * @param subscriber subscriber that implements ISinglePropertyChangeSubscriber 1472 * and/or IMultiPropertiesChangeSubscriber interfaces 1473 * 1474 * @since 10 1475 */ 1476 removeOwningProperty(property) { 1477 return this.removeOwningPropertyById(property.id__()); 1478 } 1479 /** 1480 * Same as @see removeOwningProperty() but by Subscriber id. 1481 * @param subscriberId 1482 * 1483 * framework internal function, not to be used by applications. 1484 */ 1485 removeOwningPropertyById(subscriberId) { 1486 1487 this.owningProperties_.delete(subscriberId); 1488 } 1489 /** 1490 * flush all subscribers / owning properties 1491 * This is needed when copying a SubscribableAbstract object to the localObject or @prop / SynchedPropertyObjectOneWay 1492 * - shallowCopy: copies the _reference to original_ Set. Hence, we must not modify this Set but assign a new Set 1493 * - deepCopy also (deep-) copies this class' owningProperties_ Set, incl. the numbers it includes. Assigning a new Set fixes. 1494 * 1495 */ 1496 clearOwningProperties() { 1497 this.owningProperties_ = new Set(); 1498 } 1499} 1500/** 1501 * SubscribaleAbstract class with typo in its nam,e 1502 * 1503 * @depreciated, use SubscribableAbstract 1504 */ 1505class SubscribaleAbstract extends SubscribableAbstract { 1506} 1507/* 1508 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 1509 * Licensed under the Apache License, Version 2.0 (the "License"); 1510 * you may not use this file except in compliance with the License. 1511 * You may obtain a copy of the License at 1512 * 1513 * http://www.apache.org/licenses/LICENSE-2.0 1514 * 1515 * Unless required by applicable law or agreed to in writing, software 1516 * distributed under the License is distributed on an "AS IS" BASIS, 1517 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1518 * See the License for the specific language governing permissions and 1519 * limitations under the License. 1520 */ 1521/** 1522 * file version 1523 * 1524 * To indicate the file formate 1525 * 1526 */ 1527var ObjectVersion; 1528(function (ObjectVersion) { 1529 ObjectVersion[ObjectVersion["NewVersion"] = 0] = "NewVersion"; 1530 ObjectVersion[ObjectVersion["CompatibleVersion"] = 1] = "CompatibleVersion"; 1531 ObjectVersion[ObjectVersion["Default"] = 2] = "Default"; 1532})(ObjectVersion || (ObjectVersion = {})); 1533class MapInfo { 1534 constructor(mapReplacer, keyToValue) { 1535 this.mapReplacer = mapReplacer; 1536 this.keyToValue = keyToValue; 1537 } 1538 // Check if the given object is of type MapInfo 1539 static isObject(obj) { 1540 const typedObject = obj; 1541 if ('mapReplacer' in typedObject && typedObject.mapReplacer === MapInfo.replacer) { 1542 return ObjectVersion.NewVersion; 1543 } 1544 if ('mapReplacer' in typedObject && typedObject.mapReplacer === MapInfo.replacerCompatible) { 1545 return ObjectVersion.CompatibleVersion; 1546 } 1547 return ObjectVersion.Default; 1548 } 1549 // Convert Map to Object 1550 static toObject(map) { 1551 let mapItems = []; 1552 map.forEach((val, key) => { 1553 mapItems.push({ key: key, value: val }); 1554 }); 1555 return new MapInfo(MapInfo.replacer, mapItems); 1556 } 1557 // Convert Object to Map 1558 static toMap(obj) { 1559 return new Map(obj.keyToValue.map((item) => [item.key, item.value])); 1560 } 1561 static toMapCompatible(obj) { 1562 return new Map(obj.keys.map((key, i) => [key, obj.values[i]])); 1563 } 1564} 1565MapInfo.replacer = '_____map_replacer__'; 1566MapInfo.replacerCompatible = 'ace_engine_state_mgmt_map_replacer'; 1567/** 1568 * SetInfo 1569 * 1570 * Helper class to persist Set in Persistent storage 1571 * 1572 */ 1573class SetInfo { 1574 constructor(setReplacer, values) { 1575 this.setReplacer = setReplacer; 1576 this.values = values; 1577 } 1578 // Check if the given object is of type SetInfo 1579 static isObject(obj) { 1580 const typedObject = obj; 1581 if ('setReplacer' in typedObject && 1582 (typedObject.setReplacer === SetInfo.replacer || typedObject.setReplacer === SetInfo.replacerCompatible)) { 1583 return true; 1584 } 1585 return false; 1586 } 1587 // Convert Set to Object 1588 static toObject(set) { 1589 const values = Array.from(set.values()); 1590 return new SetInfo(SetInfo.replacer, values); 1591 } 1592 // Convert Object to Set 1593 static toSet(obj) { 1594 return new Set(obj.values); 1595 } 1596} 1597SetInfo.replacer = '_____set_replacer__'; 1598SetInfo.replacerCompatible = "ace_engine_state_mgmt_set_replacer"; 1599/** 1600 * DateInfo 1601 * 1602 * Helper class to persist Date in Persistent storage 1603 * 1604 */ 1605class DateInfo { 1606 constructor(dateReplacer, date) { 1607 this.dateReplacer = dateReplacer; 1608 this.date = date; 1609 } 1610 // Check if the given object is of type DateInfo 1611 static isObject(obj) { 1612 const typedObject = obj; 1613 if ('dateReplacer' in typedObject && 1614 (typedObject.dateReplacer === DateInfo.replacer || typedObject.dateReplacer === DateInfo.replacerCompatible)) { 1615 return true; 1616 } 1617 return false; 1618 } 1619 // Convert Date to Object 1620 static toObject(date) { 1621 return new DateInfo(DateInfo.replacer, date.toISOString()); 1622 } 1623 // Convert Object to Date 1624 static toDate(obj) { 1625 return new Date(obj.date); 1626 } 1627} 1628DateInfo.replacer = '_____date_replacer__'; 1629DateInfo.replacerCompatible = "ace_engine_state_mgmt_date_replacer"; 1630/** 1631 * PersistentStorage 1632 * 1633 * Keeps current values of select AppStorage property properties persisted to file. 1634 * 1635 * since 9 1636 */ 1637class PersistentStorage { 1638 /** 1639 * all following methods are framework internal 1640 */ 1641 constructor() { 1642 this.links_ = new Map(); 1643 this.id_ = SubscriberManager.MakeId(); 1644 SubscriberManager.Add(this); 1645 } 1646 /** 1647 * 1648 * @param storage method to be used by the framework to set the backend 1649 * this is to be done during startup 1650 * 1651 * internal function, not part of the SDK 1652 * 1653 */ 1654 static configureBackend(storage) { 1655 PersistentStorage.storage_ = storage; 1656 } 1657 /** 1658 * private, use static functions! 1659 */ 1660 static getOrCreate() { 1661 if (PersistentStorage.instance_) { 1662 // already initialized 1663 return PersistentStorage.instance_; 1664 } 1665 PersistentStorage.instance_ = new PersistentStorage(); 1666 return PersistentStorage.instance_; 1667 } 1668 /** 1669 * 1670 * internal function, not part of the SDK 1671 */ 1672 static aboutToBeDeleted() { 1673 if (!PersistentStorage.instance_) { 1674 return; 1675 } 1676 PersistentStorage.getOrCreate().aboutToBeDeleted(); 1677 PersistentStorage.instance_ = undefined; 1678 } 1679 /** 1680 * Add property 'key' to AppStorage properties whose current value will be 1681 * persistent. 1682 * If AppStorage does not include this property it will be added and initializes 1683 * with given value 1684 * 1685 * @since 10 1686 * 1687 * @param key property name 1688 * @param defaultValue If AppStorage does not include this property it will be initialized with this value 1689 * 1690 */ 1691 static persistProp(key, defaultValue) { 1692 PersistentStorage.getOrCreate().persistProp(key, defaultValue); 1693 } 1694 /** 1695 * @see persistProp 1696 * @deprecated 1697 */ 1698 static PersistProp(key, defaultValue) { 1699 PersistentStorage.getOrCreate().persistProp(key, defaultValue); 1700 } 1701 /** 1702 * Reverse of @see persistProp 1703 * @param key no longer persist the property named key 1704 * 1705 * @since 10 1706 */ 1707 static deleteProp(key) { 1708 PersistentStorage.getOrCreate().deleteProp(key); 1709 } 1710 /** 1711 * @see deleteProp 1712 * @deprecated 1713 */ 1714 static DeleteProp(key) { 1715 PersistentStorage.getOrCreate().deleteProp(key); 1716 } 1717 /** 1718 * Persist given AppStorage properties with given names. 1719 * If a property does not exist in AppStorage, add it and initialize it with given value 1720 * works as @see persistProp for multiple properties. 1721 * 1722 * @param properties 1723 * 1724 * @since 10 1725 * 1726 */ 1727 static persistProps(properties) { 1728 PersistentStorage.getOrCreate().persistProps(properties); 1729 } 1730 /** 1731 * @see persistProps 1732 * @deprecated 1733 */ 1734 static PersistProps(properties) { 1735 PersistentStorage.getOrCreate().persistProps(properties); 1736 } 1737 /** 1738 * Inform persisted AppStorage property names 1739 * @returns array of AppStorage keys 1740 * 1741 * @since 10 1742 */ 1743 static keys() { 1744 let result = []; 1745 const it = PersistentStorage.getOrCreate().keys(); 1746 let val = it.next(); 1747 while (!val.done) { 1748 result.push(val.value); 1749 val = it.next(); 1750 } 1751 return result; 1752 } 1753 /** 1754 * @see keys 1755 * @deprecated 1756 */ 1757 static Keys() { 1758 return PersistentStorage.keys(); 1759 } 1760 /** 1761 * This methid offers a way to force writing the property value with given 1762 * key to persistent storage. 1763 * In the general case this is unnecessary as the framework observed changes 1764 * and triggers writing to disk by itself. For nested objects (e.g. array of 1765 * objects) however changes of a property of a property as not observed. This 1766 * is the case where the application needs to signal to the framework. 1767 * 1768 * @param key property that has changed 1769 * 1770 * @since 10 1771 * 1772 */ 1773 static notifyHasChanged(propName) { 1774 1775 PersistentStorage.getOrCreate().writeToPersistentStorage(propName, PersistentStorage.getOrCreate().links_.get(propName).get()); 1776 } 1777 /** 1778 * @see notifyHasChanged 1779 * @deprecated 1780 */ 1781 static NotifyHasChanged(propName) { 1782 1783 PersistentStorage.getOrCreate().writeToPersistentStorage(propName, PersistentStorage.getOrCreate().links_.get(propName).get()); 1784 } 1785 keys() { 1786 return this.links_.keys(); 1787 } 1788 persistProp(propName, defaultValue) { 1789 if (this.persistProp1(propName, defaultValue)) { 1790 // persist new prop 1791 1792 this.writeToPersistentStorage(propName, this.links_.get(propName).get()); 1793 } 1794 } 1795 // helper function to persist a property 1796 // does everything except writing prop to disk 1797 persistProp1(propName, defaultValue) { 1798 1799 if (defaultValue == null && !Utils.isApiVersionEQAbove(12)) { 1800 stateMgmtConsole.error(`PersistentStorage: persistProp for ${propName} called with 'null' or 'undefined' default value!`); 1801 return false; 1802 } 1803 if (this.links_.get(propName)) { 1804 stateMgmtConsole.warn(`PersistentStorage: persistProp: ${propName} is already persisted`); 1805 return false; 1806 } 1807 let link = AppStorage.link(propName, this); 1808 if (link) { 1809 1810 this.links_.set(propName, link); 1811 } 1812 else { 1813 let returnValue; 1814 if (!PersistentStorage.storage_.has(propName)) { 1815 1816 returnValue = defaultValue; 1817 } 1818 else { 1819 returnValue = this.readFromPersistentStorage(propName); 1820 } 1821 link = AppStorage.setAndLink(propName, returnValue, this); 1822 if (link === undefined) { 1823 1824 return false; 1825 } 1826 this.links_.set(propName, link); 1827 1828 } 1829 return true; 1830 } 1831 persistProps(properties) { 1832 properties.forEach(property => this.persistProp1(property.key, property.defaultValue)); 1833 this.write(); 1834 } 1835 deleteProp(propName) { 1836 let link = this.links_.get(propName); 1837 if (link) { 1838 link.aboutToBeDeleted(); 1839 this.links_.delete(propName); 1840 PersistentStorage.storage_.delete(propName); 1841 1842 } 1843 else { 1844 stateMgmtConsole.warn(`PersistentStorage: '${propName}' is not a persisted property warning.`); 1845 } 1846 } 1847 write() { 1848 this.links_.forEach((link, propName, map) => { 1849 1850 this.writeToPersistentStorage(propName, link.get()); 1851 }); 1852 } 1853 // helper function to write to the persistent storage 1854 // any additional check and formatting can to be done here 1855 writeToPersistentStorage(propName, value) { 1856 if (value instanceof Map) { 1857 value = MapInfo.toObject(value); 1858 } 1859 else if (value instanceof Set) { 1860 value = SetInfo.toObject(value); 1861 } 1862 else if (value instanceof Date) { 1863 value = DateInfo.toObject(value); 1864 } 1865 PersistentStorage.storage_.set(propName, value); 1866 } 1867 // helper function to read from the persistent storage 1868 // any additional check and formatting can to be done here 1869 readFromPersistentStorage(propName) { 1870 let newValue = PersistentStorage.storage_.get(propName); 1871 if (newValue instanceof Object) { 1872 if (MapInfo.isObject(newValue) === ObjectVersion.NewVersion) { 1873 newValue = MapInfo.toMap(newValue); 1874 } 1875 else if (MapInfo.isObject(newValue) === ObjectVersion.CompatibleVersion) { 1876 newValue = MapInfo.toMapCompatible(newValue); 1877 } 1878 else if (SetInfo.isObject(newValue)) { 1879 newValue = SetInfo.toSet(newValue); 1880 } 1881 else if (DateInfo.isObject(newValue)) { 1882 newValue = DateInfo.toDate(newValue); 1883 } 1884 } 1885 return newValue; 1886 } 1887 // FU code path method 1888 propertyHasChanged(info) { 1889 1890 this.write(); 1891 } 1892 // PU code path method 1893 syncPeerHasChanged(eventSource) { 1894 1895 this.write(); 1896 } 1897 // public required by the interface, use the static method instead! 1898 aboutToBeDeleted() { 1899 1900 this.links_.forEach((val, key, map) => { 1901 1902 val.aboutToBeDeleted(); 1903 }); 1904 this.links_.clear(); 1905 SubscriberManager.Delete(this.id__()); 1906 PersistentStorage.storage_.clear(); 1907 } 1908 id__() { 1909 return this.id_; 1910 } 1911} 1912PersistentStorage.instance_ = undefined; 1913; 1914/* 1915 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 1916 * Licensed under the Apache License, Version 2.0 (the "License"); 1917 * you may not use this file except in compliance with the License. 1918 * You may obtain a copy of the License at 1919 * 1920 * http://www.apache.org/licenses/LICENSE-2.0 1921 * 1922 * Unless required by applicable law or agreed to in writing, software 1923 * distributed under the License is distributed on an "AS IS" BASIS, 1924 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1925 * See the License for the specific language governing permissions and 1926 * limitations under the License. 1927 */ 1928/** 1929 * Environment 1930 * 1931 * Injects device properties ("environment") into AppStorage 1932 * 1933 */ 1934class Environment { 1935 constructor() { 1936 this.props_ = new Map(); 1937 Environment.envBackend_.onValueChanged(this.onValueChanged.bind(this)); 1938 } 1939 static getOrCreate() { 1940 if (Environment.instance_) { 1941 // already initialized 1942 return Environment.instance_; 1943 } 1944 Environment.instance_ = new Environment(); 1945 return Environment.instance_; 1946 } 1947 static configureBackend(envBackend) { 1948 Environment.envBackend_ = envBackend; 1949 } 1950 /** 1951 * @see configureBackend 1952 * @deprecated 1953 */ 1954 static ConfigureBackend(envBackend) { 1955 Environment.envBackend_ = envBackend; 1956 } 1957 static aboutToBeDeleted() { 1958 if (!Environment.instance_) { 1959 return; 1960 } 1961 Environment.getOrCreate().aboutToBeDeleted(); 1962 Environment.instance_ = undefined; 1963 } 1964 /** 1965 * @see aboutToBeDeleted 1966 * @deprecated 1967 */ 1968 static AboutToBeDeleted() { 1969 Environment.aboutToBeDeleted(); 1970 } 1971 static envProp(key, value) { 1972 return Environment.getOrCreate().envProp(key, value); 1973 } 1974 /** 1975 * @see envProp 1976 * @deprecated 1977 */ 1978 static EnvProp(key, value) { 1979 return Environment.getOrCreate().envProp(key, value); 1980 } 1981 static envProps(props) { 1982 Environment.getOrCreate().envProps(props); 1983 } 1984 /** 1985 * @see envProps 1986 * @deprecated 1987 */ 1988 static EnvProps(props) { 1989 Environment.getOrCreate().envProps(props); 1990 } 1991 static keys() { 1992 return Environment.getOrCreate().keys(); 1993 } 1994 /** 1995 * @see keys 1996 * @deprecated 1997 */ 1998 static Keys() { 1999 return Environment.getOrCreate().keys(); 2000 } 2001 envProp(key, value) { 2002 let prop = AppStorage.prop(key); 2003 if (prop) { 2004 stateMgmtConsole.warn(`Environment: envProp '${key}': Property already exists in AppStorage. Not using environment property.`); 2005 return false; 2006 } 2007 let tmp; 2008 switch (key) { 2009 case 'accessibilityEnabled': 2010 tmp = Environment.envBackend_.getAccessibilityEnabled(); 2011 break; 2012 case 'colorMode': 2013 tmp = Environment.envBackend_.getColorMode(); 2014 break; 2015 case 'fontScale': 2016 tmp = Environment.envBackend_.getFontScale(); 2017 break; 2018 case 'fontWeightScale': 2019 tmp = Environment.envBackend_.getFontWeightScale().toFixed(2); 2020 break; 2021 case 'layoutDirection': 2022 tmp = Environment.envBackend_.getLayoutDirection(); 2023 break; 2024 case 'languageCode': 2025 tmp = Environment.envBackend_.getLanguageCode(); 2026 break; 2027 default: 2028 tmp = value; 2029 } 2030 if (!tmp && tmp !== 0) { 2031 tmp = value; 2032 } 2033 prop = AppStorage.setAndProp(key, tmp); 2034 if (!prop) { 2035 stateMgmtConsole.warn(`Environment: envProp '${key}': AppStorage setAndProp failed.`); 2036 return false; 2037 } 2038 this.props_.set(key, prop); 2039 2040 return true; 2041 } 2042 envProps(properties) { 2043 properties.forEach(property => { 2044 this.envProp(property.key, property.defaultValue); 2045 2046 }); 2047 } 2048 keys() { 2049 let result = []; 2050 const it = this.props_.keys(); 2051 let val = it.next(); 2052 while (!val.done) { 2053 result.push(val.value); 2054 val = it.next(); 2055 } 2056 return result; 2057 } 2058 onValueChanged(key, value) { 2059 let ok = AppStorage.set(key, value); 2060 if (ok) { 2061 2062 } 2063 else { 2064 stateMgmtConsole.warn(`Environment: onValueChanged: error changing ${key}! See results above.`); 2065 } 2066 } 2067 aboutToBeDeleted() { 2068 this.props_.forEach((val, key, map) => { 2069 val.aboutToBeDeleted(); 2070 AppStorage.delete(key); 2071 }); 2072 } 2073} 2074Environment.instance_ = undefined; 2075/* 2076 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 2077 * Licensed under the Apache License, Version 2.0 (the "License"); 2078 * you may not use this file except in compliance with the License. 2079 * You may obtain a copy of the License at 2080 * 2081 * http://www.apache.org/licenses/LICENSE-2.0 2082 * 2083 * Unless required by applicable law or agreed to in writing, software 2084 * distributed under the License is distributed on an "AS IS" BASIS, 2085 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2086 * See the License for the specific language governing permissions and 2087 * limitations under the License. 2088 */ 2089/* 2090 global function Repeat() 2091 returns an object that retains the state of Repeat instance between render calls 2092 exec attribute functions on this instance. 2093*/ 2094const Repeat = (arr, owningView) => { 2095 if (!owningView) { 2096 throw new Error("Transpilation error, Repeat lacks 2nd parameter owningView"); 2097 } 2098 return owningView.__mkRepeatAPI(arr); 2099}; 2100/* 2101 * Copyright (c) 2023 Huawei Device Co., Ltd. 2102 * Licensed under the Apache License, Version 2.0 (the "License"); 2103 * you may not use this file except in compliance with the License. 2104 * You may obtain a copy of the License at 2105 * 2106 * http://www.apache.org/licenses/LICENSE-2.0 2107 * 2108 * Unless required by applicable law or agreed to in writing, software 2109 * distributed under the License is distributed on an "AS IS" BASIS, 2110 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2111 * See the License for the specific language governing permissions and 2112 * limitations under the License. 2113 */ 2114/** 2115 * state mgmt library uses its own class for logging 2116* allows to remap separately from other use of aceConsole 2117* 2118* everything in this file is framework internal 2119*/ 2120var LogTag; 2121(function (LogTag) { 2122 LogTag[LogTag["STATE_MGMT"] = 0] = "STATE_MGMT"; 2123})(LogTag || (LogTag = {})); 2124class stateMgmtConsole { 2125 static log(...args) { 2126 aceConsole.log(LogTag.STATE_MGMT, ...args); 2127 } 2128 static debug(...args) { 2129 aceConsole.debug(LogTag.STATE_MGMT, ...args); 2130 } 2131 static info(...args) { 2132 aceConsole.info(LogTag.STATE_MGMT, ...args); 2133 } 2134 static warn(...args) { 2135 aceConsole.warn(LogTag.STATE_MGMT, ...args); 2136 } 2137 static error(...args) { 2138 aceConsole.error(LogTag.STATE_MGMT, ...args); 2139 } 2140 static propertyAccess(...args) { 2141 // enable for fine grain debugging variable observation 2142 // aceConsole.error(...args) 2143 } 2144 static applicationError(...args) { 2145 aceConsole.error(LogTag.STATE_MGMT, `FIX THIS APPLICATION ERROR: `, ...args); 2146 } 2147 static applicationWarn(...args) { 2148 aceConsole.warn(LogTag.STATE_MGMT, ...args); 2149 } 2150 static featureCombinationError(msg) { 2151 aceConsole.warn(LogTag.STATE_MGMT, msg); 2152 } 2153} 2154class stateMgmtTrace { 2155 static scopedTrace(codeBlock, arg1, ...args) { 2156 aceTrace.begin(arg1, ...args); 2157 let result = codeBlock(); 2158 aceTrace.end(); 2159 return result; 2160 } 2161} 2162class errorReport { 2163 static varValueCheckFailed(params) { 2164 let msg = `@Component '${params.customComponent}': Illegal variable value error with decorated variable ${params.variableDeco} '${params.variableName}': `; 2165 msg += `failed validation: '${params.expectedType}`; 2166 try { 2167 msg += `, attempt to assign value type: '${typeof params.value}'`; 2168 msg += `, value: '${JSON.stringify(params.value, null, 4)}'`; 2169 } 2170 catch (e) { } 2171 msg += '!'; 2172 stateMgmtConsole.applicationError(msg); 2173 throw new TypeError(msg); 2174 } 2175 static varObservationFailed(params) { 2176 let msg = `@Component '${params.customComponent}': decorated variable ${params.variableDeco} '${params.variableName}': `; 2177 msg += `its class is neither decorated with '@Observed' nor it is an instance of 'SubscribableAbstract'`; 2178 try { 2179 msg += `, attempt to assign value type: '${typeof params.value}'`; 2180 msg += `, value: '${JSON.stringify(params.value, null, 4)}'`; 2181 } 2182 catch (e) { } 2183 msg += '!'; 2184 throw new TypeError(msg); 2185 } 2186} 2187/* 2188 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 2189 * Licensed under the Apache License, Version 2.0 (the "License"); 2190 * you may not use this file except in compliance with the License. 2191 * You may obtain a copy of the License at 2192 * 2193 * http://www.apache.org/licenses/LICENSE-2.0 2194 * 2195 * Unless required by applicable law or agreed to in writing, software 2196 * distributed under the License is distributed on an "AS IS" BASIS, 2197 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2198 * See the License for the specific language governing permissions and 2199 * limitations under the License. 2200 */ 2201/** 2202* @Observed class decorator 2203* 2204* usage: 2205* @Observed class ClassA { ... } 2206* 2207* Causes every instance of decorated clss to be automatically wrapped inside an ObservedObject. 2208* 2209* Implemented by extending the decroaetd class by class named 'ObservableObjectClass'. 2210* 2211* It is permisstable to decorate the base and the extended class like thisNote: I 2212* @Observed class ClassA { ...} 2213* @Observed class ClassB extends ClassA { ... } 2214* and use 2215* a = new ClassA(); 2216* b = new ClassB(); 2217* Only one ES6 Proxy is added. 2218* 2219* 2220* Take note the decorator implementation extends the prototype chain. 2221* 2222* The prototype chain of a in above example is 2223* - ObservableObjectClass prototype 2224* - ClassA prototype 2225* - Object prototype 2226* 2227* Snd the prototype chain of b is 2228* - ObservableObjectClass prototype 2229* - ClassB prototype 2230* - ObservableObjectClass prototype 2231* - ClassA prototype 2232* - Object prototype 2233* 2234* The @Observed decorator is public, part of the SDK, starting from API 9. 2235* 2236*/ 2237// define just once to get just one Symbol 2238const __IS_OBSERVED_PROXIED = Symbol('_____is_observed_proxied__'); 2239function Observed(BaseClass) { 2240 2241 // prevent use of V3 @track inside V2 @Observed class 2242 if (BaseClass.prototype && Reflect.has(BaseClass.prototype, ObserveV2.SYMBOL_REFS)) { 2243 const error = `'@Observed class ${BaseClass === null || BaseClass === void 0 ? void 0 : BaseClass.name}': invalid use of V3 @track decorator inside V2 @Observed class. Need to fix class definition to use @Track.`; 2244 stateMgmtConsole.error(error); 2245 throw new Error(error); 2246 } 2247 return class extends BaseClass { 2248 constructor(...args) { 2249 super(...args); 2250 2251 ConfigureStateMgmt.instance.usingPUObservedTrack(`@Observed`, BaseClass.name); 2252 let isProxied = Reflect.has(this, __IS_OBSERVED_PROXIED); 2253 Object.defineProperty(this, __IS_OBSERVED_PROXIED, { 2254 value: true, 2255 enumerable: false, 2256 configurable: false, 2257 writable: false 2258 }); 2259 if (isProxied) { 2260 2261 return this; 2262 } 2263 else { 2264 2265 return ObservedObject.createNewInternal(this, undefined); 2266 } 2267 } 2268 }; 2269} 2270class SubscribableHandler { 2271 constructor(owningProperty) { 2272 this.owningProperties_ = new Set(); 2273 if (owningProperty) { 2274 this.addOwningProperty(owningProperty); 2275 } 2276 2277 } 2278 isPropertyTracked(obj, property) { 2279 return Reflect.has(obj, `___TRACKED_${property}`) || 2280 property === TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY || 2281 property === TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY; 2282 } 2283 addOwningProperty(subscriber) { 2284 if (subscriber) { 2285 2286 this.owningProperties_.add(subscriber.id__()); 2287 } 2288 else { 2289 stateMgmtConsole.warn(`SubscribableHandler: addOwningProperty: undefined subscriber.`); 2290 } 2291 } 2292 /* 2293 the inverse function of createOneWaySync or createTwoWaySync 2294 */ 2295 removeOwningProperty(property) { 2296 return this.removeOwningPropertyById(property.id__()); 2297 } 2298 removeOwningPropertyById(subscriberId) { 2299 2300 this.owningProperties_.delete(subscriberId); 2301 } 2302 notifyObjectPropertyHasChanged(propName, newValue) { 2303 2304 this.owningProperties_.forEach((subscribedId) => { 2305 const owningProperty = SubscriberManager.Find(subscribedId); 2306 if (!owningProperty) { 2307 stateMgmtConsole.warn(`SubscribableHandler: notifyObjectPropertyHasChanged: unknown subscriber.'${subscribedId}' error!.`); 2308 return; 2309 } 2310 // PU code path 2311 if ('onTrackedObjectPropertyCompatModeHasChangedPU' in owningProperty) { 2312 owningProperty.onTrackedObjectPropertyCompatModeHasChangedPU(this, propName); 2313 return; 2314 } 2315 // FU code path 2316 if ('hasChanged' in owningProperty) { 2317 owningProperty.hasChanged(newValue); 2318 } 2319 if ('propertyHasChanged' in owningProperty) { 2320 owningProperty.propertyHasChanged(propName); 2321 } 2322 }); 2323 } 2324 notifyTrackedObjectPropertyHasChanged(propName) { 2325 2326 this.owningProperties_.forEach((subscribedId) => { 2327 const owningProperty = SubscriberManager.Find(subscribedId); 2328 if (owningProperty && 'onTrackedObjectPropertyHasChangedPU' in owningProperty) { 2329 // PU code path with observed object property change tracking optimization 2330 owningProperty.onTrackedObjectPropertyHasChangedPU(this, propName); 2331 } 2332 else { 2333 stateMgmtConsole.warn(`SubscribableHandler: notifyTrackedObjectPropertyHasChanged: subscriber.'${subscribedId}' lacks method 'trackedObjectPropertyHasChangedPU' internal error!.`); 2334 } 2335 }); 2336 // no need to support FU code path when app uses @Track 2337 } 2338 has(target, property) { 2339 2340 return (property === ObservedObject.__IS_OBSERVED_OBJECT) ? true : Reflect.has(target, property); 2341 } 2342 get(target, property, receiver) { 2343 switch (property) { 2344 case ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT: 2345 return target; 2346 break; 2347 case SubscribableHandler.COUNT_SUBSCRIBERS: 2348 return this.owningProperties_.size; 2349 break; 2350 case ObserveV2.SYMBOL_REFS: 2351 case ObserveV2.V2_DECO_META: 2352 case ObserveV2.SYMBOL_MAKE_OBSERVED: 2353 // return result unmonitored 2354 return Reflect.get(target, property, receiver); 2355 break; 2356 default: 2357 const result = Reflect.get(target, property, receiver); 2358 let propertyStr = String(property); 2359 if (this.readCbFunc_ && typeof result !== 'function' && this.obSelf_ !== undefined) { 2360 let isTracked = this.isPropertyTracked(target, propertyStr); 2361 2362 this.readCbFunc_.call(this.obSelf_, receiver, propertyStr, isTracked); 2363 } 2364 else { 2365 // result is function or in compatibility mode (in compat mode cbFunc will never be set) 2366 2367 } 2368 return result; 2369 break; 2370 } 2371 } 2372 set(target, property, newValue) { 2373 switch (property) { 2374 case SubscribableHandler.SUBSCRIBE: 2375 // assignment obsObj[SubscribableHandler.SUBSCRIBE] = subscriber 2376 this.addOwningProperty(newValue); 2377 return true; 2378 break; 2379 case SubscribableHandler.UNSUBSCRIBE: 2380 // assignment obsObj[SubscribableHandler.UNSUBSCRIBE] = subscriber 2381 this.removeOwningProperty(newValue); 2382 return true; 2383 break; 2384 case SubscribableHandler.SET_ONREAD_CB: 2385 // assignment obsObj[SubscribableHandler.SET_ONREAD_CB] = readCallbackFunc 2386 2387 this.readCbFunc_ = TrackedObject.isCompatibilityMode(target) ? undefined : newValue; 2388 return true; 2389 break; 2390 case SubscribableHandler.RAW_THIS: 2391 this.obSelf_ = TrackedObject.isCompatibilityMode(target) ? undefined : newValue; 2392 return true; 2393 break; 2394 default: 2395 // this is added for stability test: Reflect.get target is not object 2396 try { 2397 if (Reflect.get(target, property) === newValue) { 2398 return true; 2399 } 2400 } 2401 catch (error) { 2402 ArkTools.print('SubscribableHandler: set', target); 2403 stateMgmtConsole.error(`An error occurred in SubscribableHandler set, target type is: ${typeof target}, ${error.message}`); 2404 throw error; 2405 } 2406 Reflect.set(target, property, newValue); 2407 const propString = String(property); 2408 if (TrackedObject.isCompatibilityMode(target)) { 2409 2410 this.notifyObjectPropertyHasChanged(propString, newValue); 2411 } 2412 else { 2413 if (this.isPropertyTracked(target, propString)) { 2414 2415 this.notifyTrackedObjectPropertyHasChanged(propString); 2416 } 2417 else { 2418 2419 } 2420 } 2421 return true; 2422 break; 2423 } 2424 // unreachable 2425 return false; 2426 } 2427} 2428SubscribableHandler.SUBSCRIBE = Symbol('_____subscribe__'); 2429SubscribableHandler.UNSUBSCRIBE = Symbol('_____unsubscribe__'); 2430SubscribableHandler.COUNT_SUBSCRIBERS = Symbol('____count_subscribers__'); 2431SubscribableHandler.SET_ONREAD_CB = Symbol('_____set_onread_cb__'); 2432SubscribableHandler.RAW_THIS = Symbol('_____raw_this'); 2433class SubscribableMapSetHandler extends SubscribableHandler { 2434 constructor(owningProperty) { 2435 super(owningProperty); 2436 // In-place Map/Set modification functions 2437 this.mutatingFunctions = new Set([ 2438 /*Map functions*/ 2439 'set', 'clear', 'delete', 2440 /*Set functions*/ 2441 'add', 'clear', 'delete', 2442 ]); 2443 this.proxiedFunctions = new Set([ 2444 /*Map functions*/ 2445 'set', 2446 /*Set functions*/ 2447 'add' 2448 ]); 2449 } 2450 /** 2451 * Get trap for Map/Set type proxy 2452 * Functions that modify Map/Set in-place are intercepted and replaced with a function 2453 * that executes the original function and notifies the handler of a change. 2454 * @param target Original Map/Set object 2455 * @param property 2456 * @param receiver Proxied Map/Set object 2457 * @returns 2458 */ 2459 get(target, property, receiver) { 2460 if (property === ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT) { 2461 return target; 2462 } 2463 //receiver will fail for internal slot methods of Set and Map 2464 //So assign the target as receiver in this case. 2465 if (property === Symbol.iterator || property === 'size') { 2466 receiver = target; 2467 } 2468 let ret = super.get(target, property, receiver); 2469 if (ret && typeof ret === 'function') { 2470 const self = this; 2471 return function () { 2472 // execute original function with given arguments 2473 const result = ret.apply(target, arguments); 2474 if (self.mutatingFunctions.has(property)) { 2475 self.notifyObjectPropertyHasChanged(property, target); 2476 } 2477 // Only calls to inserting items can be chained, so returning the 'proxiedObject' 2478 // ensures that when chain calls also 2nd function call operates on the proxied object. 2479 // Otherwise return the original result of the function. 2480 return self.proxiedFunctions.has(property) ? receiver : result; 2481 }.bind(receiver); 2482 } 2483 return ret; 2484 } 2485} 2486class SubscribableDateHandler extends SubscribableHandler { 2487 constructor(owningProperty) { 2488 super(owningProperty); 2489 this.dateSetFunctions = new Set(['setFullYear', 'setMonth', 'setDate', 'setHours', 'setMinutes', 'setSeconds', 2490 'setMilliseconds', 'setTime', 'setUTCFullYear', 'setUTCMonth', 'setUTCDate', 'setUTCHours', 'setUTCMinutes', 2491 'setUTCSeconds', 'setUTCMilliseconds']); 2492 } 2493 /** 2494 * Get trap for Date type proxy 2495 * Functions that modify Date in-place are intercepted and replaced with a function 2496 * that executes the original function and notifies the handler of a change. 2497 * @param target Original Date object 2498 * @param property 2499 * @returns 2500 */ 2501 get(target, property) { 2502 let ret = super.get(target, property); 2503 if (typeof ret === 'function') { 2504 if (this.dateSetFunctions.has(property)) { 2505 const self = this; 2506 return function () { 2507 // execute original function with given arguments 2508 let result = ret.apply(this, arguments); 2509 self.notifyObjectPropertyHasChanged(property.toString(), this); 2510 return result; 2511 // bind 'this' to target inside the function 2512 }.bind(target); 2513 } 2514 return ret.bind(target); 2515 } 2516 return ret; 2517 } 2518} 2519class SubscribableArrayHandler extends SubscribableHandler { 2520 constructor(owningProperty) { 2521 super(owningProperty); 2522 // In-place array modification functions 2523 this.mutatingFunctions = new Set(['splice', 'copyWithin', 'fill', 'reverse', 'sort']); 2524 // 'splice' and 'pop' self modifies the array, returns deleted array items 2525 // means, alike other self-modifying functions, splice does not return the array itself. 2526 this.specialFunctions = new Set(['splice', 'pop']); 2527 } 2528 /** 2529 * Get trap for Array type proxy 2530 * Functions that modify Array in-place are intercepted and replaced with a function 2531 * that executes the original function and notifies the handler of a change. 2532 * @param target Original Array object 2533 * @param property 2534 * @param receiver Proxied Array object 2535 * @returns 2536 */ 2537 get(target, property, receiver) { 2538 if (property === ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT) { 2539 return target; 2540 } 2541 let ret = super.get(target, property, receiver); 2542 if (ret && typeof ret === 'function') { 2543 const self = this; 2544 const prop = property.toString(); 2545 if (self.mutatingFunctions.has(prop)) { 2546 return function () { 2547 const result = ret.apply(target, arguments); 2548 // prop is the function name here 2549 // and result is the function return value 2550 // function modifies none or more properties 2551 self.notifyObjectPropertyHasChanged(prop, self.specialFunctions.has(prop) ? target : result); 2552 // returning the 'receiver(proxied object)' ensures that when chain calls also 2nd function call 2553 // operates on the proxied object. 2554 return self.specialFunctions.has(prop) ? result : receiver; 2555 }.bind(receiver); 2556 } 2557 // binding the proxiedObject ensures that modifying functions like push() operate on the 2558 // proxied array and each array change is notified. 2559 return ret.bind(receiver); 2560 } 2561 return ret; 2562 } 2563} 2564class ExtendableProxy { 2565 constructor(obj, handler) { 2566 return new Proxy(obj, handler); 2567 } 2568} 2569class ObservedObject extends ExtendableProxy { 2570 /** 2571 * To create a new ObservableObject use CreateNew function 2572 * 2573 * constructor create a new ObservableObject and subscribe its owner to propertyHasChanged 2574 * notifications 2575 * @param obj raw Object, if obj is a ObservableOject throws an error 2576 * @param objectOwner 2577 */ 2578 constructor(obj, handler, objectOwningProperty) { 2579 super(obj, handler); 2580 if (ObservedObject.IsObservedObject(obj)) { 2581 stateMgmtConsole.error('ObservableOject constructor: INTERNAL ERROR: after jsObj is observedObject already'); 2582 } 2583 if (objectOwningProperty) { 2584 this[SubscribableHandler.SUBSCRIBE] = objectOwningProperty; 2585 } 2586 } // end of constructor 2587 /** 2588 * Factory function for ObservedObjects / 2589 * wrapping of objects for proxying 2590 * 2591 * @param rawObject unproxied Object or ObservedObject 2592 * @param objOwner owner of this Object to sign uop for propertyChange 2593 * notifications 2594 * @returns the rawObject if object is already an ObservedObject, 2595 * otherwise the newly created ObservedObject 2596 */ 2597 static createNew(rawObject, owningProperty) { 2598 if (rawObject === null || rawObject === undefined) { 2599 stateMgmtConsole.error(`ObservedObject.CreateNew, input object must not be null or undefined.`); 2600 return rawObject; 2601 } 2602 if (ObservedObject.IsObservedObject(rawObject)) { 2603 ObservedObject.addOwningProperty(rawObject, owningProperty); 2604 return rawObject; 2605 } 2606 return ObservedObject.createNewInternal(rawObject, owningProperty); 2607 } 2608 static createNewInternal(rawObject, owningProperty) { 2609 let proxiedObject; 2610 if (rawObject instanceof Map || rawObject instanceof Set) { 2611 proxiedObject = new ObservedObject(rawObject, new SubscribableMapSetHandler(owningProperty), owningProperty); 2612 } 2613 else if (rawObject instanceof Date) { 2614 proxiedObject = new ObservedObject(rawObject, new SubscribableDateHandler(owningProperty), owningProperty); 2615 } 2616 else if (Array.isArray(rawObject)) { 2617 proxiedObject = new ObservedObject(rawObject, new SubscribableArrayHandler(owningProperty), owningProperty); 2618 } 2619 else { 2620 proxiedObject = new ObservedObject(rawObject, new SubscribableHandler(owningProperty), owningProperty); 2621 } 2622 return proxiedObject; 2623 } 2624 /* 2625 Return the unproxied object 'inside' the ObservedObject / the ES6 Proxy 2626 no set observation, no notification of changes! 2627 Use with caution, do not store any references 2628 */ 2629 static GetRawObject(obj) { 2630 return !ObservedObject.IsObservedObject(obj) ? obj : obj[ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT]; 2631 } 2632 /** 2633 * 2634 * @param obj anything 2635 * @returns true if the parameter is an Object wrpped with a ObservedObject 2636 * Note: Since ES6 Proying is transparent, 'instance of' will not work. Use 2637 * this static function instead. 2638 */ 2639 static IsObservedObject(obj) { 2640 return (obj && (typeof obj === 'object') && Reflect.has(obj, ObservedObject.__IS_OBSERVED_OBJECT)); 2641 } 2642 /** 2643 * add a subscriber to given ObservedObject 2644 * due to the proxy nature this static method approach needs to be used instead of a member 2645 * function 2646 * @param obj 2647 * @param subscriber 2648 * @returns false if given object is not an ObservedObject 2649 */ 2650 static addOwningProperty(obj, subscriber) { 2651 if (!ObservedObject.IsObservedObject(obj) || !subscriber) { 2652 return false; 2653 } 2654 obj[SubscribableHandler.SUBSCRIBE] = subscriber; 2655 return true; 2656 } 2657 /** 2658 * remove a subscriber to given ObservedObject 2659 * due to the proxy nature this static method approach needs to be used instead of a member 2660 * function 2661 * @param obj 2662 * @param subscriber 2663 * @returns false if given object is not an ObservedObject 2664 */ 2665 static removeOwningProperty(obj, subscriber) { 2666 if (!ObservedObject.IsObservedObject(obj)) { 2667 return false; 2668 } 2669 obj[SubscribableHandler.UNSUBSCRIBE] = subscriber; 2670 return true; 2671 } 2672 /** 2673 * 2674 * @param obj any Object 2675 * @returns return number of subscribers to the given ObservedObject 2676 * or false if given object is not an ObservedObject 2677 */ 2678 static countSubscribers(obj) { 2679 return ObservedObject.IsObservedObject(obj) ? obj[SubscribableHandler.COUNT_SUBSCRIBERS] : false; 2680 } 2681 /* 2682 set or unset callback function to be called when a property has been called 2683 */ 2684 static registerPropertyReadCb(obj, readPropCb, obSelf) { 2685 if (!ObservedObject.IsObservedObject(obj)) { 2686 return false; 2687 } 2688 obj[SubscribableHandler.SET_ONREAD_CB] = readPropCb; 2689 obj[SubscribableHandler.RAW_THIS] = obSelf; 2690 return true; 2691 } 2692 static unregisterPropertyReadCb(obj) { 2693 if (!ObservedObject.IsObservedObject(obj)) { 2694 return false; 2695 } 2696 obj[SubscribableHandler.SET_ONREAD_CB] = undefined; 2697 obj[SubscribableHandler.RAW_THIS] = undefined; 2698 return true; 2699 } 2700 /** 2701 * Utility function for debugging the prototype chain of given Object 2702 * The given object can be any Object, it is not required to be an ObservedObject 2703 * @param object 2704 * @returns multi-line string containing info about the prototype chain 2705 * on class in class hiararchy per line 2706 */ 2707 static tracePrototypeChainOfObject(object) { 2708 let proto = Object.getPrototypeOf(object); 2709 let result = ''; 2710 let sepa = ''; 2711 while (proto) { 2712 result += `${sepa}${ObservedObject.tracePrototype(proto)}`; 2713 proto = Object.getPrototypeOf(proto); 2714 sepa = ',\n'; 2715 } 2716 return result; 2717 } 2718 /** 2719 * Utility function for debugging all functions of given Prototype. 2720 * @returns string containing containing names of all functions and members of given Prototype 2721 */ 2722 static tracePrototype(proto) { 2723 if (!proto) { 2724 return ''; 2725 } 2726 let result = `${proto.constructor && proto.constructor.name ? proto.constructor.name : '<no class>'}: `; 2727 let sepa = ''; 2728 for (let name of Object.getOwnPropertyNames(proto)) { 2729 result += `${sepa}${name}`; 2730 sepa = ', '; 2731 } 2732 ; 2733 return result; 2734 } 2735 /** 2736 * @Observed decorator extends the decorated class. This function returns the prototype of the decorated class 2737 * @param proto 2738 * @returns prototype of the @Observed decorated class or 'proto' parameter if not @Observed decorated 2739 */ 2740 static getPrototypeOfObservedClass(proto) { 2741 return (proto.constructor && proto.constructor.name === 'ObservedClass') 2742 ? Object.getPrototypeOf(proto.constructor.prototype) 2743 : proto; 2744 } 2745} 2746ObservedObject.__IS_OBSERVED_OBJECT = Symbol('_____is_observed_object__'); 2747ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT = Symbol('_____raw_object__'); 2748/* 2749 * Copyright (c) 2021 Huawei Device Co., Ltd. 2750 * Licensed under the Apache License, Version 2.0 (the "License"); 2751 * you may not use this file except in compliance with the License. 2752 * You may obtain a copy of the License at 2753 * 2754 * http://www.apache.org/licenses/LICENSE-2.0 2755 * 2756 * Unless required by applicable law or agreed to in writing, software 2757 * distributed under the License is distributed on an "AS IS" BASIS, 2758 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2759 * See the License for the specific language governing permissions and 2760 * limitations under the License. 2761 */ 2762/* 2763 manage subscriptions to a property 2764 managing the property is left to sub 2765 classes 2766 Extended by ObservedProperty, SyncedPropertyOneWay 2767 and SyncedPropertyTwoWay 2768*/ 2769class ObservedPropertyAbstract extends SubscribedAbstractProperty { 2770 constructor(subscribeMe, info) { 2771 super(); 2772 this.subscribers_ = new Set(); 2773 this.id_ = SubscriberManager.MakeStateVariableId(); 2774 SubscriberManager.Add(this); 2775 if (subscribeMe) { 2776 this.subscribers_.add(subscribeMe.id__()); 2777 } 2778 if (info) { 2779 this.info_ = info; 2780 } 2781 } 2782 aboutToBeDeleted() { 2783 SubscriberManager.Delete(this.id__()); 2784 } 2785 id__() { 2786 return this.id_; 2787 } 2788 info() { 2789 return this.info_; 2790 } 2791 setInfo(propName) { 2792 if (propName && propName !== '') { 2793 this.info_ = propName; 2794 } 2795 } 2796 // Partial Update "*PU" classes will overwrite 2797 getUnmonitored() { 2798 return this.get(); 2799 } 2800 // update the element id for recycle custom component 2801 updateElmtId(oldElmtId, newElmtId) { 2802 if (this.subscribers_.has(oldElmtId)) { 2803 this.subscribers_.delete(oldElmtId); 2804 this.subscribers_.add(newElmtId); 2805 } 2806 } 2807 // Method name is used to check object is of type ObservedPropertyAbstract 2808 // Do NOT override in derived classed, use addSubscriber 2809 subscribeMe(subscriber) { 2810 2811 this.subscribers_.add(subscriber.id__()); 2812 } 2813 /* 2814 the inverse function of createOneWaySync or createTwoWaySync 2815 Do NOT override in derived classed, use removeSubscriber 2816 */ 2817 unlinkSuscriber(subscriberId) { 2818 this.subscribers_.delete(subscriberId); 2819 } 2820 /* 2821 Virtualized version of the subscription mechanism - add subscriber 2822 */ 2823 addSubscriber(subscriber) { 2824 if (subscriber) { 2825 this.subscribeMe(subscriber); 2826 } 2827 } 2828 /* 2829 Virtualized version of the subscription mechanism - remove subscriber 2830 */ 2831 removeSubscriber(subscriber, id) { 2832 if (id) { 2833 this.unlinkSuscriber(id); 2834 } 2835 else if (subscriber) { 2836 this.unlinkSuscriber(subscriber.id__()); 2837 } 2838 } 2839 // FU code path callback 2840 notifyHasChanged(newValue) { 2841 2842 2843 this.subscribers_.forEach((subscribedId) => { 2844 let subscriber = SubscriberManager.Find(subscribedId); 2845 if (subscriber) { 2846 // FU code path 2847 if ('hasChanged' in subscriber) { 2848 subscriber.hasChanged(newValue); 2849 } 2850 if ('propertyHasChanged' in subscriber) { 2851 subscriber.propertyHasChanged(this.info_); 2852 } 2853 } 2854 else { 2855 stateMgmtConsole.warn(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || 'unknown'}']: notifyHasChanged: unknown subscriber ID '${subscribedId}' error!`); 2856 } 2857 }); 2858 2859 } 2860 notifyPropertyRead() { 2861 2862 2863 this.subscribers_.forEach((subscribedId) => { 2864 var subscriber = SubscriberManager.Find(subscribedId); 2865 if (subscriber) { 2866 if ('propertyRead' in subscriber) { 2867 subscriber.propertyRead(this.info_); 2868 } 2869 } 2870 }); 2871 2872 } 2873 /* 2874 return numebr of subscribers to this property 2875 mostly useful for unit testin 2876 */ 2877 numberOfSubscrbers() { 2878 return this.subscribers_.size; 2879 } 2880 /** 2881 * provide a factory function that creates a SynchedPropertyXXXX of choice 2882 * that uses 'this' as source 2883 * @param factoryFunc 2884 * @returns 2885 */ 2886 createSync(factoryFunc) { 2887 return factoryFunc(this); 2888 } 2889 /** 2890 * depreciated SDK function, not used anywhere by the framework 2891 */ 2892 createTwoWaySync(subscribeMe, info) { 2893 stateMgmtConsole.warn("Using depreciated method 'createTwoWaySync'!"); 2894 return this.createLink(subscribeMe, info); 2895 } 2896 /** 2897 * depreciated SDK function, not used anywhere by the framework 2898 */ 2899 createOneWaySync(subscribeMe, info) { 2900 stateMgmtConsole.warn("Using depreciated method 'createOneWaySync' !"); 2901 return this.createProp(subscribeMe, info); 2902 } 2903 /** 2904 * factory function for concrete 'object' or 'simple' ObservedProperty object 2905 * depending if value is Class object 2906 * or simple type (boolean | number | string) 2907 * @param value 2908 * @param owningView 2909 * @param thisPropertyName 2910 * @returns either 2911 */ 2912 static CreateObservedObject(value, owningView, thisPropertyName) { 2913 return (typeof value === 'object') ? 2914 new ObservedPropertyObject(value, owningView, thisPropertyName) 2915 : new ObservedPropertySimple(value, owningView, thisPropertyName); 2916 } 2917} 2918/* 2919 * Copyright (c) 2024 Huawei Device Co., Ltd. 2920 * Licensed under the Apache License, Version 2.0 (the "License"); 2921 * you may not use this file except in compliance with the License. 2922 * You may obtain a copy of the License at 2923 * 2924 * http://www.apache.org/licenses/LICENSE-2.0 2925 * 2926 * Unless required by applicable law or agreed to in writing, software 2927 * distributed under the License is distributed on an "AS IS" BASIS, 2928 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2929 * See the License for the specific language governing permissions and 2930 * limitations under the License. 2931 */ 2932class CustomDialogController extends NativeCustomDialogController { 2933 constructor(arg, view) { 2934 super(arg, view); 2935 this.arg_ = arg; 2936 this.view_ = view; 2937 } 2938} 2939/* 2940 * Copyright (c) 2024 Huawei Device Co., Ltd. 2941 * Licensed under the Apache License, Version 2.0 (the "License"); 2942 * you may not use this file except in compliance with the License. 2943 * You may obtain a copy of the License at 2944 * 2945 * http://www.apache.org/licenses/LICENSE-2.0 2946 * 2947 * Unless required by applicable law or agreed to in writing, software 2948 * distributed under the License is distributed on an "AS IS" BASIS, 2949 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2950 * See the License for the specific language governing permissions and 2951 * limitations under the License. 2952 * 2953 */ 2954class Utils { 2955 static getApiVersion() { 2956 return typeof ViewStackProcessor["getApiVersion"] === "function" 2957 ? ViewStackProcessor["getApiVersion"]() 2958 : undefined; 2959 } 2960 static isApiVersionEQAbove(target) { 2961 let version = Utils.getApiVersion(); 2962 if (version == null) { 2963 return false; 2964 } 2965 if (typeof version === "number") { 2966 version = version % 1000; 2967 } 2968 return version >= target; 2969 } 2970} 2971/* 2972 * Copyright (c) 2024 Huawei Device Co., Ltd. 2973 * Licensed under the Apache License, Version 2.0 (the "License"); 2974 * you may not use this file except in compliance with the License. 2975 * You may obtain a copy of the License at 2976 * 2977 * http://www.apache.org/licenses/LICENSE-2.0 2978 * 2979 * Unless required by applicable law or agreed to in writing, software 2980 * distributed under the License is distributed on an "AS IS" BASIS, 2981 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2982 * See the License for the specific language governing permissions and 2983 * limitations under the License. 2984 */ 2985class stateMgmtDFX { 2986 static getObservedPropertyInfo(observedProp, isProfiler, changedTrackPropertyName) { 2987 return { 2988 decorator: observedProp.debugInfoDecorator(), propertyName: observedProp.info(), id: observedProp.id__(), 2989 changedTrackPropertyName: changedTrackPropertyName, 2990 value: stateMgmtDFX.getRawValue(observedProp), 2991 inRenderingElementId: stateMgmtDFX.inRenderingElementId.length === 0 ? 2992 -1 : stateMgmtDFX.inRenderingElementId[stateMgmtDFX.inRenderingElementId.length - 1], 2993 dependentElementIds: observedProp.dumpDependentElmtIdsObj(typeof observedProp.getUnmonitored() === 'object' ? 2994 !TrackedObject.isCompatibilityMode(observedProp.getUnmonitored()) : false, isProfiler), 2995 owningView: observedProp.getOwningView(), 2996 length: stateMgmtDFX.getRawValueLength(observedProp), 2997 syncPeers: observedProp.dumpSyncPeers(isProfiler, changedTrackPropertyName) 2998 }; 2999 } 3000 static getType(item) { 3001 try { 3002 return Object.prototype.toString.call(item); 3003 } 3004 catch (e) { 3005 stateMgmtConsole.warn(`Cannot get the type of current value, error message is: ${e.message}`); 3006 return 'unknown type'; 3007 } 3008 } 3009 /** 3010 * Dump 10 items at most. 3011 * If length > 10, the output will be the first 7 and last 3 items. 3012 * eg: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 3013 * output: [0, 1, 2, 3, 4, 5, 6, '...', 9, 10, 11] 3014 * 3015 */ 3016 static dumpItems(arr) { 3017 let dumpArr = arr.slice(0, stateMgmtDFX.DUMP_MAX_LENGTH); 3018 if (arr.length > stateMgmtDFX.DUMP_MAX_LENGTH) { 3019 dumpArr.splice(stateMgmtDFX.DUMP_MAX_LENGTH - stateMgmtDFX.DUMP_LAST_LENGTH, stateMgmtDFX.DUMP_LAST_LENGTH, '...', ...arr.slice(-stateMgmtDFX.DUMP_LAST_LENGTH)); 3020 } 3021 return dumpArr.map(item => typeof item === 'object' ? this.getType(item) : item); 3022 } 3023 static dumpMap(map) { 3024 let dumpKey = this.dumpItems(Array.from(map.keys())); 3025 let dumpValue = this.dumpItems(Array.from(map.values())); 3026 return dumpKey.map((item, index) => [item, dumpValue[index]]); 3027 } 3028 static dumpObjectProperty(value) { 3029 let tempObj = {}; 3030 try { 3031 let properties = Object.getOwnPropertyNames(value); 3032 properties 3033 .slice(0, stateMgmtDFX.DUMP_MAX_PROPERTY_COUNT) 3034 .forEach((varName) => { 3035 const propertyValue = Reflect.get(value, varName); 3036 tempObj[varName] = typeof propertyValue === 'object' ? this.getType(propertyValue) : propertyValue; 3037 }); 3038 if (properties.length > stateMgmtDFX.DUMP_MAX_PROPERTY_COUNT) { 3039 tempObj['...'] = '...'; 3040 } 3041 } 3042 catch (e) { 3043 stateMgmtConsole.warn(`can not dump Obj, error msg ${e.message}`); 3044 return 'unknown type'; 3045 } 3046 return tempObj; 3047 } 3048 static getRawValue(observedProp) { 3049 let wrappedValue = observedProp.getUnmonitored(); 3050 if (typeof wrappedValue !== 'object') { 3051 return wrappedValue; 3052 } 3053 let rawObject = ObservedObject.GetRawObject(wrappedValue); 3054 if (rawObject instanceof Map) { 3055 return stateMgmtDFX.dumpMap(rawObject); 3056 } 3057 else if (rawObject instanceof Set) { 3058 return stateMgmtDFX.dumpItems(Array.from(rawObject.values())); 3059 } 3060 else if (rawObject instanceof Array) { 3061 return stateMgmtDFX.dumpItems(Array.from(rawObject)); 3062 } 3063 else if (rawObject instanceof Date) { 3064 return rawObject; 3065 } 3066 else { 3067 return stateMgmtDFX.dumpObjectProperty(rawObject); 3068 } 3069 } 3070 static getRawValueLength(observedProp) { 3071 let wrappedValue = observedProp.getUnmonitored(); 3072 if (typeof wrappedValue !== 'object') { 3073 return -1; 3074 } 3075 let rawObject = ObservedObject.GetRawObject(wrappedValue); 3076 if (rawObject instanceof Map || rawObject instanceof Set) { 3077 return rawObject.size; 3078 } 3079 else if (rawObject instanceof Array) { 3080 return rawObject.length; 3081 } 3082 try { 3083 return Object.getOwnPropertyNames(rawObject).length; 3084 } 3085 catch (e) { 3086 return -1; 3087 } 3088 } 3089} 3090// enable profile 3091stateMgmtDFX.enableProfiler = false; 3092stateMgmtDFX.inRenderingElementId = new Array(); 3093stateMgmtDFX.DUMP_MAX_PROPERTY_COUNT = 50; 3094stateMgmtDFX.DUMP_MAX_LENGTH = 10; 3095stateMgmtDFX.DUMP_LAST_LENGTH = 3; 3096function setProfilerStatus(profilerStatus) { 3097 stateMgmtConsole.warn(`${profilerStatus ? `start` : `stop`} stateMgmt Profiler`); 3098 stateMgmtDFX.enableProfiler = profilerStatus; 3099} 3100class DumpInfo { 3101 constructor() { 3102 this.observedPropertiesInfo = []; 3103 } 3104} 3105/* 3106 * Copyright (c) 2021 Huawei Device Co., Ltd. 3107 * Licensed under the Apache License, Version 2.0 (the "License"); 3108 * you may not use this file except in compliance with the License. 3109 * You may obtain a copy of the License at 3110 * 3111 * http://www.apache.org/licenses/LICENSE-2.0 3112 * 3113 * Unless required by applicable law or agreed to in writing, software 3114 * distributed under the License is distributed on an "AS IS" BASIS, 3115 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3116 * See the License for the specific language governing permissions and 3117 * limitations under the License. 3118 */ 3119/** 3120 * ObservedPropertyObjectAbstract 3121 * 3122 * all definitions in this file are framework internal 3123 * 3124 * common base class of ObservedPropertyObject and 3125 * SyncedObjectPropertyTwoWay 3126 * adds the createObjectLink to the ObservedPropertyAbstract base 3127 */ 3128class ObservedPropertyObjectAbstract extends ObservedPropertyAbstract { 3129 constructor(owningView, thisPropertyName) { 3130 super(owningView, thisPropertyName); 3131 } 3132} 3133/* 3134 * Copyright (c) 2021 Huawei Device Co., Ltd. 3135 * Licensed under the Apache License, Version 2.0 (the "License"); 3136 * you may not use this file except in compliance with the License. 3137 * You may obtain a copy of the License at 3138 * 3139 * http://www.apache.org/licenses/LICENSE-2.0 3140 * 3141 * Unless required by applicable law or agreed to in writing, software 3142 * distributed under the License is distributed on an "AS IS" BASIS, 3143 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3144 * See the License for the specific language governing permissions and 3145 * limitations under the License. 3146 */ 3147/** 3148 * 3149 * ObservedPropertySimpleAbstract 3150 * 3151 * all definitions in this file are framework internal 3152 */ 3153class ObservedPropertySimpleAbstract extends ObservedPropertyAbstract { 3154 constructor(owningView, propertyName) { 3155 super(owningView, propertyName); 3156 } 3157} 3158/* 3159 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3160 * Licensed under the Apache License, Version 2.0 (the "License"); 3161 * you may not use this file except in compliance with the License. 3162 * You may obtain a copy of the License at 3163 * 3164 * http://www.apache.org/licenses/LICENSE-2.0 3165 * 3166 * Unless required by applicable law or agreed to in writing, software 3167 * distributed under the License is distributed on an "AS IS" BASIS, 3168 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3169 * See the License for the specific language governing permissions and 3170 * limitations under the License. 3171 */ 3172/** 3173 * ObservedPropertyObject 3174 * 3175 * all definitions in this file are framework internal 3176 * 3177 * class that holds an actual property value of type T 3178 * uses its base class to manage subscribers to this 3179 * property. 3180*/ 3181class ObservedPropertyObject extends ObservedPropertyObjectAbstract { 3182 constructor(value, owningView, propertyName) { 3183 super(owningView, propertyName); 3184 this.setValueInternal(value); 3185 } 3186 aboutToBeDeleted(unsubscribeMe) { 3187 this.unsubscribeFromOwningProperty(); 3188 if (unsubscribeMe) { 3189 this.unlinkSuscriber(unsubscribeMe.id__()); 3190 } 3191 super.aboutToBeDeleted(); 3192 } 3193 // notification from ObservedObject value one of its 3194 // props has chnaged. Implies the ObservedProperty has changed 3195 // Note: this function gets called when in this case: 3196 // thisProp.aObsObj.aProp = 47 a object prop gets changed 3197 // It is NOT called when 3198 // thisProp.aObsObj = new ClassA 3199 hasChanged(newValue) { 3200 3201 this.notifyHasChanged(this.wrappedValue_); 3202 } 3203 unsubscribeFromOwningProperty() { 3204 if (this.wrappedValue_) { 3205 if (this.wrappedValue_ instanceof SubscribaleAbstract) { 3206 this.wrappedValue_.removeOwningProperty(this); 3207 } 3208 else { 3209 ObservedObject.removeOwningProperty(this.wrappedValue_, this); 3210 } 3211 } 3212 } 3213 /* 3214 actually update this.wrappedValue_ 3215 called needs to do value change check 3216 and also notify with this.aboutToChange(); 3217 */ 3218 setValueInternal(newValue) { 3219 if (typeof newValue !== 'object') { 3220 3221 return false; 3222 } 3223 this.unsubscribeFromOwningProperty(); 3224 if (ObservedObject.IsObservedObject(newValue)) { 3225 3226 ObservedObject.addOwningProperty(newValue, this); 3227 this.wrappedValue_ = newValue; 3228 } 3229 else if (newValue instanceof SubscribaleAbstract) { 3230 3231 this.wrappedValue_ = newValue; 3232 this.wrappedValue_.addOwningProperty(this); 3233 } 3234 else { 3235 3236 this.wrappedValue_ = ObservedObject.createNew(newValue, this); 3237 } 3238 return true; 3239 } 3240 get() { 3241 3242 this.notifyPropertyRead(); 3243 return this.wrappedValue_; 3244 } 3245 set(newValue) { 3246 if (this.wrappedValue_ == newValue) { 3247 3248 return; 3249 } 3250 3251 this.setValueInternal(newValue); 3252 this.notifyHasChanged(newValue); 3253 } 3254 /** 3255 * These functions are used 3256 * LocalStorage.link (also in partial update config) 3257 * (FU)View.initializeConsumeinitializeConsume 3258 */ 3259 createLink(subscribeOwner, linkPropName) { 3260 return new SynchedPropertyObjectTwoWay(this, subscribeOwner, linkPropName); 3261 } 3262 createProp(subscribeOwner, linkPropName) { 3263 throw new Error("Creating a 'Prop' property is unsupported for Object type property value."); 3264 } 3265} 3266/* 3267 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3268 * Licensed under the Apache License, Version 2.0 (the "License"); 3269 * you may not use this file except in compliance with the License. 3270 * You may obtain a copy of the License at 3271 * 3272 * http://www.apache.org/licenses/LICENSE-2.0 3273 * 3274 * Unless required by applicable law or agreed to in writing, software 3275 * distributed under the License is distributed on an "AS IS" BASIS, 3276 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3277 * See the License for the specific language governing permissions and 3278 * limitations under the License. 3279 */ 3280/** 3281 * ObservedPropertySimple 3282 * 3283 * all definitions in this file are framework internal 3284 */ 3285class ObservedPropertySimple extends ObservedPropertySimpleAbstract { 3286 constructor(value, owningView, propertyName) { 3287 super(owningView, propertyName); 3288 if (typeof value === 'object') { 3289 throw new SyntaxError('ObservedPropertySimple value must not be an object'); 3290 } 3291 this.setValueInternal(value); 3292 } 3293 aboutToBeDeleted(unsubscribeMe) { 3294 if (unsubscribeMe) { 3295 this.unlinkSuscriber(unsubscribeMe.id__()); 3296 } 3297 super.aboutToBeDeleted(); 3298 } 3299 hasChanged(newValue) { 3300 3301 this.notifyHasChanged(this.wrappedValue_); 3302 } 3303 /* 3304 actually update this.wrappedValue_ 3305 called needs to do value change check 3306 and also notify with this.aboutToChange(); 3307 */ 3308 setValueInternal(newValue) { 3309 3310 this.wrappedValue_ = newValue; 3311 } 3312 get() { 3313 3314 this.notifyPropertyRead(); 3315 return this.wrappedValue_; 3316 } 3317 set(newValue) { 3318 if (this.wrappedValue_ === newValue) { 3319 3320 return; 3321 } 3322 3323 this.setValueInternal(newValue); 3324 this.notifyHasChanged(newValue); 3325 } 3326 /** 3327 * These functions are meant for use in connection with the App Stoage and 3328 * business logic implementation. 3329 * the created Link and Prop will update when 'this' property value 3330 * changes. 3331 */ 3332 createLink(subscribeOwner, linkPropName) { 3333 return new SynchedPropertySimpleTwoWay(this, subscribeOwner, linkPropName); 3334 } 3335 createProp(subscribeOwner, linkPropName) { 3336 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, linkPropName); 3337 } 3338} 3339/* 3340 * Copyright (c) 2021 Huawei Device Co., Ltd. 3341 * Licensed under the Apache License, Version 2.0 (the "License"); 3342 * you may not use this file except in compliance with the License. 3343 * You may obtain a copy of the License at 3344 * 3345 * http://www.apache.org/licenses/LICENSE-2.0 3346 * 3347 * Unless required by applicable law or agreed to in writing, software 3348 * distributed under the License is distributed on an "AS IS" BASIS, 3349 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3350 * See the License for the specific language governing permissions and 3351 * limitations under the License. 3352 */ 3353/** 3354 * SynchedPropertyObjectTwoWay 3355 * 3356 * all definitions in this file are framework internal 3357 */ 3358class SynchedPropertyObjectTwoWay extends ObservedPropertyObjectAbstract { 3359 constructor(linkSource, owningChildView, thisPropertyName) { 3360 super(owningChildView, thisPropertyName); 3361 this.changeNotificationIsOngoing_ = false; 3362 this.linkedParentProperty_ = linkSource; 3363 if (this.linkedParentProperty_) { 3364 // register to the parent property 3365 this.linkedParentProperty_.subscribeMe(this); 3366 } 3367 // register to the ObservedObject 3368 ObservedObject.addOwningProperty(this.getObject(), this); 3369 } 3370 /* 3371 like a destructor, need to call this before deleting 3372 the property. 3373 */ 3374 aboutToBeDeleted() { 3375 if (this.linkedParentProperty_) { 3376 // unregister from parent of this link 3377 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 3378 // unregister from the ObservedObject 3379 ObservedObject.removeOwningProperty(this.getObject(), this); 3380 } 3381 super.aboutToBeDeleted(); 3382 } 3383 getObject() { 3384 this.notifyPropertyRead(); 3385 return (this.linkedParentProperty_ ? this.linkedParentProperty_.get() : undefined); 3386 } 3387 setObject(newValue) { 3388 if (this.linkedParentProperty_) { 3389 this.linkedParentProperty_.set(newValue); 3390 } 3391 } 3392 // this object is subscriber to ObservedObject 3393 // will call this cb function when property has changed 3394 hasChanged(newValue) { 3395 if (!this.changeNotificationIsOngoing_) { 3396 3397 this.notifyHasChanged(this.getObject()); 3398 } 3399 } 3400 // get 'read through` from the ObservedProperty 3401 get() { 3402 3403 return this.getObject(); 3404 } 3405 // set 'writes through` to the ObservedProperty 3406 set(newValue) { 3407 if (this.getObject() === newValue) { 3408 3409 return; 3410 } 3411 3412 ObservedObject.removeOwningProperty(this.getObject(), this); 3413 // the purpose of the changeNotificationIsOngoing_ is to avoid 3414 // circular notifications @Link -> source @State -> other but alos same @Link 3415 this.changeNotificationIsOngoing_ = true; 3416 this.setObject(newValue); 3417 ObservedObject.addOwningProperty(this.getObject(), this); 3418 this.notifyHasChanged(newValue); 3419 this.changeNotificationIsOngoing_ = false; 3420 } 3421 /** 3422 * These functions are meant for use in connection with the App Stoage and 3423 * business logic implementation. 3424 * the created Link and Prop will update when 'this' property value 3425 * changes. 3426 */ 3427 createLink(subscribeOwner, linkPropName) { 3428 return new SynchedPropertyObjectTwoWay(this, subscribeOwner, linkPropName); 3429 } 3430 createProp(subscribeOwner, linkPropName) { 3431 throw new Error("Creating a 'Prop' property is unsupported for Object type property value."); 3432 } 3433} 3434/* 3435 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3436 * Licensed under the Apache License, Version 2.0 (the "License"); 3437 * you may not use this file except in compliance with the License. 3438 * You may obtain a copy of the License at 3439 * 3440 * http://www.apache.org/licenses/LICENSE-2.0 3441 * 3442 * Unless required by applicable law or agreed to in writing, software 3443 * distributed under the License is distributed on an "AS IS" BASIS, 3444 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3445 * See the License for the specific language governing permissions and 3446 * limitations under the License. 3447 */ 3448/** 3449 * SynchedPropertySimpleOneWay 3450 * 3451 * all definitions in this file are framework internal 3452 */ 3453class SynchedPropertySimpleOneWay extends ObservedPropertySimpleAbstract { 3454 constructor(value, subscribeMe, info) { 3455 super(subscribeMe, info); 3456 // add a test here that T is a simple type 3457 this.wrappedValue_ = value; 3458 } 3459 /* 3460 like a destructor, need to call this before deleting 3461 the property. 3462 */ 3463 aboutToBeDeleted() { 3464 super.aboutToBeDeleted(); 3465 } 3466 // get 'read through` from the ObservedProperty 3467 get() { 3468 3469 this.notifyPropertyRead(); 3470 return this.wrappedValue_; 3471 } 3472 set(newValue) { 3473 if (this.wrappedValue_ == newValue) { 3474 3475 return; 3476 } 3477 3478 this.wrappedValue_ = newValue; 3479 this.notifyHasChanged(newValue); 3480 } 3481 /** 3482 * These functions are meant for use in connection with the App Stoage and 3483 * business logic implementation. 3484 * the created Link and Prop will update when 'this' property value 3485 * changes. 3486 */ 3487 createLink(subscribeOwner, linkPropName) { 3488 throw new Error("Can not create a 'Link' from a 'Prop' property. "); 3489 } 3490 createProp(subscribeOwner, linkPropName) { 3491 throw new Error('Method not supported, create a SynchedPropertySimpleOneWaySubscribing from, where to create a Prop.'); 3492 } 3493} 3494/* 3495 This exrension of SynchedPropertySimpleOneWay needs to be used for AppStorage 3496 because it needs to be notified about the source property changing 3497 ( there is no re-render process as in Views to update the wrappedValue ) 3498*/ 3499class SynchedPropertySimpleOneWaySubscribing extends SynchedPropertySimpleOneWay { 3500 constructor(linkedProperty, subscribeMe, info) { 3501 super(linkedProperty.get(), subscribeMe, info); 3502 this.linkedParentProperty_ = linkedProperty; 3503 this.linkedParentProperty_.subscribeMe(this); 3504 } 3505 aboutToBeDeleted() { 3506 // unregister from parent of this prop 3507 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 3508 super.aboutToBeDeleted(); 3509 } 3510 hasChanged(newValue) { 3511 3512 this.set(newValue); 3513 } 3514 /** 3515 * These functions are meant for use in connection with the App Stoage and 3516 * business logic implementation. 3517 * the created Link and Prop will update when 'this' property value 3518 * changes. 3519 */ 3520 createLink(subscribeOwner, linkPropName) { 3521 throw new Error("Can not create a 'Link' from a 'Prop' property. "); 3522 } 3523 createProp(subscribeOwner, propPropName) { 3524 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, propPropName); 3525 } 3526} 3527/* 3528 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3529 * Licensed under the Apache License, Version 2.0 (the "License"); 3530 * you may not use this file except in compliance with the License. 3531 * You may obtain a copy of the License at 3532 * 3533 * http://www.apache.org/licenses/LICENSE-2.0 3534 * 3535 * Unless required by applicable law or agreed to in writing, software 3536 * distributed under the License is distributed on an "AS IS" BASIS, 3537 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3538 * See the License for the specific language governing permissions and 3539 * limitations under the License. 3540 */ 3541/** 3542 * SynchedPropertySimpleTwoWay 3543 * 3544 * all definitions in this file are framework internal 3545 */ 3546class SynchedPropertySimpleTwoWay extends ObservedPropertySimpleAbstract { 3547 constructor(source, owningView, owningViewPropNme) { 3548 super(owningView, owningViewPropNme); 3549 this.changeNotificationIsOngoing_ = false; 3550 this.source_ = source; 3551 this.source_.subscribeMe(this); 3552 } 3553 /* 3554 like a destructor, need to call this before deleting 3555 the property. 3556 */ 3557 aboutToBeDeleted() { 3558 if (this.source_) { 3559 this.source_.unlinkSuscriber(this.id__()); 3560 this.source_ = undefined; 3561 } 3562 super.aboutToBeDeleted(); 3563 } 3564 // this object is subscriber to SynchedPropertySimpleTwoWay 3565 // will call this cb function when property has changed 3566 // a set (newValue) is not done because get reads through for the source_ 3567 hasChanged(newValue) { 3568 if (!this.changeNotificationIsOngoing_) { 3569 3570 this.notifyHasChanged(newValue); 3571 } 3572 } 3573 // get 'read through` from the ObservedProperty 3574 get() { 3575 3576 if (!this.source_) { 3577 stateMgmtConsole.error(`SynchedPropertySimpleTwoWay[${this.id__()}IP, '${this.info() || 'unknown'}'] source_ is undefined: get value is undefined.`); 3578 return undefined; 3579 } 3580 this.notifyPropertyRead(); 3581 return this.source_.get(); 3582 } 3583 // set 'writes through` to the ObservedProperty 3584 set(newValue) { 3585 if (!this.source_) { 3586 stateMgmtConsole.error(`SynchedPropertySimpleTwoWay[${this.id__()}IP, '${this.info() || 'unknown'}'] source_ is undefined: set '${newValue}' ignoring.`); 3587 return; 3588 } 3589 if (this.source_.get() === newValue) { 3590 3591 return; 3592 } 3593 3594 // the source_ ObservedProeprty will call: this.hasChanged(newValue); 3595 // the purpose of the changeNotificationIsOngoing_ is to avoid 3596 // circular notifications @Link -> source @State -> other but alos same @Link 3597 this.changeNotificationIsOngoing_ = true; 3598 this.source_.set(newValue); 3599 this.notifyHasChanged(newValue); 3600 this.changeNotificationIsOngoing_ = false; 3601 } 3602 /** 3603 * These functions are meant for use in connection with the App Stoage and 3604 * business logic implementation. 3605 * the created Link and Prop will update when 'this' property value 3606 * changes. 3607 */ 3608 createLink(subscribeOwner, linkPropName) { 3609 return new SynchedPropertySimpleTwoWay(this, subscribeOwner, linkPropName); 3610 } 3611 createProp(subscribeOwner, propPropName) { 3612 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, propPropName); 3613 } 3614} 3615/* 3616 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3617 * Licensed under the Apache License, Version 2.0 (the "License"); 3618 * you may not use this file except in compliance with the License. 3619 * You may obtain a copy of the License at 3620 * 3621 * http://www.apache.org/licenses/LICENSE-2.0 3622 * 3623 * Unless required by applicable law or agreed to in writing, software 3624 * distributed under the License is distributed on an "AS IS" BASIS, 3625 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3626 * See the License for the specific language governing permissions and 3627 * limitations under the License. 3628 */ 3629/** 3630 * SynchedPropertyNesedObject 3631 * 3632 * all definitions in this file are framework internal 3633 */ 3634class SynchedPropertyNesedObject extends ObservedPropertyObjectAbstract { 3635 /** 3636 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 3637 * example 3638 * this.b.$a with b of type PC and a of type C, or 3639 * this.$b[5] with this.b of type PC and array item b[5] of type C; 3640 * 3641 * @param subscribeMe 3642 * @param propName 3643 */ 3644 constructor(obsObject, owningChildView, propertyName) { 3645 super(owningChildView, propertyName); 3646 this.obsObject_ = obsObject; 3647 // register to the ObservedObject 3648 ObservedObject.addOwningProperty(this.obsObject_, this); 3649 } 3650 /* 3651 like a destructor, need to call this before deleting 3652 the property. 3653 */ 3654 aboutToBeDeleted() { 3655 // unregister from the ObservedObject 3656 ObservedObject.removeOwningProperty(this.obsObject_, this); 3657 super.aboutToBeDeleted(); 3658 } 3659 // this object is subscriber to ObservedObject 3660 // will call this cb function when property has changed 3661 hasChanged(newValue) { 3662 3663 this.notifyHasChanged(this.obsObject_); 3664 } 3665 // get 'read through` from the ObservedProperty 3666 get() { 3667 3668 this.notifyPropertyRead(); 3669 return this.obsObject_; 3670 } 3671 // set 'writes through` to the ObservedProperty 3672 set(newValue) { 3673 if (this.obsObject_ === newValue) { 3674 3675 return; 3676 } 3677 3678 // unsubscribe from the old value ObservedObject 3679 ObservedObject.removeOwningProperty(this.obsObject_, this); 3680 this.obsObject_ = newValue; 3681 // subscribe to the new value ObservedObject 3682 ObservedObject.addOwningProperty(this.obsObject_, this); 3683 // notify value change to subscribing View 3684 this.notifyHasChanged(this.obsObject_); 3685 } 3686 /** 3687 * These functions are meant for use in connection with the App Stoage and 3688 * business logic implementation. 3689 * the created Link and Prop will update when 'this' property value 3690 * changes. 3691 */ 3692 createLink(subscribeOwner, linkPropName) { 3693 throw new Error('Method not supported for property linking to a nested objects.'); 3694 } 3695 createProp(subscribeOwner, linkPropName) { 3696 throw new Error("Creating a 'Prop' proerty is unsuppoeted for Object type prperty value."); 3697 } 3698} 3699/* 3700 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3701 * Licensed under the Apache License, Version 2.0 (the "License"); 3702 * you may not use this file except in compliance with the License. 3703 * You may obtain a copy of the License at 3704 * 3705 * http://www.apache.org/licenses/LICENSE-2.0 3706 * 3707 * Unless required by applicable law or agreed to in writing, software 3708 * distributed under the License is distributed on an "AS IS" BASIS, 3709 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3710 * See the License for the specific language governing permissions and 3711 * limitations under the License. 3712 */ 3713// Nativeview 3714// implemented in C++ for release 3715// and in utest/view_native_mock.ts for testing 3716class View extends NativeViewFullUpdate { 3717 /** 3718 * Create a View 3719 * 3720 * 1. option: top level View, specify 3721 * - compilerAssignedUniqueChildId must specify 3722 * - parent=undefined 3723 * - localStorage must provide if @LocalSTorageLink/Prop variables are used 3724 * in this View or descendant Views. 3725 * 3726 * 2. option: not a top level View 3727 * - compilerAssignedUniqueChildId must specify 3728 * - parent must specify 3729 * - localStorage do not specify, will inherit from parent View. 3730 * 3731 * @param compilerAssignedUniqueChildId Tw 3732 * @param parent 3733 * @param localStorage 3734 */ 3735 constructor(compilerAssignedUniqueChildId, parent, localStorage) { 3736 super(compilerAssignedUniqueChildId, parent); 3737 this.propsUsedForRender = new Set(); 3738 this.isRenderingInProgress = false; 3739 this.watchedProps = new Map(); 3740 // my LocalStorge instance, shared with ancestor Views. 3741 // create a default instance on demand if none is initialized 3742 this.localStoragebackStore_ = undefined; 3743 this.id_ = SubscriberManager.MakeId(); 3744 this.providedVars_ = parent ? new Map(parent.providedVars_) 3745 : new Map(); 3746 this.localStoragebackStore_ = undefined; 3747 if (parent) { 3748 // this View is not a top-level View 3749 3750 this.setCardId(parent.getCardId()); 3751 this.localStorage_ = parent.localStorage_; 3752 } 3753 else if (localStorage) { 3754 this.localStorage_ = localStorage; 3755 3756 } 3757 SubscriberManager.Add(this); 3758 3759 } 3760 get localStorage_() { 3761 if (!this.localStoragebackStore_) { 3762 3763 this.localStoragebackStore_ = new LocalStorage({ /* emty */}); 3764 } 3765 return this.localStoragebackStore_; 3766 } 3767 set localStorage_(instance) { 3768 if (!instance) { 3769 // setting to undefined not allowed 3770 return; 3771 } 3772 if (this.localStoragebackStore_) { 3773 stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`); 3774 } 3775 this.localStoragebackStore_ = instance; 3776 } 3777 // globally unique id, this is different from compilerAssignedUniqueChildId! 3778 id__() { 3779 return this.id_; 3780 } 3781 // temporary function, do not use, it will be removed soon! 3782 // prupsoe is to allow eDSL transpiler to fix a bug that 3783 // relies on this method 3784 id() { 3785 return this.id__(); 3786 } 3787 propertyHasChanged(info) { 3788 if (info) { 3789 // need to sync container instanceId to switch instanceId in C++ side. 3790 this.syncInstanceId(); 3791 if (this.propsUsedForRender.has(info)) { 3792 3793 this.markNeedUpdate(); 3794 } 3795 else { 3796 3797 } 3798 let cb = this.watchedProps.get(info); 3799 if (cb) { 3800 3801 cb.call(this, info); 3802 } 3803 this.restoreInstanceId(); 3804 } // if info avail. 3805 } 3806 propertyRead(info) { 3807 3808 if (info && (info !== 'unknown') && this.isRenderingInProgress) { 3809 this.propsUsedForRender.add(info); 3810 } 3811 } 3812 // for test purposes 3813 propertiesNeededToRender() { 3814 return this.propsUsedForRender; 3815 } 3816 aboutToRender() { 3817 3818 // reset 3819 this.propsUsedForRender = new Set(); 3820 this.isRenderingInProgress = true; 3821 } 3822 aboutToContinueRender() { 3823 // do not reset 3824 this.isRenderingInProgress = true; 3825 } 3826 onRenderDone() { 3827 this.isRenderingInProgress = false; 3828 3829 } 3830 /** 3831 * Function to be called from the constructor of the sub component 3832 * to register a @Watch varibale 3833 * @param propStr name of the variable. Note from @Provide and @Consume this is 3834 * the variable name and not the alias! 3835 * @param callback application defined member function of sub-class 3836 */ 3837 declareWatch(propStr, callback) { 3838 this.watchedProps.set(propStr, callback); 3839 } 3840 /** 3841 * This View @Provide's a variable under given name 3842 * Call this function from the constructor of the sub class 3843 * @param providedPropName either the variable name or the alias defined as 3844 * decorator param 3845 * @param store the backing store object for this variable (not the get/set variable!) 3846 */ 3847 addProvidedVar(providedPropName, store) { 3848 if (this.providedVars_.has(providedPropName)) { 3849 throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}. 3850 Property with this name is provided by one of the ancestor Views already.`); 3851 } 3852 this.providedVars_.set(providedPropName, store); 3853 } 3854 /** 3855 * Method for the sub-class to call from its constructor for resolving 3856 * a @Consume variable and initializing its backing store 3857 * with the yncedPropertyTwoWay<T> object created from the 3858 * @Provide variable's backing store. 3859 * @param providedPropName the name of the @Provide'd variable. 3860 * This is either the @Consume decortor parameter, or variable name. 3861 * @param consumeVarName the @Consume variable name (not the 3862 * @Consume decortor parameter) 3863 * @returns initiaizing value of the @Consume backing store 3864 */ 3865 initializeConsume(providedPropName, consumeVarName) { 3866 let providedVarStore = this.providedVars_.get(providedPropName); 3867 if (providedVarStore === undefined) { 3868 throw new ReferenceError(`${this.constructor.name}: missing @Provide property with name ${providedPropName}. 3869 Fail to resolve @Consume(${providedPropName}).`); 3870 } 3871 return providedVarStore.createLink(this, consumeVarName); 3872 } 3873} 3874/* 3875 * Copyright (c) 2024 Huawei Device Co., Ltd. 3876 * Licensed under the Apache License, Version 2.0 (the "License"); 3877 * you may not use this file except in compliance with the License. 3878 * You may obtain a copy of the License at 3879 * 3880 * http://www.apache.org/licenses/LICENSE-2.0 3881 * 3882 * Unless required by applicable law or agreed to in writing, software 3883 * distributed under the License is distributed on an "AS IS" BASIS, 3884 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3885 * See the License for the specific language governing permissions and 3886 * limitations under the License. 3887 */ 3888/* 3889 * Copyright (c) 2022 Huawei Device Co., Ltd. 3890 * Licensed under the Apache License, Version 2.0 (the "License"); 3891 * you may not use this file except in compliance with the License. 3892 * You may obtain a copy of the License at 3893 * 3894 * http://www.apache.org/licenses/LICENSE-2.0 3895 * 3896 * Unless required by applicable law or agreed to in writing, software 3897 * distributed under the License is distributed on an "AS IS" BASIS, 3898 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3899 * See the License for the specific language governing permissions and 3900 * limitations under the License. 3901 */ 3902// UpdateFuncRecord: misc framework-internal info related to updating of a UINode C++ object 3903// that TS side needs to know. 3904// updateFunc_ lambda function to update the UINode 3905// JS interface class reference (it only has static functions) 3906class UpdateFuncRecord { 3907 constructor(params) { 3908 this.updateFunc_ = params.updateFunc; 3909 this.classObject_ = params.classObject; 3910 this.node_ = params.node; 3911 } 3912 getUpdateFunc() { 3913 return this.updateFunc_; 3914 } 3915 getComponentClass() { 3916 return this.classObject_; 3917 } 3918 getComponentName() { 3919 return (this.classObject_ && ('name' in this.classObject_)) ? Reflect.get(this.classObject_, 'name') : 'unspecified UINode'; 3920 } 3921 getPopFunc() { 3922 return (this.classObject_ && 'pop' in this.classObject_) ? this.classObject_.pop : () => { }; 3923 } 3924 getNode() { 3925 return this.node_; 3926 } 3927 setNode(node) { 3928 this.node_ = node; 3929 } 3930} // UpdateFuncRecord 3931class UpdateFuncsByElmtId { 3932 constructor() { 3933 this.map_ = new Map(); 3934 } 3935 delete(elmtId) { 3936 return this.map_.delete(elmtId); 3937 } 3938 set(elmtId, params) { 3939 (typeof params === 'object') ? 3940 this.map_.set(elmtId, new UpdateFuncRecord(params)) : 3941 this.map_.set(elmtId, new UpdateFuncRecord({ updateFunc: params })); 3942 } 3943 get(elmtId) { 3944 return this.map_.get(elmtId); 3945 } 3946 has(elmtId) { 3947 return this.map_.has(elmtId); 3948 } 3949 keys() { 3950 return this.map_.keys(); 3951 } 3952 clear() { 3953 return this.map_.clear(); 3954 } 3955 get size() { 3956 return this.map_.size; 3957 } 3958 forEach(callbackfn) { 3959 this.map_.forEach(callbackfn); 3960 } 3961 // dump info about known elmtIds to a string 3962 // use function only for debug output and DFX. 3963 debugInfoRegisteredElmtIds() { 3964 let result = ''; 3965 let sepa = ''; 3966 this.map_.forEach((value, elmtId) => { 3967 result += `${sepa}${value.getComponentName()}[${elmtId}]`; 3968 sepa = ', '; 3969 }); 3970 return result; 3971 } 3972 debugInfoElmtId(elmtId) { 3973 const updateFuncEntry = this.map_.get(elmtId); 3974 return updateFuncEntry ? `${updateFuncEntry.getComponentName()}[${elmtId}]` : `'unknown component type'[${elmtId}]`; 3975 } 3976} // class UpdateFuncByElmtId 3977/* 3978 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 3979 * Licensed under the Apache License, Version 2.0 (the "License"); 3980 * you may not use this file except in compliance with the License. 3981 * You may obtain a copy of the License at 3982 * 3983 * http://www.apache.org/licenses/LICENSE-2.0 3984 * 3985 * Unless required by applicable law or agreed to in writing, software 3986 * distributed under the License is distributed on an "AS IS" BASIS, 3987 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3988 * See the License for the specific language governing permissions and 3989 * limitations under the License. 3990 * 3991*/ 3992// NativeView 3993// implemented in C++ for release 3994class PUV2ViewBase extends NativeViewPartialUpdate { 3995 constructor(parent, elmtId = UINodeRegisterProxy.notRecordingDependencies, extraInfo = undefined) { 3996 super(); 3997 // indicates the currently rendered or rendered UINode's elmtIds 3998 // or UINodeRegisterProxy.notRecordingDependencies if none is currently rendering 3999 // isRenderInProgress == true always when currentlyRenderedElmtIdStack_ length >= 0 4000 this.currentlyRenderedElmtIdStack_ = new Array(); 4001 // Set of elmtIds that need re-render 4002 this.dirtDescendantElementIds_ = new Set(); 4003 // Map elmtId -> Repeat instance in this ViewPU 4004 this.elmtId2Repeat_ = new Map(); 4005 this.parent_ = undefined; 4006 this.childrenWeakrefMap_ = new Map(); 4007 // flag if active of inActive 4008 // inActive means updates are delayed 4009 this.isActive_ = true; 4010 // flag if {aboutToBeDeletedInternal} is called and the instance of ViewPU/V2 has not been GC. 4011 this.isDeleting_ = false; 4012 this.isCompFreezeAllowed_ = false; 4013 // registry of update functions 4014 // the key is the elementId of the Component/Element that's the result of this function 4015 this.updateFuncByElmtId = new UpdateFuncsByElmtId(); 4016 this.extraInfo_ = undefined; 4017 // Set of elements for delayed update 4018 this.elmtIdsDelayedUpdate_ = new Set(); 4019 // if set use the elmtId also as the ViewPU/V2 object's subscribable id. 4020 // these matching is requirement for updateChildViewById(elmtId) being able to 4021 // find the child ViewPU/V2 object by given elmtId 4022 this.id_ = elmtId === UINodeRegisterProxy.notRecordingDependencies ? SubscriberManager.MakeId() : elmtId; 4023 4024 if (extraInfo) { 4025 this.extraInfo_ = extraInfo; 4026 } 4027 if (parent) { 4028 // this View is not a top-level View 4029 this.setCardId(parent.getCardId()); 4030 // Call below will set this parent_ to parent as well 4031 parent.addChild(this); // FIXME 4032 } 4033 this.isCompFreezeAllowed_ = this.isCompFreezeAllowed_ || (this.parent_ && this.parent_.isCompFreezeAllowed()); 4034 4035 } 4036 // globally unique id, this is different from compilerAssignedUniqueChildId! 4037 id__() { 4038 return this.id_; 4039 } 4040 updateId(elmtId) { 4041 this.id_ = elmtId; 4042 } 4043 /* Adds the elmtId to elmtIdsDelayedUpdate for delayed update 4044 once the view gets active 4045 */ 4046 scheduleDelayedUpdate(elmtId) { 4047 this.elmtIdsDelayedUpdate.add(elmtId); 4048 } 4049 get elmtIdsDelayedUpdate() { 4050 return this.elmtIdsDelayedUpdate_; 4051 } 4052 setParent(parent) { 4053 if (this.parent_ && parent) { 4054 stateMgmtConsole.warn(`${this.debugInfo__()}: setChild: changing parent to '${parent === null || parent === void 0 ? void 0 : parent.debugInfo__()} (unsafe operation)`); 4055 } 4056 this.parent_ = parent; 4057 } 4058 getParent() { 4059 return this.parent_; 4060 } 4061 /** 4062 * add given child and set 'this' as its parent 4063 * @param child child to add 4064 * @returns returns false if child with given child's id already exists 4065 * 4066 * framework internal function 4067 * Note: Use of WeakRef ensures child and parent do not generate a cycle dependency. 4068 * The add. Set<ids> is required to reliably tell what children still exist. 4069 */ 4070 addChild(child) { 4071 if (this.childrenWeakrefMap_.has(child.id__())) { 4072 stateMgmtConsole.warn(`${this.debugInfo__()}: addChild '${child === null || child === void 0 ? void 0 : child.debugInfo__()}' elmtId already exists ${child.id__()}. Internal error!`); 4073 return false; 4074 } 4075 this.childrenWeakrefMap_.set(child.id__(), new WeakRef(child)); 4076 child.setParent(this); // FIXME 4077 return true; 4078 } 4079 /** 4080 * remove given child and remove 'this' as its parent 4081 * @param child child to add 4082 * @returns returns false if child with given child's id does not exist 4083 */ 4084 removeChild(child) { 4085 const hasBeenDeleted = this.childrenWeakrefMap_.delete(child.id__()); 4086 if (!hasBeenDeleted) { 4087 stateMgmtConsole.warn(`${this.debugInfo__()}: removeChild '${child === null || child === void 0 ? void 0 : child.debugInfo__()}', child id ${child.id__()} not known. Internal error!`); 4088 } 4089 else { 4090 child.setParent(undefined); 4091 } 4092 return hasBeenDeleted; 4093 } 4094 /** 4095 * Retrieve child by given id 4096 * @param id 4097 * @returns child if in map and weak ref resolves to IView object 4098 */ 4099 getChildById(id) { 4100 const childWeakRef = this.childrenWeakrefMap_.get(id); 4101 return childWeakRef ? childWeakRef.deref() : undefined; 4102 } 4103 aboutToReuse(_) { } 4104 aboutToRecycle() { } 4105 isDeleting() { 4106 return this.isDeleting_; 4107 } 4108 setDeleting() { 4109 4110 this.isDeleting_ = true; 4111 } 4112 setDeleteStatusRecursively() { 4113 if (!this.childrenWeakrefMap_.size) { 4114 return; 4115 } 4116 4117 this.childrenWeakrefMap_.forEach((value) => { 4118 let child = value.deref(); 4119 if (child) { 4120 child.setDeleting(); 4121 child.setDeleteStatusRecursively(); 4122 } 4123 }); 4124 } 4125 isCompFreezeAllowed() { 4126 return this.isCompFreezeAllowed_; 4127 } 4128 getChildViewV2ForElmtId(elmtId) { 4129 const optComp = this.childrenWeakrefMap_.get(elmtId); 4130 return (optComp === null || optComp === void 0 ? void 0 : optComp.deref()) && (optComp.deref() instanceof ViewV2) ? optComp === null || optComp === void 0 ? void 0 : optComp.deref() : undefined; 4131 } 4132 purgeVariableDependenciesOnElmtIdOwnFunc(elmtId) { 4133 // ViewPU overrides to unregister ViewPU from variables, 4134 // not in use in ViewV2 4135 } 4136 // overwritten by sub classes 4137 debugInfo__() { 4138 return `@Component '${this.constructor.name}'[${this.id__()}]`; 4139 } 4140 debugInfoRegisteredElmtIds() { 4141 return this.updateFuncByElmtId.debugInfoRegisteredElmtIds(); 4142 } 4143 // for given elmtIds look up their component name/type and format a string out of this info 4144 // use function only for debug output and DFX. 4145 debugInfoElmtIds(elmtIds) { 4146 let result = ''; 4147 let sepa = ''; 4148 elmtIds.forEach((elmtId) => { 4149 result += `${sepa}${this.debugInfoElmtId(elmtId)}`; 4150 sepa = ', '; 4151 }); 4152 return result; 4153 } 4154 debugInfoElmtId(elmtId, isProfiler = false) { 4155 return isProfiler ? { 4156 elementId: elmtId, 4157 elementTag: this.updateFuncByElmtId.get(elmtId).getComponentName(), 4158 isCustomNode: this.childrenWeakrefMap_.has(elmtId) 4159 } : this.updateFuncByElmtId.debugInfoElmtId(elmtId); 4160 } 4161 dumpStateVars() { 4162 4163 } 4164 isViewActive() { 4165 return this.isActive_; 4166 } 4167 dumpReport() { 4168 stateMgmtConsole.warn(`Printing profiler information`); 4169 stateMgmtProfiler.report(); 4170 } 4171 updateStateVarsOfChildByElmtId(elmtId, params) { 4172 4173 4174 if (elmtId < 0) { 4175 stateMgmtConsole.warn(`${this.debugInfo__()}: updateChildViewById(${elmtId}) - invalid elmtId - internal error!`); 4176 4177 return; 4178 } 4179 let iChild = this.getChildById(elmtId); 4180 if (!iChild || !((iChild instanceof ViewPU) || (iChild instanceof ViewV2))) { 4181 stateMgmtConsole.warn(`${this.debugInfo__()}: updateChildViewById(${elmtId}) - no child with this elmtId - internal error!`); 4182 4183 return; 4184 } 4185 const child = iChild; 4186 if ('updateStateVars' in child) { 4187 child.updateStateVars(params); 4188 } 4189 4190 4191 } 4192 // request list of all (global) elmtIds of deleted UINodes and unregister from the all ViewPUs/ViewV2 4193 // this function equals purgeDeletedElmtIdsRecursively because it does un-registration for all ViewPU/V2's 4194 purgeDeletedElmtIds() { 4195 4196 // request list of all (global) elmtIds of deleted UINodes that need to be unregistered 4197 UINodeRegisterProxy.obtainDeletedElmtIds(); 4198 // unregister the removed elmtIds requested from the cpp side for all ViewPUs/ViewV2, it will make the first ViewPUs/ViewV2 slower 4199 // than before, but the rest ViewPUs/ViewV2 will be faster 4200 UINodeRegisterProxy.unregisterElmtIdsFromIViews(); 4201 4202 } 4203 /** 4204 * force a complete rerender / update by executing all update functions 4205 * exec a regular rerender first 4206 * 4207 * @param deep recurse all children as well 4208 * 4209 * framework internal functions, apps must not call 4210 */ 4211 forceCompleteRerender(deep = false) { 4212 4213 4214 // see which elmtIds are managed by this View 4215 // and clean up all book keeping for them 4216 this.purgeDeletedElmtIds(); 4217 Array.from(this.updateFuncByElmtId.keys()).sort(ViewPU.compareNumber).forEach(elmtId => this.UpdateElement(elmtId)); 4218 if (!deep) { 4219 4220 4221 return; 4222 } 4223 for (const child of this.childrenWeakrefMap_.values()) { 4224 const childView = child.deref(); 4225 if (!childView) { 4226 continue; 4227 } 4228 if (child instanceof ViewPU) { 4229 if (!child.isRecycled()) { 4230 child.forceCompleteRerender(true); 4231 } 4232 else { 4233 child.delayCompleteRerender(deep); 4234 } 4235 } 4236 else { 4237 childView.forceCompleteRerender(true); 4238 } 4239 } 4240 4241 4242 } 4243 /** 4244 * force a complete rerender / update on specific node by executing update function. 4245 * 4246 * @param elmtId which node needs to update. 4247 * 4248 * framework internal functions, apps must not call 4249 */ 4250 forceRerenderNode(elmtId) { 4251 4252 // see which elmtIds are managed by this View 4253 // and clean up all book keeping for them 4254 this.purgeDeletedElmtIds(); 4255 this.UpdateElement(elmtId); 4256 // remove elemtId from dirtDescendantElementIds. 4257 this.dirtDescendantElementIds_.delete(elmtId); 4258 4259 } 4260 /** 4261 * for C++ to judge whether a CustomNode has updateFunc with specified nodeId. 4262 * use same judgement with UpdateElement, to make sure it can rerender if return true. 4263 * 4264 * @param elmtId query ID 4265 * 4266 * framework internal function 4267 */ 4268 hasNodeUpdateFunc(elmtId) { 4269 const entry = this.updateFuncByElmtId.get(elmtId); 4270 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 4271 // if this component does not have updateFunc for elmtId, return false. 4272 return typeof updateFunc === 'function'; 4273 } 4274 static pauseRendering() { 4275 PUV2ViewBase.renderingPaused = true; 4276 } 4277 static restoreRendering() { 4278 PUV2ViewBase.renderingPaused = false; 4279 } 4280 // performs the update on a branch within if() { branch } else if (..) { branch } else { branch } 4281 ifElseBranchUpdateFunction(branchId, branchfunc) { 4282 var _a, _b; 4283 const oldBranchid = If.getBranchId(); 4284 if (branchId === oldBranchid) { 4285 4286 return; 4287 } 4288 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onIfElseBranchUpdateEnter(); 4289 // branchid identifies uniquely the if .. <1> .. else if .<2>. else .<3>.branch 4290 // ifElseNode stores the most recent branch, so we can compare 4291 // removedChildElmtIds will be filled with the elmtIds of all children and their children will be deleted in response to if .. else change 4292 let removedChildElmtIds = new Array(); 4293 If.branchId(branchId, removedChildElmtIds); 4294 //un-registers the removed child elementIDs using proxy 4295 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 4296 // purging these elmtIds from state mgmt will make sure no more update function on any deleted child wi;ll be executed 4297 4298 this.purgeDeletedElmtIds(); 4299 branchfunc(); 4300 (_b = PUV2ViewBase.arkThemeScopeManager) === null || _b === void 0 ? void 0 : _b.onIfElseBranchUpdateExit(removedChildElmtIds); 4301 } 4302 /** 4303 Partial updates for ForEach. 4304 * @param elmtId ID of element. 4305 * @param itemArray Array of items for use of itemGenFunc. 4306 * @param itemGenFunc Item generation function to generate new elements. If index parameter is 4307 * given set itemGenFuncUsesIndex to true. 4308 * @param idGenFunc ID generation function to generate unique ID for each element. If index parameter is 4309 * given set idGenFuncUsesIndex to true. 4310 * @param itemGenFuncUsesIndex itemGenFunc optional index parameter is given or not. 4311 * @param idGenFuncUsesIndex idGenFunc optional index parameter is given or not. 4312 */ 4313 forEachUpdateFunction(elmtId, itemArray, itemGenFunc, idGenFunc, itemGenFuncUsesIndex = false, idGenFuncUsesIndex = false) { 4314 4315 4316 if (itemArray === null || itemArray === undefined) { 4317 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): input array is null or undefined error. Application error!`); 4318 4319 return; 4320 } 4321 if (typeof itemGenFunc !== 'function') { 4322 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): Item generation function missing. Application error!`); 4323 4324 return; 4325 } 4326 if (idGenFunc !== undefined && typeof idGenFunc !== 'function') { 4327 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): id generator is not a function. Application error!`); 4328 4329 return; 4330 } 4331 if (idGenFunc === undefined) { 4332 4333 idGenFuncUsesIndex = true; 4334 // catch possible error caused by Stringify and re-throw an Error with a meaningful (!) error message 4335 idGenFunc = (item, index) => { 4336 try { 4337 return `${index}__${JSON.stringify(item)}`; 4338 } 4339 catch (e) { 4340 throw new Error(`${this.debugInfo__()}: ForEach id ${elmtId}: use of default id generator function not possible on provided data structure. Need to specify id generator function (ForEach 3rd parameter). Application Error!`); 4341 } 4342 }; 4343 } 4344 let diffIndexArray = []; // New indexes compared to old one. 4345 let newIdArray = []; 4346 let idDuplicates = []; 4347 const arr = itemArray; // just to trigger a 'get' onto the array 4348 // ID gen is with index. 4349 if (idGenFuncUsesIndex || idGenFunc.length > 1) { 4350 // Create array of new ids. 4351 arr.forEach((item, indx) => { 4352 newIdArray.push(idGenFunc(item, indx)); 4353 }); 4354 } 4355 else { 4356 // Create array of new ids. 4357 arr.forEach((item, index) => { 4358 newIdArray.push(`${itemGenFuncUsesIndex ? index + '_' : ''}` + idGenFunc(item)); 4359 }); 4360 } 4361 // removedChildElmtIds will be filled with the elmtIds of all children and their children will be deleted in response to foreach change 4362 let removedChildElmtIds = []; 4363 // Set new array on C++ side. 4364 // C++ returns array of indexes of newly added array items. 4365 // these are indexes in new child list. 4366 ForEach.setIdArray(elmtId, newIdArray, diffIndexArray, idDuplicates, removedChildElmtIds); 4367 // Its error if there are duplicate IDs. 4368 if (idDuplicates.length > 0) { 4369 idDuplicates.forEach((indx) => { 4370 stateMgmtConsole.error(`Error: ${newIdArray[indx]} generated for ${indx}${indx < 4 ? indx === 2 ? 'nd' : 'rd' : 'th'} array item ${arr[indx]}.`); 4371 }); 4372 stateMgmtConsole.applicationError(`${this.debugInfo__()}: Ids generated by the ForEach id gen function must be unique. Application error!`); 4373 } 4374 4375 // Item gen is with index. 4376 4377 // Create new elements if any. 4378 4379 diffIndexArray.forEach((indx) => { 4380 ForEach.createNewChildStart(newIdArray[indx], this); 4381 if (itemGenFuncUsesIndex) { 4382 itemGenFunc(arr[indx], indx); 4383 } 4384 else { 4385 itemGenFunc(arr[indx]); 4386 } 4387 ForEach.createNewChildFinish(newIdArray[indx], this); 4388 }); 4389 // un-registers the removed child elementIDs using proxy 4390 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 4391 // purging these elmtIds from state mgmt will make sure no more update function on any deleted child will be executed 4392 4393 this.purgeDeletedElmtIds(); 4394 4395 4396 4397 } 4398 createOrGetNode(elmtId, builder) { 4399 const entry = this.updateFuncByElmtId.get(elmtId); 4400 if (entry === undefined) { 4401 throw new Error(`${this.debugInfo__()} fail to create node, elmtId is illegal`); 4402 } 4403 let nodeInfo = entry.getNode(); 4404 if (nodeInfo === undefined) { 4405 nodeInfo = builder(); 4406 entry.setNode(nodeInfo); 4407 } 4408 return nodeInfo; 4409 } 4410 /** 4411 * getNodeById is used to get ArkComponent stored updateFuncByElmtId 4412 * @param elmtId - the id of the component 4413 * @returns ArkComponent | undefined 4414 */ 4415 getNodeById(elmtId) { 4416 const entry = this.updateFuncByElmtId.get(elmtId); 4417 return entry ? entry.getNode() : undefined; 4418 } 4419 /** 4420 * return its elmtId if currently rendering or re-rendering an UINode 4421 * otherwise return UINodeRegisterProxy.notRecordingDependencies 4422 * set in observeComponentCreation(2) 4423 */ 4424 getCurrentlyRenderedElmtId() { 4425 return PUV2ViewBase.renderingPaused || this.currentlyRenderedElmtIdStack_.length === 0 4426 ? UINodeRegisterProxy.notRecordingDependencies 4427 : this.currentlyRenderedElmtIdStack_[this.currentlyRenderedElmtIdStack_.length - 1]; 4428 } 4429 debugInfoViewHierarchy(recursive = false) { 4430 return this.debugInfoViewHierarchyInternal(0, recursive); 4431 } 4432 debugInfoViewHierarchyInternal(depth = 0, recursive = false) { 4433 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]`; 4434 if (this.isCompFreezeAllowed()) { 4435 retVaL += ` {freezeWhenInactive : ${this.isCompFreezeAllowed()}}`; 4436 } 4437 if (depth < 1 || recursive) { 4438 this.childrenWeakrefMap_.forEach((weakChild) => { 4439 var _a; 4440 retVaL += (_a = weakChild.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoViewHierarchyInternal(depth + 1, recursive); 4441 }); 4442 } 4443 return retVaL; 4444 } 4445 debugInfoUpdateFuncByElmtId(recursive = false) { 4446 return this.debugInfoUpdateFuncByElmtIdInternal({ total: 0 }, 0, recursive); 4447 } 4448 debugInfoUpdateFuncByElmtIdInternal(counter, depth = 0, recursive = false) { 4449 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`; 4450 this.updateFuncByElmtId.forEach((value, key, map) => { 4451 retVaL += `\n${' '.repeat(depth + 2)}${value.getComponentName()}[${key}]`; 4452 }); 4453 counter.total += this.updateFuncByElmtId.size; 4454 retVaL += `\n${' '.repeat(depth + 1)}}[${this.updateFuncByElmtId.size}]`; 4455 if (recursive) { 4456 this.childrenWeakrefMap_.forEach((value, key, map) => { 4457 var _a; 4458 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoUpdateFuncByElmtIdInternal(counter, depth + 1, recursive); 4459 }); 4460 } 4461 if (recursive && depth === 0) { 4462 retVaL += `\nTotal: ${counter.total}`; 4463 } 4464 return retVaL; 4465 } 4466 debugInfoInactiveComponents() { 4467 return Array.from(PUV2ViewBase.inactiveComponents_) 4468 .map((component) => `- ${component}`).join('\n'); 4469 } 4470 onGlobalThemeChanged() { 4471 } 4472 static setArkThemeScopeManager(mgr) { 4473 PUV2ViewBase.arkThemeScopeManager = mgr; 4474 } 4475} // class PUV2ViewBase 4476// List of inactive components used for Dfx 4477PUV2ViewBase.inactiveComponents_ = new Set(); 4478// Array.sort() converts array items to string to compare them! 4479PUV2ViewBase.compareNumber = (a, b) => { 4480 return (a < b) ? -1 : (a > b) ? 1 : 0; 4481}; 4482// static flag for paused rendering 4483// when paused, getCurrentlyRenderedElmtId() will return UINodeRegisterProxy.notRecordingDependencies 4484PUV2ViewBase.renderingPaused = false; 4485PUV2ViewBase.arkThemeScopeManager = undefined; 4486/* 4487 * Copyright (c) 2024 Huawei Device Co., Ltd. 4488 * Licensed under the Apache License, Version 2.0 (the "License"); 4489 * you may not use this file except in compliance with the License. 4490 * You may obtain a copy of the License at 4491 * 4492 * http://www.apache.org/licenses/LICENSE-2.0 4493 * 4494 * Unless required by applicable law or agreed to in writing, software 4495 * distributed under the License is distributed on an "AS IS" BASIS, 4496 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4497 * See the License for the specific language governing permissions and 4498 * limitations under the License. 4499 */ 4500; 4501; 4502; 4503class SendableType { 4504 static isArray(o) { 4505 return o instanceof SendableArray; 4506 } 4507 static isSet(o) { 4508 return o instanceof SendableSet; 4509 } 4510 static isMap(o) { 4511 return o instanceof SendableMap; 4512 } 4513 static isContainer(o) { 4514 return o instanceof SendableMap || o instanceof SendableSet || o instanceof SendableArray; 4515 } 4516} 4517/* 4518 * Copyright (c) 2022 Huawei Device Co., Ltd. 4519 * Licensed under the Apache License, Version 2.0 (the "License"); 4520 * you may not use this file except in compliance with the License. 4521 * You may obtain a copy of the License at 4522 * 4523 * http://www.apache.org/licenses/LICENSE-2.0 4524 * 4525 * Unless required by applicable law or agreed to in writing, software 4526 * distributed under the License is distributed on an "AS IS" BASIS, 4527 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4528 * See the License for the specific language governing permissions and 4529 * limitations under the License. 4530 */ 4531/* 4532 * Copyright (c) 2023 Huawei Device Co., Ltd. 4533 * Licensed under the Apache License, Version 2.0 (the "License"); 4534 * you may not use this file except in compliance with the License. 4535 * You may obtain a copy of the License at 4536 * 4537 * http://www.apache.org/licenses/LICENSE-2.0 4538 * 4539 * Unless required by applicable law or agreed to in writing, software 4540 * distributed under the License is distributed on an "AS IS" BASIS, 4541 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4542 * See the License for the specific language governing permissions and 4543 * limitations under the License. 4544 */ 4545// @Track class property decorator 4546// indicates to framework to track individual object property value changes 4547function Track(target, property) { 4548 var _a; 4549 ConfigureStateMgmt.instance.usingPUObservedTrack(`@Track`, property); 4550 Reflect.set(target, `${TrackedObject.___TRACKED_PREFIX}${property}`, true); 4551 Reflect.set(target, TrackedObject.___IS_TRACKED_OPTIMISED, true); 4552 4553} 4554class TrackedObject { 4555 static isCompatibilityMode(obj) { 4556 return !obj || (typeof obj !== 'object') || !Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED); 4557 } 4558 static needsPropertyReadCb(obj) { 4559 return obj && (typeof obj === 'object') && Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED); 4560 } 4561 /** 4562 * @Track new object assignment optimization 4563 * can apply if old and new value are object, instance of same class, do not use compat mode. 4564 * in this case function returns true and calls supplied notifyTrackedPropertyChanged cb function 4565 * for each @Tracked'ed property whose value actually changed. 4566 * if optimisation can not be applied calls notifyPropertyChanged and returns false 4567 */ 4568 static notifyObjectValueAssignment(obj1, obj2, notifyPropertyChanged, // notify as assignment (none-optimised) 4569 notifyTrackedPropertyChange, obSelf) { 4570 if (!obj1 || !obj2 || (typeof obj1 !== 'object') || (typeof obj2 !== 'object') || 4571 (obj1.constructor !== obj2.constructor) || 4572 TrackedObject.isCompatibilityMode(obj1)) { 4573 4574 notifyPropertyChanged.call(obSelf); 4575 return false; 4576 } 4577 4578 const obj1Raw = ObservedObject.GetRawObject(obj1); 4579 const obj2Raw = ObservedObject.GetRawObject(obj2); 4580 let shouldFakePropPropertyBeNotified = false; 4581 Object.keys(obj2Raw) 4582 .forEach(propName => { 4583 // Collect only @Track'ed changed properties 4584 if (Reflect.has(obj1Raw, `${TrackedObject.___TRACKED_PREFIX}${propName}`) && 4585 (Reflect.get(obj1Raw, propName) !== Reflect.get(obj2Raw, propName))) { 4586 4587 notifyTrackedPropertyChange.call(obSelf, propName); 4588 shouldFakePropPropertyBeNotified = true; 4589 } 4590 else { 4591 4592 } 4593 }); 4594 // notify this non-existing object property has changed only if some of the tracked properties changed. 4595 // SynchedPropertyOneWay reset() report a 'read' on this property, thereby creating a dependency 4596 // reporting the property as changed causes @Prop sync from source 4597 if (shouldFakePropPropertyBeNotified) { 4598 4599 notifyTrackedPropertyChange.call(obSelf, TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY); 4600 } 4601 // always notify this non-existing object property has changed for SynchedPropertyNestedObject as 4602 // the object has changed in assigment. 4603 // SynchedPropertyNestedObject set() reports a 'read' on this property, thereby creating a dependency 4604 // reporting the property as changed causes @ObjectLink sync from source 4605 4606 notifyTrackedPropertyChange.call(obSelf, TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY); 4607 return true; 4608 } 4609} 4610TrackedObject.___IS_TRACKED_OPTIMISED = `___IS_TRACKED_OPTIMISED`; 4611TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_PROP_PROPERTY`; 4612TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_OBJLINK_PROPERTY`; 4613TrackedObject.___TRACKED_PREFIX = `___TRACKED_`; 4614TrackedObject.___TRACKED_PREFIX_LEN = TrackedObject.___TRACKED_PREFIX.length; 4615/* 4616 * Copyright (c) 2022 Huawei Device Co., Ltd. 4617 * Licensed under the Apache License, Version 2.0 (the "License"); 4618 * you may not use this file except in compliance with the License. 4619 * You may obtain a copy of the License at 4620 * 4621 * http://www.apache.org/licenses/LICENSE-2.0 4622 * 4623 * Unless required by applicable law or agreed to in writing, software 4624 * distributed under the License is distributed on an "AS IS" BASIS, 4625 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4626 * See the License for the specific language governing permissions and 4627 * limitations under the License. 4628 */ 4629var _a; 4630/** 4631 * ObservedPropertyAbstractPU aka ObservedPropertyAbstract for partial update 4632 * 4633 * all definitions in this file are framework internal 4634 */ 4635class ObservedPropertyAbstractPU extends ObservedPropertyAbstract { 4636 constructor(subscriber, viewName) { 4637 super(subscriber, viewName); 4638 this.changeNotificationIsOngoing_ = false; 4639 // when owning ViewPU is inActive, delay notifying changes 4640 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay; 4641 // install when current value is ObservedObject and the value type is not using compatibility mode 4642 // note value may change for union type variables when switching an object from one class to another. 4643 this.shouldInstallTrackedObjectReadCb = false; 4644 this.dependentElmtIdsByProperty_ = new PropertyDependencies(); 4645 Object.defineProperty(this, 'owningView_', { writable: true, enumerable: false, value: undefined }); 4646 Object.defineProperty(this, 'subscriberRefs_', { writable: true, enumerable: false, value: new Set() }); 4647 if (subscriber) { 4648 if (subscriber instanceof ViewPU) { 4649 this.owningView_ = subscriber; 4650 } 4651 else { 4652 this.subscriberRefs_.add(subscriber); 4653 } 4654 } 4655 } 4656 aboutToBeDeleted() { 4657 super.aboutToBeDeleted(); 4658 this.subscriberRefs_.clear(); 4659 this.owningView_ = undefined; 4660 } 4661 setDecoratorInfo(decorate) { 4662 this.decoratorInfo_ = decorate; 4663 } 4664 // dump info about variable decorator to string 4665 // e.g. @State, @Link, etc. 4666 debugInfoDecorator() { 4667 return this.decoratorInfo_; 4668 } 4669 // dump basic info about this variable to a string, non-recursive, no subscriber info 4670 debugInfo() { 4671 const propSource = this.isPropSourceObservedPropertyFakeName(); 4672 return (propSource) 4673 ? `internal source (ObservedPropertyPU) of @Prop ${propSource} [${this.id__()}]` 4674 : `${this.debugInfoDecorator()} '${this.info()}'[${this.id__()}] <${this.debugInfoOwningView()}>`; 4675 } 4676 debugInfoOwningView() { 4677 return `${this.owningView_ ? this.owningView_.debugInfo__() : 'owning @Component UNKNOWN'}`; 4678 } 4679 // dump info about owning view and subscribers (PU ones only) 4680 // use function only for debug output and DFX. 4681 debugInfoSubscribers() { 4682 return (this.owningView_) 4683 ? `|--Owned by ${this.debugInfoOwningView()} ` 4684 : `|--Owned by: owning view not known`; 4685 } 4686 debugInfoSyncPeers() { 4687 if (!this.subscriberRefs_.size) { 4688 return '|--Sync peers: none'; 4689 } 4690 let result = `|--Sync peers: {`; 4691 let sepa = ''; 4692 this.subscriberRefs_.forEach((subscriber) => { 4693 if ('debugInfo' in subscriber) { 4694 result += `\n ${sepa}${subscriber.debugInfo()}`; 4695 sepa = ', '; 4696 } 4697 }); 4698 result += '\n }'; 4699 return result; 4700 } 4701 debugInfoDependentElmtIds(dumpDependantElements = false) { 4702 return this.dependentElmtIdsByProperty_.dumpInfoDependencies(this.owningView_, dumpDependantElements); 4703 } 4704 dumpDependentElmtIdsObj(isTrackedMode, isProfiler) { 4705 return this.dependentElmtIdsByProperty_.dumpInfoDependenciesObj(this.owningView_, isTrackedMode, isProfiler); 4706 } 4707 debugInfoElmtId(elmtId) { 4708 if (this.owningView_) { 4709 return this.owningView_.debugInfoElmtId(elmtId); 4710 } 4711 return '<unknown element id ' + elmtId + ', missing owning view>'; 4712 } 4713 debugInfoDependentComponents() { 4714 let result = `|--Dependent elements: `; 4715 let sepa = '; '; 4716 let sepaDiff = ''; 4717 const dumpDependantElements = true; 4718 let queue = [this]; 4719 let seen = new Set(); 4720 while (queue.length) { 4721 let item = queue.shift(); 4722 seen.add(item); 4723 if (item !== this) { 4724 result += `${sepa}${item.debugInfoOwningView()}`; 4725 } 4726 result += `${sepaDiff}${item.debugInfoDependentElmtIds(dumpDependantElements)}`; // new dependent elements 4727 sepaDiff = ', '; 4728 item.subscriberRefs_.forEach((subscriber) => { 4729 if ((subscriber instanceof ObservedPropertyAbstractPU)) { 4730 if (!seen.has(subscriber)) { 4731 queue.push(subscriber); 4732 } 4733 } 4734 }); 4735 } 4736 return result; 4737 } 4738 /**/ 4739 hasDependencies() { 4740 return this.dependentElmtIdsByProperty_.hasDependencies(); 4741 } 4742 /* for @Prop value from source we need to generate a @State 4743 that observes when this value changes. This ObservedPropertyPU 4744 sits inside SynchedPropertyOneWayPU. 4745 below methods invent a fake variable name for it 4746 */ 4747 getPropSourceObservedPropertyFakeName() { 4748 return `${this.info()}_prop_fake_state_source___`; 4749 } 4750 isPropSourceObservedPropertyFakeName() { 4751 return this.info() && this.info().endsWith('_prop_fake_state_source___') 4752 ? this.info().substring(0, this.info().length - '_prop_fake_state_source___'.length) 4753 : false; 4754 } 4755 getOwningView() { 4756 var _a, _b; 4757 return { componentName: (_a = this.owningView_) === null || _a === void 0 ? void 0 : _a.constructor.name, id: (_b = this.owningView_) === null || _b === void 0 ? void 0 : _b.id__() }; 4758 } 4759 dumpSyncPeers(isProfiler, changedTrackPropertyName) { 4760 let res = []; 4761 this.subscriberRefs_.forEach((subscriber) => { 4762 if ('debugInfo' in subscriber) { 4763 const observedProp = subscriber; 4764 res.push(stateMgmtDFX.getObservedPropertyInfo(observedProp, isProfiler, changedTrackPropertyName)); 4765 } 4766 }); 4767 return res; 4768 } 4769 onDumpProfiler(changedTrackPropertyName) { 4770 var _a, _b; 4771 let res = new DumpInfo(); 4772 res.viewInfo = { componentName: (_a = this.owningView_) === null || _a === void 0 ? void 0 : _a.constructor.name, id: (_b = this.owningView_) === null || _b === void 0 ? void 0 : _b.id__() }; 4773 res.observedPropertiesInfo.push(stateMgmtDFX.getObservedPropertyInfo(this, true, changedTrackPropertyName)); 4774 if (this.owningView_) { 4775 try { 4776 this.owningView_.sendStateInfo(JSON.stringify(res)); 4777 } 4778 catch (error) { 4779 stateMgmtConsole.applicationError(`${this.debugInfo()} has error in sendStateInfo: ${error.message}`); 4780 } 4781 } 4782 } 4783 /* 4784 Virtualized version of the subscription mechanism - add subscriber 4785 Overrides implementation in ObservedPropertyAbstract<T> 4786 */ 4787 addSubscriber(subscriber) { 4788 if (subscriber) { 4789 // ObservedPropertyAbstract will also add subscriber to 4790 // SubscriberManager map and to its own Set of subscribers as well 4791 // Something to improve in the future for PU path. 4792 // subscribeMe should accept IPropertySubscriber interface 4793 super.subscribeMe(subscriber); 4794 this.subscriberRefs_.add(subscriber); 4795 } 4796 } 4797 /* 4798 Virtualized version of the subscription mechanism - remove subscriber 4799 Overrides implementation in ObservedPropertyAbstract<T> 4800 */ 4801 removeSubscriber(subscriber, id) { 4802 if (subscriber) { 4803 this.subscriberRefs_.delete(subscriber); 4804 if (!id) { 4805 id = subscriber.id__(); 4806 } 4807 } 4808 super.unlinkSuscriber(id); 4809 } 4810 /** 4811 * put the property to delayed notification mode 4812 * feature is only used for @StorageLink/Prop, @LocalStorageLink/Prop 4813 */ 4814 enableDelayedNotification() { 4815 if (this.delayedNotification_ !== ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending) { 4816 4817 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_none_pending; 4818 } 4819 } 4820 /* 4821 when moving from inActive to active state the owning ViewPU calls this function 4822 This solution is faster than ViewPU polling each variable to send back a viewPropertyHasChanged event 4823 with the elmtIds 4824 4825 returns undefined if variable has _not_ changed 4826 returns dependentElementIds_ Set if changed. This Set is empty if variable is not used to construct the UI 4827 */ 4828 moveElmtIdsForDelayedUpdate(isReused = false) { 4829 const result = (this.delayedNotification_ === ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending) ? 4830 this.dependentElmtIdsByProperty_.getAllPropertyDependencies() : 4831 undefined; 4832 4833 if (isReused && !this.owningView_.isViewActive()) { 4834 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_none_pending; 4835 } 4836 else { 4837 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay; 4838 } 4839 return result; 4840 } 4841 notifyPropertyRead() { 4842 stateMgmtConsole.error(`${this.debugInfo()}: notifyPropertyRead, DO NOT USE with PU. Use notifyReadCb mechanism.`); 4843 } 4844 // notify owning ViewPU and peers of a variable assignment 4845 // also property/item changes to ObservedObjects of class object type, which use compat mode 4846 // Date and Array are notified as if there had been an assignment. 4847 notifyPropertyHasChangedPU() { 4848 4849 4850 if (this.owningView_) { 4851 if (this.delayedNotification_ === ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay) { 4852 // send viewPropertyHasChanged right away 4853 this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElmtIdsByProperty_.getAllPropertyDependencies()); 4854 // send changed observed property to profiler 4855 // only will be true when enable profiler 4856 if (stateMgmtDFX.enableProfiler) { 4857 4858 this.onDumpProfiler(); 4859 } 4860 } 4861 else { 4862 // mark this @StorageLink/Prop or @LocalStorageLink/Prop variable has having changed and notification of viewPropertyHasChanged delivery pending 4863 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending; 4864 } 4865 } 4866 this.subscriberRefs_.forEach((subscriber) => { 4867 if (subscriber && typeof subscriber === 'object' && 'syncPeerHasChanged' in subscriber) { 4868 subscriber.syncPeerHasChanged(this); 4869 } 4870 else { 4871 stateMgmtConsole.warn(`${this.debugInfo()}: notifyPropertyHasChangedPU: unknown subscriber ID 'subscribedId' error!`); 4872 } 4873 }); 4874 4875 } 4876 // notify owning ViewPU and peers of a ObservedObject @Track property's assignment 4877 notifyTrackedObjectPropertyHasChanged(changedPropertyName) { 4878 4879 4880 if (this.owningView_) { 4881 if (this.delayedNotification_ == ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay) { 4882 // send viewPropertyHasChanged right away 4883 this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElmtIdsByProperty_.getTrackedObjectPropertyDependencies(changedPropertyName, 'notifyTrackedObjectPropertyHasChanged')); 4884 // send changed observed property to profiler 4885 // only will be true when enable profiler 4886 if (stateMgmtDFX.enableProfiler) { 4887 4888 this.onDumpProfiler(changedPropertyName); 4889 } 4890 } 4891 else { 4892 // mark this @StorageLink/Prop or @LocalStorageLink/Prop variable has having changed and notification of viewPropertyHasChanged delivery pending 4893 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending; 4894 } 4895 } 4896 this.subscriberRefs_.forEach((subscriber) => { 4897 if (subscriber) { 4898 if ('syncPeerTrackedPropertyHasChanged' in subscriber) { 4899 subscriber.syncPeerTrackedPropertyHasChanged(this, changedPropertyName); 4900 } 4901 else { 4902 stateMgmtConsole.warn(`${this.debugInfo()}: notifyTrackedObjectPropertyHasChanged: unknown subscriber ID 'subscribedId' error!`); 4903 } 4904 } 4905 }); 4906 4907 } 4908 markDependentElementsDirty(view) { 4909 // TODO ace-ets2bundle, framework, complicated apps need to update together 4910 // this function will be removed after a short transition period. 4911 stateMgmtConsole.warn(`${this.debugInfo()}: markDependentElementsDirty no longer supported. App will work ok, but 4912 please update your ace-ets2bundle and recompile your application!`); 4913 } 4914 numberOfSubscrbers() { 4915 return this.subscriberRefs_.size + (this.owningView_ ? 1 : 0); 4916 } 4917 /* 4918 type checking for any supported type, as required for union type support 4919 see 1st parameter for explanation what is allowed 4920 4921 FIXME this expects the Map, Set patch to go in 4922 */ 4923 checkIsSupportedValue(value) { 4924 // FIXME enable the check when V1-V2 interoperability is forbidden 4925 // && !ObserveV2.IsProxiedObservedV2(value)) 4926 let res = ((typeof value === 'object' && typeof value !== 'function' && 4927 !ObserveV2.IsObservedObjectV2(value) && 4928 !ObserveV2.IsMakeObserved(value)) || 4929 typeof value === 'number' || 4930 typeof value === 'string' || 4931 typeof value === 'boolean' || 4932 value === undefined || 4933 value === null); 4934 if (!res) { 4935 errorReport.varValueCheckFailed({ 4936 customComponent: this.debugInfoOwningView(), 4937 variableDeco: this.debugInfoDecorator(), 4938 variableName: this.info(), 4939 expectedType: `undefined, null, number, boolean, string, or Object but not function, not V2 @ObservedV2 / @Trace class, and makeObserved return value either`, 4940 value: value 4941 }); 4942 } 4943 return res; 4944 } 4945 /* 4946 type checking for allowed Object type value 4947 see 1st parameter for explanation what is allowed 4948 4949 FIXME this expects the Map, Set patch to go in 4950 */ 4951 checkIsObject(value) { 4952 let res = ((typeof value === 'object' && typeof value !== 'function' && !ObserveV2.IsObservedObjectV2(value)) || 4953 value === undefined || value === null); 4954 if (!res) { 4955 errorReport.varValueCheckFailed({ 4956 customComponent: this.debugInfoOwningView(), 4957 variableDeco: this.debugInfoDecorator(), 4958 variableName: this.info(), 4959 expectedType: `undefined, null, Object including Array and instance of SubscribableAbstract and excluding function and V3 @observed/@track object`, 4960 value: value 4961 }); 4962 } 4963 return res; 4964 } 4965 /* 4966 type checking for allowed simple types value 4967 see 1st parameter for explanation what is allowed 4968 */ 4969 checkIsSimple(value) { 4970 let res = (value === undefined || typeof value === 'number' || typeof value === 'string' || typeof value === 'boolean'); 4971 if (!res) { 4972 errorReport.varValueCheckFailed({ 4973 customComponent: this.debugInfoOwningView(), 4974 variableDeco: this.debugInfoDecorator(), 4975 variableName: this.info(), 4976 expectedType: `undefined, number, boolean, string`, 4977 value: value 4978 }); 4979 } 4980 return res; 4981 } 4982 checkNewValue(isAllowedComment, newValue, validator) { 4983 if (validator(newValue)) { 4984 return true; 4985 } 4986 // report error 4987 // current implementation throws an Exception 4988 errorReport.varValueCheckFailed({ 4989 customComponent: this.debugInfoOwningView(), 4990 variableDeco: this.debugInfoDecorator(), 4991 variableName: this.info(), 4992 expectedType: isAllowedComment, 4993 value: newValue 4994 }); 4995 // never gets here if errorReport.varValueCheckFailed throws an exception 4996 // but should not depend on its implementation 4997 return false; 4998 } 4999 /** 5000 * factory function for concrete 'object' or 'simple' ObservedProperty object 5001 * depending if value is Class object 5002 * or simple type (boolean | number | string) 5003 * @param value 5004 * @param owningView 5005 * @param thisPropertyName 5006 * @returns either 5007 */ 5008 static CreateObservedObject(value, owningView, thisPropertyName) { 5009 return (typeof value === 'object') ? 5010 new ObservedPropertyObject(value, owningView, thisPropertyName) 5011 : new ObservedPropertySimple(value, owningView, thisPropertyName); 5012 } 5013 /** 5014 * If owning viewPU is currently rendering or re-rendering a UINode, return its elmtId 5015 * return notRecordingDependencies (-1) otherwise 5016 * ViewPU caches the info, it does not request the info from C++ side (by calling 5017 * ViewStackProcessor.GetElmtIdToAccountFor(); as done in earlier implementation 5018 */ 5019 getRenderingElmtId() { 5020 return (this.owningView_) ? this.owningView_.getCurrentlyRenderedElmtId() : UINodeRegisterProxy.notRecordingDependencies; 5021 } 5022 /** 5023 * during 'get' access recording take note of the created component and its elmtId 5024 * and add this component to the list of components who are dependent on this property 5025 */ 5026 recordPropertyDependentUpdate() { 5027 const elmtId = this.getRenderingElmtId(); 5028 if (elmtId === UINodeRegisterProxy.notRecordingDependencies) { 5029 // not access recording 5030 return; 5031 } 5032 if (elmtId === UINodeRegisterProxy.monitorIllegalV2V3StateAccess) { 5033 const error = `${this.debugInfo()}: recordPropertyDependentUpdate trying to use V2 state to init/update child V3 @Component. Application error`; 5034 stateMgmtConsole.applicationError(error); 5035 throw new TypeError(error); 5036 } 5037 5038 this.dependentElmtIdsByProperty_.addPropertyDependency(elmtId); 5039 } 5040 /** record dependency ObservedObject + propertyName -> elmtId 5041 * caller ensures renderingElmtId >= 0 5042 */ 5043 recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readTrackedPropertyName) { 5044 5045 this.dependentElmtIdsByProperty_.addTrackedObjectPropertyDependency(readTrackedPropertyName, renderingElmtId); 5046 } 5047 purgeDependencyOnElmtId(rmElmtId) { 5048 var _a; 5049 (_a = this.dependentElmtIdsByProperty_) === null || _a === void 0 ? void 0 : _a.purgeDependenciesForElmtId(rmElmtId); 5050 } 5051 SetPropertyUnchanged() { 5052 // function to be removed 5053 // keep it here until transpiler is updated. 5054 } 5055 // unified Appstorage, what classes to use, and the API 5056 createLink(subscribeOwner, linkPropName) { 5057 throw new Error(`${this.debugInfo()}: createLink: Can not create a AppStorage 'Link' from this property.`); 5058 } 5059 createProp(subscribeOwner, linkPropName) { 5060 throw new Error(`${this.debugInfo()}: createProp: Can not create a AppStorage 'Prop' from a @State property. `); 5061 } 5062 /* 5063 Below empty functions required to keep as long as this class derives from FU version 5064 ObservedPropertyAbstract. Need to overwrite these functions to do nothing for PU 5065 */ 5066 notifyHasChanged(_) { 5067 stateMgmtConsole.error(`${this.debugInfo()}: notifyHasChanged, DO NOT USE with PU. Use syncPeerHasChanged() \ 5068 or onTrackedObjectProperty(CompatMode)HasChangedPU()`); 5069 } 5070 /** 5071 * event emitted by wrapped ObservedObject, when one of its property values changes 5072 * for class objects when in compatibility mode 5073 * for Array, Date instances always 5074 * @param souceObject 5075 * @param changedPropertyName 5076 */ 5077 onTrackedObjectPropertyHasChangedPU(sourceObject, changedPropertyName) { 5078 5079 this.notifyTrackedObjectPropertyHasChanged(changedPropertyName); 5080 } 5081 /** 5082 * event emitted by wrapped ObservedObject, when one of its property values changes 5083 * for class objects when in compatibility mode 5084 * for Array, Date instances always 5085 * @param souceObject 5086 * @param changedPropertyName 5087 */ 5088 onTrackedObjectPropertyCompatModeHasChangedPU(sourceObject, changedPropertyName) { 5089 5090 this.notifyPropertyHasChangedPU(); 5091 } 5092 hasChanged(_) { 5093 // unused for PU 5094 // need to overwrite impl of base class with empty function. 5095 } 5096 propertyHasChanged(_) { 5097 // unused for PU 5098 // need to overwrite impl of base class with empty function. 5099 } 5100 propertyRead(_) { 5101 // unused for PU 5102 // need to overwrite impl of base class with empty function. 5103 } 5104} 5105ObservedPropertyAbstractPU.DelayedNotifyChangesEnum = (_a = class { 5106 }, 5107 _a.do_not_delay = 0, 5108 _a.delay_none_pending = 1, 5109 _a.delay_notification_pending = 2, 5110 _a); 5111class PropertyDependencies { 5112 constructor() { 5113 // dependencies for property -> elmtId 5114 // variable read during render adds elmtId 5115 // variable assignment causes elmtId to need re-render. 5116 // UINode with elmtId deletion needs elmtId to be removed from all records, see purgeDependenciesForElmtId 5117 this.propertyDependencies_ = new Set(); 5118 // dependencies on individual object properties 5119 this.trackedObjectPropertyDependencies_ = new Map(); 5120 } 5121 getAllPropertyDependencies() { 5122 5123 return this.propertyDependencies_; 5124 } 5125 addPropertyDependency(elmtId) { 5126 this.propertyDependencies_.add(elmtId); 5127 5128 } 5129 purgeDependenciesForElmtId(rmElmtId) { 5130 5131 this.propertyDependencies_.delete(rmElmtId); 5132 5133 this.trackedObjectPropertyDependencies_.forEach((propertyElmtId, propertyName) => { 5134 propertyElmtId.delete(rmElmtId); 5135 5136 }); 5137 } 5138 addTrackedObjectPropertyDependency(readProperty, elmtId) { 5139 let dependentElmtIds = this.trackedObjectPropertyDependencies_.get(readProperty); 5140 if (!dependentElmtIds) { 5141 dependentElmtIds = new Set(); 5142 this.trackedObjectPropertyDependencies_.set(readProperty, dependentElmtIds); 5143 } 5144 dependentElmtIds.add(elmtId); 5145 5146 } 5147 getTrackedObjectPropertyDependencies(changedObjectProperty, debugInfo) { 5148 const dependentElmtIds = this.trackedObjectPropertyDependencies_.get(changedObjectProperty) || new Set(); 5149 5150 return dependentElmtIds; 5151 } 5152 dumpInfoDependencies(owningView = undefined, dumpDependantElements) { 5153 const formatElmtId = owningView ? (elmtId => owningView.debugInfoElmtId(elmtId)) : (elmtId => elmtId); 5154 let result = ''; 5155 const arr = Array.from(this.propertyDependencies_).map(formatElmtId); 5156 if (dumpDependantElements) { 5157 return (arr.length > 1 ? arr.join(', ') : arr[0]); 5158 } 5159 if (!this.trackedObjectPropertyDependencies_.size) { 5160 result += `dependencies: variable assignment affects elmtIds: ${Array.from(this.propertyDependencies_).map(formatElmtId).join(', ')}`; 5161 return result; 5162 } 5163 this.trackedObjectPropertyDependencies_.forEach((propertyElmtId, propertyName) => { 5164 result += `dependencies: property '@Track ${propertyName}' change affects elmtIds: ${Array.from(propertyElmtId).map(formatElmtId).join(', ')}`; 5165 }); 5166 return result; 5167 } 5168 dumpInfoDependenciesObj(owningView = undefined, isTrackedMode, isProfiler) { 5169 const formatElmtId = owningView ? (elmtId => owningView.debugInfoElmtId(elmtId, isProfiler)) : (elmtId => elmtId); 5170 let trackedObjectPropertyDependenciesDumpInfo = new Map(); 5171 this.trackedObjectPropertyDependencies_.forEach((propertyElmtId, propertyName) => { 5172 trackedObjectPropertyDependenciesDumpInfo.set(propertyName, Array.from(propertyElmtId).map(formatElmtId)); 5173 }); 5174 let PropertyDependenciesInfo = { 5175 mode: isTrackedMode ? 'Track Mode' : 'Compatible Mode', 5176 trackPropertiesDependencies: MapInfo.toObject(trackedObjectPropertyDependenciesDumpInfo).keyToValue, 5177 propertyDependencies: Array.from(this.propertyDependencies_).map(formatElmtId), 5178 }; 5179 return PropertyDependenciesInfo; 5180 } 5181 hasDependencies() { 5182 return this.propertyDependencies_.size > 0 || this.trackedObjectPropertyDependencies_.size > 0; 5183 } 5184} 5185/* 5186 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 5187 * Licensed under the Apache License, Version 2.0 (the "License"); 5188 * you may not use this file except in compliance with the License. 5189 * You may obtain a copy of the License at 5190 * 5191 * http://www.apache.org/licenses/LICENSE-2.0 5192 * 5193 * Unless required by applicable law or agreed to in writing, software 5194 * distributed under the License is distributed on an "AS IS" BASIS, 5195 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5196 * See the License for the specific language governing permissions and 5197 * limitations under the License. 5198 */ 5199/** 5200 * ObservedPropertyObjectPU 5201 * implementation of @State and @Provide decorated variables of type class object 5202 * 5203 * all definitions in this file are framework internal 5204 * 5205 * class that holds an actual property value of type T 5206 * uses its base class to manage subscribers to this 5207 * property. 5208*/ 5209class ObservedPropertyPU extends ObservedPropertyAbstractPU { 5210 constructor(localInitValue, owningView, propertyName) { 5211 super(owningView, propertyName); 5212 this.setValueInternal(localInitValue); 5213 this.setDecoratorInfo('@State'); 5214 } 5215 aboutToBeDeleted(unsubscribeMe) { 5216 this.unsubscribeWrappedObject(); 5217 this.removeSubscriber(unsubscribeMe); 5218 super.aboutToBeDeleted(); 5219 } 5220 /** 5221 * Called by a SynchedPropertyObjectTwoWayPU (@Link, @Consume) that uses this as sync peer when it has changed 5222 * @param eventSource 5223 */ 5224 syncPeerHasChanged(eventSource) { 5225 5226 this.notifyPropertyHasChangedPU(); 5227 } 5228 syncPeerTrackedPropertyHasChanged(eventSource, changedTrackedObjectPropertyName) { 5229 5230 this.notifyTrackedObjectPropertyHasChanged(changedTrackedObjectPropertyName); 5231 } 5232 /** 5233 * Wrapped ObservedObjectPU has changed 5234 * @param souceObject 5235 * @param changedPropertyName 5236 */ 5237 objectPropertyHasChangedPU(souceObject, changedPropertyName) { 5238 5239 this.notifyPropertyHasChangedPU(); 5240 } 5241 unsubscribeWrappedObject() { 5242 if (this.wrappedValue_) { 5243 if (this.wrappedValue_ instanceof SubscribableAbstract) { 5244 this.wrappedValue_.removeOwningProperty(this); 5245 } 5246 else { 5247 ObservedObject.removeOwningProperty(this.wrappedValue_, this); 5248 // make sure the ObservedObject no longer has a read callback function 5249 // assigned to it 5250 ObservedObject.unregisterPropertyReadCb(this.wrappedValue_); 5251 } 5252 } 5253 } 5254 /* 5255 actually update this.wrappedValue_ 5256 called needs to do value change check 5257 and also notify with this.aboutToChange(); 5258 */ 5259 setValueInternal(newValue) { 5260 5261 if (newValue === this.wrappedValue_) { 5262 5263 5264 return false; 5265 } 5266 if (!this.checkIsSupportedValue(newValue)) { 5267 5268 return false; 5269 } 5270 this.unsubscribeWrappedObject(); 5271 if (!newValue || typeof newValue !== 'object') { 5272 // undefined, null, simple type: 5273 // nothing to subscribe to in case of new value undefined || null || simple type 5274 this.wrappedValue_ = newValue; 5275 } 5276 else if (newValue instanceof SubscribableAbstract) { 5277 5278 this.wrappedValue_ = newValue; 5279 this.wrappedValue_.addOwningProperty(this); 5280 } 5281 else if (ObservedObject.IsObservedObject(newValue)) { 5282 5283 ObservedObject.addOwningProperty(newValue, this); 5284 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(newValue); 5285 this.wrappedValue_ = newValue; 5286 } 5287 else { 5288 5289 this.wrappedValue_ = ObservedObject.createNew(newValue, this); 5290 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.wrappedValue_); 5291 } 5292 5293 return true; 5294 } 5295 get() { 5296 5297 5298 this.recordPropertyDependentUpdate(); 5299 if (this.shouldInstallTrackedObjectReadCb) { 5300 5301 ObservedObject.registerPropertyReadCb(this.wrappedValue_, this.onOptimisedObjectPropertyRead, this); 5302 } 5303 else { 5304 5305 } 5306 5307 return this.wrappedValue_; 5308 } 5309 getUnmonitored() { 5310 5311 // unmonitored get access , no call to notifyPropertyRead ! 5312 return this.wrappedValue_; 5313 } 5314 set(newValue) { 5315 if (this.wrappedValue_ === newValue) { 5316 5317 return; 5318 } 5319 5320 const oldValue = this.wrappedValue_; 5321 if (this.setValueInternal(newValue)) { 5322 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.wrappedValue_, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 5323 } 5324 } 5325 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 5326 5327 const renderingElmtId = this.getRenderingElmtId(); 5328 if (renderingElmtId >= 0) { 5329 if (!isTracked) { 5330 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 5331 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 5332 } 5333 else { 5334 5335 // only record dependency when 5336 // 1 - currently rendering or re-rendering 5337 // TODO room for further optimization: if not an expression in updateFunc, only first time render needs to record 5338 // because there can be change to depended variables unless one of the bindings is a JS expression 5339 // 2 - the changed ObservedObject is the wrapped object. The situation where it can be different is after a value assignment. 5340 if (this.getUnmonitored() === readObservedObject) { 5341 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 5342 } 5343 } 5344 } 5345 5346 } 5347} 5348// class definitions for backward compatibility 5349class ObservedPropertyObjectPU extends ObservedPropertyPU { 5350} 5351class ObservedPropertySimplePU extends ObservedPropertyPU { 5352} 5353/* 5354 * Copyright (c) 2022 Huawei Device Co., Ltd. 5355 * Licensed under the Apache License, Version 2.0 (the "License"); 5356 * you may not use this file except in compliance with the License. 5357 * You may obtain a copy of the License at 5358 * 5359 * http://www.apache.org/licenses/LICENSE-2.0 5360 * 5361 * Unless required by applicable law or agreed to in writing, software 5362 * distributed under the License is distributed on an "AS IS" BASIS, 5363 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5364 * See the License for the specific language governing permissions and 5365 * limitations under the License. 5366 */ 5367/** 5368 * SynchedPropertyObjectOneWayPU 5369 * implementation of @Prop decorated variables of type class object 5370 * 5371 * all definitions in this file are framework internal 5372 * 5373 */ 5374/** 5375 * Initialisation scenarios: 5376 * ------------------------- 5377 * 5378 * 1 - no local initialization, source provided (its ObservedObject value) 5379 * wrap the ObservedObject into an ObservedPropertyObjectPU 5380 * deep copy the ObservedObject into localCopyObservedObject_ 5381 * 5382 * 2 - local initialization, no source provided 5383 * app transpiled code calls set 5384 * leave source_ undefined 5385 * no deep copy needed, but provided local init might need wrapping inside an ObservedObject to set to 5386 * localCopyObservedObject_ 5387 * 5388 * 3 local initialization, source provided (its ObservedObject value) 5389 * current app transpiled code is not optional 5390 * sets source in constructor, as in case 1 5391 * calls set() to set the source value, but this will not deepcopy 5392 * 5393 * Update scenarios: 5394 * ----------------- 5395 * 5396 * 1- assignment of a new Object value: this.aProp = new ClassA() 5397 * rhs can be ObservedObject because of @Observed decoration or now 5398 * notifyPropertyHasChangedPU 5399 * 5400 * 2- local ObservedObject member property change 5401 * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored in localCopyObservedObject_ 5402 * no need to copy, notifyPropertyHasChangedPU 5403 * 5404 * 3- Rerender of the custom component triggered from the parent 5405 * reset() is called (code generated by the transpiler), set the value of source_ , if that causes a change will call syncPeerHasChanged 5406 * syncPeerHasChanged need to deep copy the ObservedObject from source to localCopyObservedObject_ 5407 * notifyPropertyHasChangedPU 5408 * 5409 * 4- source_ ObservedObject member property change 5410 * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored source_.getUnmonitored 5411 * notifyPropertyHasChangedPU 5412 */ 5413class SynchedPropertyOneWayPU extends ObservedPropertyAbstractPU { 5414 constructor(source, owningChildView, thisPropertyName) { 5415 super(owningChildView, thisPropertyName); 5416 this.setDecoratorInfo("@Prop"); 5417 if (source && (typeof (source) === 'object') && ('subscribeMe' in source)) { 5418 // code path for @(Local)StorageProp, the source is a ObservedPropertyObject<C> in a LocalStorage) 5419 this.source_ = source; 5420 this.sourceIsOwnObject = false; 5421 // subscribe to receive value change updates from LocalStorage source property 5422 this.source_.addSubscriber(this); 5423 } 5424 else { 5425 const sourceValue = source; 5426 if (this.checkIsSupportedValue(sourceValue)) { 5427 // code path for 5428 // 1- source is of same type C in parent, source is its value, not the backing store ObservedPropertyObject 5429 // 2- nested Object/Array inside observed another object/array in parent, source is its value 5430 5431 this.createSourceDependency(sourceValue); 5432 this.source_ = new ObservedPropertyObjectPU(sourceValue, this, this.getPropSourceObservedPropertyFakeName()); 5433 this.sourceIsOwnObject = true; 5434 } 5435 } 5436 if (this.source_ !== undefined) { 5437 this.resetLocalValue(this.source_.get(), /* needCopyObject */ true); 5438 } 5439 5440 } 5441 /* 5442 like a destructor, need to call this before deleting 5443 the property. 5444 */ 5445 aboutToBeDeleted() { 5446 if (this.source_) { 5447 this.source_.removeSubscriber(this); 5448 if (this.sourceIsOwnObject === true && this.source_.numberOfSubscrbers() === 0) { 5449 5450 this.source_.aboutToBeDeleted(); 5451 } 5452 this.source_ = undefined; 5453 } 5454 super.aboutToBeDeleted(); 5455 } 5456 // sync peer can be 5457 // 1. the embedded ObservedPropertyPU, followed by a reset when the owning ViewPU received a local update in parent 5458 // 2. a @Link or @Consume that uses this @Prop as a source. FIXME is this possible? - see the if (eventSource && this.source_ == eventSource) { 5459 syncPeerHasChanged(eventSource) { 5460 5461 if (this.source_ === undefined) { 5462 stateMgmtConsole.error(`${this.debugInfo()}: syncPeerHasChanged from peer ${eventSource && eventSource.debugInfo && eventSource.debugInfo()}. source_ undefined. Internal error.`); 5463 5464 return; 5465 } 5466 if (eventSource && this.source_ === eventSource) { 5467 // defensive programming: should always be the case! 5468 const newValue = this.source_.getUnmonitored(); 5469 if (this.checkIsSupportedValue(newValue)) { 5470 5471 if (this.resetLocalValue(newValue, /* needCopyObject */ true)) { 5472 this.notifyPropertyHasChangedPU(); 5473 } 5474 } 5475 } 5476 else { 5477 stateMgmtConsole.warn(`${this.debugInfo()}: syncPeerHasChanged: from peer '${eventSource === null || eventSource === void 0 ? void 0 : eventSource.debugInfo()}', Unexpected situation. syncPeerHasChanged from different sender than source_. Ignoring event.`); 5478 } 5479 5480 } 5481 syncPeerTrackedPropertyHasChanged(eventSource, changedPropertyName) { 5482 5483 if (this.source_ == undefined) { 5484 stateMgmtConsole.error(`${this.debugInfo()}: syncPeerTrackedPropertyHasChanged from peer ${eventSource && eventSource.debugInfo && eventSource.debugInfo()}. source_ undefined. Internal error.`); 5485 5486 return; 5487 } 5488 if (eventSource && this.source_ == eventSource) { 5489 // defensive programming: should always be the case! 5490 const newValue = this.source_.getUnmonitored(); 5491 if (this.checkIsSupportedValue(newValue)) { 5492 5493 if (this.resetLocalValue(newValue, /* needCopyObject */ true)) { 5494 this.notifyTrackedObjectPropertyHasChanged(changedPropertyName); 5495 } 5496 } 5497 } 5498 else { 5499 stateMgmtConsole.warn(`${this.debugInfo()}: syncPeerTrackedPropertyHasChanged: from peer '${eventSource === null || eventSource === void 0 ? void 0 : eventSource.debugInfo()}', Unexpected situation. syncPeerHasChanged from different sender than source_. Ignoring event.`); 5500 } 5501 5502 } 5503 getUnmonitored() { 5504 5505 // unmonitored get access , no call to notifyPropertyRead ! 5506 return this.localCopyObservedObject_; 5507 } 5508 get() { 5509 5510 5511 this.recordPropertyDependentUpdate(); 5512 if (this.shouldInstallTrackedObjectReadCb) { 5513 5514 ObservedObject.registerPropertyReadCb(this.localCopyObservedObject_, this.onOptimisedObjectPropertyRead, this); 5515 } 5516 else { 5517 5518 } 5519 5520 return this.localCopyObservedObject_; 5521 } 5522 // assignment to local variable in the form of this.aProp = <object value> 5523 set(newValue) { 5524 if (this.localCopyObservedObject_ === newValue) { 5525 5526 return; 5527 } 5528 5529 const oldValue = this.localCopyObservedObject_; 5530 if (this.resetLocalValue(newValue, /* needCopyObject */ false)) { 5531 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.localCopyObservedObject_, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 5532 } 5533 } 5534 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 5535 5536 const renderingElmtId = this.getRenderingElmtId(); 5537 if (renderingElmtId >= 0) { 5538 if (!isTracked) { 5539 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 5540 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 5541 } 5542 else { 5543 5544 if (this.getUnmonitored() === readObservedObject) { 5545 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 5546 } 5547 } 5548 } 5549 5550 } 5551 // called when updated from parent 5552 // during parent ViewPU rerender, calls update lambda of child ViewPU with @Prop variable 5553 // this lambda generated code calls ViewPU.updateStateVarsOfChildByElmtId, 5554 // calls inside app class updateStateVars() 5555 // calls reset() for each @Prop 5556 reset(sourceChangedValue) { 5557 5558 if (this.source_ !== undefined && this.checkIsSupportedValue(sourceChangedValue)) { 5559 // if this.source_.set causes an actual change, then, ObservedPropertyObject source_ will call syncPeerHasChanged method 5560 this.createSourceDependency(sourceChangedValue); 5561 this.source_.set(sourceChangedValue); 5562 } 5563 } 5564 createSourceDependency(sourceObject) { 5565 if (ObservedObject.IsObservedObject(sourceObject)) { 5566 5567 const fake = sourceObject[TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY]; 5568 } 5569 } 5570 /* 5571 unsubscribe from previous wrapped ObjectObject 5572 take a shallow or (TODO) deep copy 5573 copied Object might already be an ObservedObject (e.g. becurse of @Observed decorator) or might be raw 5574 Therefore, conditionally wrap the object, then subscribe 5575 return value true only if localCopyObservedObject_ has been changed 5576 */ 5577 resetLocalValue(newObservedObjectValue, needCopyObject) { 5578 // note: We can not test for newObservedObjectValue == this.localCopyObservedObject_ 5579 // here because the object might still be the same, but some property of it has changed 5580 // this is added for stability test: Target of target is not Object/is not callable/ 5581 // InstanceOf error when target is not Callable/Can not get Prototype on non ECMA Object 5582 try { 5583 if (!this.checkIsSupportedValue(newObservedObjectValue)) { 5584 return false; 5585 } 5586 // unsubscribe from old local copy 5587 if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { 5588 this.localCopyObservedObject_.removeOwningProperty(this); 5589 } 5590 else { 5591 ObservedObject.removeOwningProperty(this.localCopyObservedObject_, this); 5592 // make sure the ObservedObject no longer has a read callback function 5593 // assigned to it 5594 ObservedObject.unregisterPropertyReadCb(this.localCopyObservedObject_); 5595 } 5596 } 5597 catch (error) { 5598 stateMgmtConsole.error(`${this.debugInfo()}, an error occurred in resetLocalValue: ${error.message}`); 5599 ArkTools.print("resetLocalValue SubscribableAbstract", SubscribableAbstract); 5600 ArkTools.print("resetLocalValue ObservedObject", ObservedObject); 5601 ArkTools.print("resetLocalValue this", this); 5602 let a = Reflect.getPrototypeOf(this); 5603 ArkTools.print("resetLocalVale getPrototypeOf", a); 5604 throw error; 5605 } 5606 // shallow/deep copy value 5607 // needed whenever newObservedObjectValue comes from source 5608 // not needed on a local set (aka when called from set() method) 5609 if (needCopyObject) { 5610 ViewPU.pauseRendering(); 5611 this.localCopyObservedObject_ = this.copyObject(newObservedObjectValue, this.info_); 5612 ViewPU.restoreRendering(); 5613 } 5614 else { 5615 this.localCopyObservedObject_ = newObservedObjectValue; 5616 } 5617 if (typeof this.localCopyObservedObject_ === 'object') { 5618 if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { 5619 // deep copy will copy Set of subscribers as well. But local copy only has its own subscribers 5620 // not those of its parent value. 5621 this.localCopyObservedObject_.clearOwningProperties(); 5622 this.localCopyObservedObject_.addOwningProperty(this); 5623 } 5624 else if (ObservedObject.IsObservedObject(this.localCopyObservedObject_)) { 5625 // case: new ObservedObject 5626 ObservedObject.addOwningProperty(this.localCopyObservedObject_, this); 5627 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.localCopyObservedObject_); 5628 } 5629 else { 5630 // wrap newObservedObjectValue raw object as ObservedObject and subscribe to it 5631 5632 this.localCopyObservedObject_ = ObservedObject.createNew(this.localCopyObservedObject_, this); 5633 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.localCopyObservedObject_); 5634 } 5635 5636 } 5637 return true; 5638 } 5639 copyObject(value, propName) { 5640 // ViewStackProcessor.getApiVersion function is not present in API9 5641 // therefore shallowCopyObject will always be used in API version 9 and before 5642 // but the code in this file is the same regardless of API version 5643 5644 return ((typeof ViewStackProcessor['getApiVersion'] == 'function') && 5645 (ViewStackProcessor['getApiVersion']() >= 10)) 5646 ? this.deepCopyObject(value, propName) 5647 : this.shallowCopyObject(value, propName); 5648 } 5649 // API 9 code path 5650 shallowCopyObject(value, propName) { 5651 let rawValue = ObservedObject.GetRawObject(value); 5652 let copy; 5653 if (!rawValue || typeof rawValue !== 'object') { 5654 copy = rawValue; 5655 } 5656 else if (typeof rawValue != 'object') { 5657 // FIXME would it be better to throw Exception here? 5658 stateMgmtConsole.error(`${this.debugInfo()}: shallowCopyObject: request to copy non-object value, actual type is '${typeof rawValue}'. Internal error! Setting copy:=original value.`); 5659 copy = rawValue; 5660 } 5661 else if (rawValue instanceof Array) { 5662 // case Array inside ObservedObject 5663 copy = ObservedObject.createNew([...rawValue], this); 5664 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 5665 } 5666 else if (rawValue instanceof Date) { 5667 // case Date inside ObservedObject 5668 let d = new Date(); 5669 d.setTime(rawValue.getTime()); 5670 // subscribe, also Date gets wrapped / proxied by ObservedObject 5671 copy = ObservedObject.createNew(d, this); 5672 } 5673 else if (rawValue instanceof SubscribableAbstract) { 5674 // case SubscribableAbstract, no wrapping inside ObservedObject 5675 copy = Object.assign({}, rawValue); 5676 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 5677 if (copy instanceof SubscribableAbstract) { 5678 // subscribe 5679 copy.addOwningProperty(this); 5680 } 5681 } 5682 else if (typeof rawValue === 'object') { 5683 // case Object that is not Array, not Date, not SubscribableAbstract 5684 copy = ObservedObject.createNew(Object.assign({}, rawValue), this); 5685 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 5686 } 5687 else { 5688 // TODO in PR "F": change to exception throwing: 5689 stateMgmtConsole.error(`${this.debugInfo()}: shallow failed. Attempt to copy unsupported value of type '${typeof rawValue}' .`); 5690 copy = rawValue; 5691 } 5692 return copy; 5693 } 5694 // API 10 code path 5695 deepCopyObject(obj, variable) { 5696 let copy = SynchedPropertyObjectOneWayPU.deepCopyObjectInternal(obj, variable); 5697 // this subscribe to the top level object/array of the copy 5698 // same as shallowCopy does 5699 if ((obj instanceof SubscribableAbstract) && 5700 (copy instanceof SubscribableAbstract)) { 5701 copy.addOwningProperty(this); 5702 } 5703 else if (ObservedObject.IsObservedObject(obj) && ObservedObject.IsObservedObject(copy)) { 5704 ObservedObject.addOwningProperty(copy, this); 5705 } 5706 return copy; 5707 ; 5708 } 5709 // do not use this function from outside unless it is for testing purposes. 5710 static deepCopyObjectInternal(obj, variable) { 5711 if (!obj || typeof obj !== 'object') { 5712 return obj; 5713 } 5714 let copiedObjects = new Map(); 5715 return getDeepCopyOfObjectRecursive(obj); 5716 function getDeepCopyOfObjectRecursive(obj) { 5717 if (!obj || typeof obj !== 'object') { 5718 return obj; 5719 } 5720 const alreadyCopiedObject = copiedObjects.get(obj); 5721 if (alreadyCopiedObject) { 5722 5723 return alreadyCopiedObject; 5724 } 5725 let copy; 5726 if (obj instanceof Set) { 5727 copy = new Set(); 5728 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5729 copiedObjects.set(obj, copy); 5730 obj.forEach((setKey) => { 5731 copy.add(getDeepCopyOfObjectRecursive(setKey)); 5732 }); 5733 } 5734 else if (obj instanceof Map) { 5735 copy = new Map(); 5736 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5737 copiedObjects.set(obj, copy); 5738 obj.forEach((mapValue, mapKey) => { 5739 copy.set(mapKey, getDeepCopyOfObjectRecursive(mapValue)); 5740 }); 5741 } 5742 else if (obj instanceof Date) { 5743 copy = new Date(); 5744 copy.setTime(obj.getTime()); 5745 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5746 copiedObjects.set(obj, copy); 5747 } 5748 else if (obj instanceof Object) { 5749 copy = Array.isArray(obj) ? [] : {}; 5750 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5751 copiedObjects.set(obj, copy); 5752 } 5753 else { 5754 /** 5755 * As we define a variable called 'copy' with no initial value before this if/else branch, 5756 * so it will crash at Reflect.set when obj is not instance of Set/Map/Date/Object/Array. 5757 * This branch is for those known special cases: 5758 * 1、obj is a NativePointer 5759 * 2、obj is a @Sendable decorated class 5760 * In case the application crash directly, use shallow copy instead. 5761 * Will use new API when ark engine team is ready which will be a more elegant way. 5762 * If we difine the copy like 'let copy = {};', 5763 * it will not crash but copy will be a normal JSObject, not a @Sendable object. 5764 * To keep the functionality of @Sendable, still not define copy with initial value. 5765 */ 5766 stateMgmtConsole.warn('DeepCopy target obj is not instance of Set/Date/Map/Object/Array, will use shallow copy instead.'); 5767 return obj; 5768 } 5769 Object.keys(obj).forEach((objKey) => { 5770 copy[objKey] = getDeepCopyOfObjectRecursive(obj[objKey]); 5771 }); 5772 return ObservedObject.IsObservedObject(obj) ? ObservedObject.createNew(copy, undefined) : copy; 5773 } 5774 } 5775} 5776// class definitions for backward compatibility 5777class SynchedPropertySimpleOneWayPU extends SynchedPropertyOneWayPU { 5778} 5779class SynchedPropertyObjectOneWayPU extends SynchedPropertyOneWayPU { 5780} 5781/* 5782 * Copyright (c) 2022 Huawei Device Co., Ltd. 5783 * Licensed under the Apache License, Version 2.0 (the "License"); 5784 * you may not use this file except in compliance with the License. 5785 * You may obtain a copy of the License at 5786 * 5787 * http://www.apache.org/licenses/LICENSE-2.0 5788 * 5789 * Unless required by applicable law or agreed to in writing, software 5790 * distributed under the License is distributed on an "AS IS" BASIS, 5791 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5792 * See the License for the specific language governing permissions and 5793 * limitations under the License. 5794 */ 5795/** 5796 * SynchedPropertyObjectTwoWayPU 5797 * implementation of @Link and @Consume decorated variables of type class object 5798 * 5799 * all definitions in this file are framework internal 5800*/ 5801class SynchedPropertyTwoWayPU extends ObservedPropertyAbstractPU { 5802 constructor(source, owningChildView, thisPropertyName) { 5803 super(owningChildView, thisPropertyName); 5804 this.source_ = source; 5805 if (this.source_) { 5806 // register to the parent property 5807 this.source_.addSubscriber(this); 5808 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.source_.getUnmonitored()); 5809 } 5810 else { 5811 throw new SyntaxError(`${this.debugInfo()}: constructor: source variable in parent/ancestor @Component must be defined. Application error!`); 5812 } 5813 this.setDecoratorInfo("@Link"); 5814 } 5815 /* 5816 like a destructor, need to call this before deleting 5817 the property. 5818 */ 5819 aboutToBeDeleted() { 5820 // unregister from parent of this link 5821 if (this.source_) { 5822 this.source_.removeSubscriber(this); 5823 // unregister from the ObservedObject 5824 ObservedObject.removeOwningProperty(this.source_.getUnmonitored(), this); 5825 } 5826 super.aboutToBeDeleted(); 5827 } 5828 isStorageLinkProp() { 5829 return (this.source_ && this.source_ instanceof ObservedPropertyAbstract && (!(this.source_ instanceof ObservedPropertyAbstractPU))); 5830 } 5831 setObject(newValue) { 5832 if (!this.source_) { 5833 throw new SyntaxError(`${this.debugInfo()}: setObject (assign a new value), no source variable in parent/ancestor \ 5834 @Component. Application error.`); 5835 } 5836 if (this.getUnmonitored() === newValue) { 5837 5838 return; 5839 } 5840 5841 if (this.checkIsSupportedValue(newValue)) { 5842 // the source_ ObservedProperty will call: this.syncPeerHasChanged(newValue); 5843 this.source_.set(newValue); 5844 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(newValue); 5845 } 5846 } 5847 /** 5848 * Called when sync peer ObservedPropertyObject or SynchedPropertyObjectTwoWay has changed value 5849 * that peer can be in either parent or child component if 'this' is used for a @Link 5850 * that peer can be in either ancestor or descendant component if 'this' is used for a @Consume 5851 * @param eventSource 5852 */ 5853 syncPeerHasChanged(eventSource) { 5854 5855 if (!this.changeNotificationIsOngoing_) { 5856 5857 this.notifyPropertyHasChangedPU(); 5858 } 5859 5860 } 5861 syncPeerTrackedPropertyHasChanged(eventSource, changedTrackedObjectPropertyName) { 5862 5863 if (!this.changeNotificationIsOngoing_) { 5864 5865 this.notifyTrackedObjectPropertyHasChanged(changedTrackedObjectPropertyName); 5866 } 5867 5868 } 5869 getUnmonitored() { 5870 5871 return (this.source_ ? this.source_.getUnmonitored() : undefined); 5872 } 5873 // get 'read through` from the ObservedProperty 5874 get() { 5875 5876 5877 this.recordPropertyDependentUpdate(); 5878 const result = this.getUnmonitored(); 5879 if (this.shouldInstallTrackedObjectReadCb) { 5880 5881 ObservedObject.registerPropertyReadCb(result, this.onOptimisedObjectPropertyRead, this); 5882 } 5883 else { 5884 5885 } 5886 5887 return result; 5888 } 5889 // set 'writes through` to the ObservedProperty 5890 set(newValue) { 5891 5892 if (this.getUnmonitored() === newValue) { 5893 5894 5895 return; 5896 } 5897 5898 // avoid circular notifications @Link -> source @State -> other but also back to same @Link 5899 this.changeNotificationIsOngoing_ = true; 5900 let oldValue = this.getUnmonitored(); 5901 this.setObject(newValue); 5902 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ newValue, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 5903 this.changeNotificationIsOngoing_ = false; 5904 5905 } 5906 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 5907 5908 const renderingElmtId = this.getRenderingElmtId(); 5909 if (renderingElmtId >= 0) { 5910 if (!isTracked) { 5911 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 5912 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 5913 } 5914 else { 5915 5916 if (this.getUnmonitored() === readObservedObject) { 5917 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 5918 } 5919 } 5920 } 5921 5922 } 5923} 5924// class definitions for backward compatibility 5925class SynchedPropertyObjectTwoWayPU extends SynchedPropertyTwoWayPU { 5926} 5927class SynchedPropertySimpleTwoWayPU extends SynchedPropertyTwoWayPU { 5928} 5929/* 5930 * Copyright (c) 2022 Huawei Device Co., Ltd. 5931 * Licensed under the Apache License, Version 2.0 (the "License"); 5932 * you may not use this file except in compliance with the License. 5933 * You may obtain a copy of the License at 5934 * 5935 * http://www.apache.org/licenses/LICENSE-2.0 5936 * 5937 * Unless required by applicable law or agreed to in writing, software 5938 * distributed under the License is distributed on an "AS IS" BASIS, 5939 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5940 * See the License for the specific language governing permissions and 5941 * limitations under the License. 5942 */ 5943/** 5944 * SynchedPropertyNestedObjectPU 5945 * implementation of @ObjectLink decorated variables 5946 * 5947 * all definitions in this file are framework internal 5948 * 5949 */ 5950class SynchedPropertyNestedObjectPU extends ObservedPropertyAbstractPU { 5951 /** 5952 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 5953 * example 5954 * this.b.$a with b of type PC and a of type C, or 5955 * this.$b[5] with this.b of type PC and array item b[5] of type C; 5956 * 5957 * @param subscribeMe 5958 * @param propName 5959 */ 5960 constructor(obsObject, owningChildView, propertyName) { 5961 super(owningChildView, propertyName); 5962 this.obsObject_ = undefined; 5963 this.createSourceDependency(obsObject); 5964 this.setValueInternal(obsObject); 5965 this.setDecoratorInfo("@ObjectLink"); 5966 } 5967 /* 5968 like a destructor, need to call this before deleting 5969 the property. 5970 */ 5971 aboutToBeDeleted() { 5972 // unregister from the ObservedObject 5973 ObservedObject.removeOwningProperty(this.obsObject_, this); 5974 super.aboutToBeDeleted(); 5975 } 5976 getUnmonitored() { 5977 5978 // unmonitored get access , no call to notifyPropertyRead ! 5979 return this.obsObject_; 5980 } 5981 // get 'read through` from the ObservedProperty 5982 get() { 5983 5984 5985 this.recordPropertyDependentUpdate(); 5986 if (this.shouldInstallTrackedObjectReadCb) { 5987 5988 ObservedObject.registerPropertyReadCb(this.obsObject_, this.onOptimisedObjectPropertyRead, this); 5989 } 5990 else { 5991 5992 } 5993 5994 return this.obsObject_; 5995 } 5996 // parent ViewPU rerender, runs update lambda with child ViewPU that contains a @ObjectLink 5997 // calls ViewPU.updateStateVarsByElmtId, calls updateStateVars in application class, calls this 'set' function 5998 set(newValue) { 5999 if (this.obsObject_ === newValue) { 6000 6001 return; 6002 } 6003 6004 const oldValue = this.obsObject_; 6005 if (this.setValueInternal(newValue)) { 6006 this.createSourceDependency(newValue); 6007 // notify value change to subscribing View 6008 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.obsObject_, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 6009 } 6010 } 6011 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 6012 6013 const renderingElmtId = this.getRenderingElmtId(); 6014 if (renderingElmtId >= 0) { 6015 if (!isTracked) { 6016 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 6017 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 6018 } 6019 else { 6020 6021 if (this.getUnmonitored() === readObservedObject) { 6022 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 6023 } 6024 } 6025 } 6026 6027 } 6028 createSourceDependency(sourceObject) { 6029 if (ObservedObject.IsObservedObject(sourceObject)) { 6030 6031 const fake = sourceObject[TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY]; 6032 } 6033 } 6034 setValueInternal(newValue) { 6035 if (!this.checkIsObject(newValue)) { 6036 return false; 6037 } 6038 if (this.obsObject_ != undefined) { 6039 if (this.obsObject_ instanceof SubscribableAbstract) { 6040 // unregister from SubscribableAbstract object 6041 this.obsObject_.removeOwningProperty(this); 6042 } 6043 else if (ObservedObject.IsObservedObject(this.obsObject_)) { 6044 // unregister from the ObservedObject 6045 ObservedObject.removeOwningProperty(this.obsObject_, this); 6046 // make sure the ObservedObject no longer has a read callback function 6047 // assigned to it 6048 ObservedObject.unregisterPropertyReadCb(this.obsObject_); 6049 } 6050 } 6051 this.obsObject_ = newValue; 6052 if (this.obsObject_ != undefined) { 6053 if (this.obsObject_ instanceof SubscribableAbstract) { 6054 // register to SubscribableAbstract object 6055 this.obsObject_.addOwningProperty(this); 6056 } 6057 else if (ObservedObject.IsObservedObject(this.obsObject_)) { 6058 // register to the ObservedObject 6059 ObservedObject.addOwningProperty(this.obsObject_, this); 6060 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.obsObject_); 6061 } 6062 else { 6063 stateMgmtConsole.applicationWarn(`${this.debugInfo()}: set/init (method setValueInternal): assigned value is not 6064 be decorated by @Observed. Value changes will not be observed and UI will not update.`); 6065 } 6066 } 6067 return true; 6068 } 6069} 6070/** backward compatibility after typo in classname fix */ 6071class SynchedPropertyNesedObjectPU extends SynchedPropertyNestedObjectPU { 6072} 6073/* 6074 * Copyright (c) 2023 Huawei Device Co., Ltd. 6075 * Licensed under the Apache License, Version 2.0 (the "License"); 6076 * you may not use this file except in compliance with the License. 6077 * You may obtain a copy of the License at 6078 * 6079 * http://www.apache.org/licenses/LICENSE-2.0 6080 * 6081 * Unless required by applicable law or agreed to in writing, software 6082 * distributed under the License is distributed on an "AS IS" BASIS, 6083 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6084 * See the License for the specific language governing permissions and 6085 * limitations under the License. 6086 */ 6087// defined a globle function to clean up the removeItems when idle 6088function uiNodeCleanUpIdleTask() { 6089 6090 UINodeRegisterProxy.obtainDeletedElmtIds(); 6091 UINodeRegisterProxy.unregisterElmtIdsFromIViews(); 6092} 6093class UINodeRegisterProxy { 6094 constructor() { 6095 this.removeElementsInfo_ = new Array(); 6096 } 6097 static obtainDeletedElmtIds() { 6098 6099 if ((!UINodeRegisterProxy.instance_.obtainDeletedElmtIds) || typeof UINodeRegisterProxy.instance_.obtainDeletedElmtIds !== 'function') { 6100 stateMgmtConsole.error(`UINodeRegisterProxy obtainDeletedElmtIds is not a function: ${UINodeRegisterProxy.instance_.obtainDeletedElmtIds}.`); 6101 } 6102 else { 6103 UINodeRegisterProxy.instance_.obtainDeletedElmtIds(); 6104 } 6105 } 6106 // FIXME unregisterElmtIdsFromIViews needs adaptation 6107 static unregisterElmtIdsFromIViews() { 6108 6109 UINodeRegisterProxy.instance_.unregisterElmtIdsFromIViews(); 6110 } 6111 // unregisters all the received removedElements in func parameter 6112 static unregisterRemovedElmtsFromViewPUs(removedElements) { 6113 6114 UINodeRegisterProxy.instance_.populateRemoveElementInfo(removedElements); 6115 UINodeRegisterProxy.instance_.unregisterElmtIdsFromIViews(); 6116 } 6117 static registerModifierElmtDeleteCallback(callback) { 6118 if (UINodeRegisterProxy.modifierElmtDeleteCallback_) { 6119 return; 6120 } 6121 UINodeRegisterProxy.modifierElmtDeleteCallback_ = callback; 6122 } 6123 populateRemoveElementInfo(removedElements) { 6124 for (const elmtId of removedElements) { 6125 this.removeElementsInfo_.push(elmtId); 6126 } 6127 } 6128 /* just get the remove items from the native side 6129 */ 6130 obtainDeletedElmtIds() { 6131 6132 let removedElementsInfo = new Array(); 6133 ViewStackProcessor.moveDeletedElmtIds(removedElementsInfo); 6134 6135 this.removeElementsInfo_ = removedElementsInfo; 6136 } 6137 unregisterElmtIdsFromIViews() { 6138 6139 if (this.removeElementsInfo_.length === 0) { 6140 6141 return; 6142 } 6143 let owningView; 6144 this.removeElementsInfo_.forEach((elmtId) => { 6145 const owningViewPUWeak = UINodeRegisterProxy.ElementIdToOwningViewPU_.get(elmtId); 6146 if (owningViewPUWeak !== undefined) { 6147 owningView = owningViewPUWeak.deref(); 6148 if (owningView) { 6149 owningView.purgeDeleteElmtId(elmtId); 6150 } 6151 else { 6152 6153 } 6154 if (UINodeRegisterProxy.modifierElmtDeleteCallback_) { 6155 UINodeRegisterProxy.modifierElmtDeleteCallback_(elmtId); 6156 } 6157 } 6158 else { 6159 6160 } 6161 // FIXME: only do this if app uses V3 6162 ObserveV2.getObserve().clearBinding(elmtId); 6163 }); 6164 this.removeElementsInfo_.length = 0; 6165 } 6166 static cleanUpDeadReferences() { 6167 6168 ObserveV2.getObserve().cleanUpDeadReferences(); 6169 } 6170} 6171UINodeRegisterProxy.notRecordingDependencies = -1; 6172UINodeRegisterProxy.monitorIllegalV2V3StateAccess = -2; 6173UINodeRegisterProxy.instance_ = new UINodeRegisterProxy(); 6174UINodeRegisterProxy.ElementIdToOwningViewPU_ = new Map(); 6175/* 6176 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 6177 * Licensed under the Apache License, Version 2.0 (the "License"); 6178 * you may not use this file except in compliance with the License. 6179 * You may obtain a copy of the License at 6180 * 6181 * http://www.apache.org/licenses/LICENSE-2.0 6182 * 6183 * Unless required by applicable law or agreed to in writing, software 6184 * distributed under the License is distributed on an "AS IS" BASIS, 6185 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6186 * See the License for the specific language governing permissions and 6187 * limitations under the License. 6188 * 6189 * * ViewPU - View for Partial Update 6190 * 6191* all definitions in this file are framework internal 6192*/ 6193class ViewPU extends PUV2ViewBase { 6194 /** 6195 * Create a View 6196 * 6197 * 1. option: top level View, specify 6198 * - compilerAssignedUniqueChildId must specify 6199 * - parent=undefined 6200 * - localStorage must provide if @LocalSTorageLink/Prop variables are used 6201 * in this View or descendant Views. 6202 * 6203 * 2. option: not a top level View 6204 * - compilerAssignedUniqueChildId must specify 6205 * - parent must specify 6206 * - localStorage do not specify, will inherit from parent View. 6207 * 6208 */ 6209 constructor(parent, localStorage, elmtId = UINodeRegisterProxy.notRecordingDependencies, extraInfo = undefined) { 6210 var _a; 6211 super(parent, elmtId, extraInfo); 6212 // flag for initial rendering or re-render on-going. 6213 this.isRenderInProgress = false; 6214 // flag for initial rendering being done 6215 this.isInitialRenderDone = false; 6216 this.runReuse_ = false; 6217 this.watchedProps = new Map(); 6218 this.recycleManager_ = undefined; 6219 this.hasBeenRecycled_ = false; 6220 this.preventRecursiveRecycle_ = false; 6221 this.delayRecycleNodeRerender = false; 6222 this.delayRecycleNodeRerenderDeep = false; 6223 // @Provide'd variables by this class and its ancestors 6224 this.providedVars_ = new Map(); 6225 // my LocalStorage instance, shared with ancestor Views. 6226 // create a default instance on demand if none is initialized 6227 this.localStoragebackStore_ = undefined; 6228 /** 6229 * on first render create a new Instance of Repeat 6230 * on re-render connect to existing instance 6231 * @param arr 6232 * @returns 6233 */ 6234 this.__mkRepeatAPI = (arr) => { 6235 // factory is for future extensions, currently always return the same 6236 const elmtId = this.getCurrentlyRenderedElmtId(); 6237 let repeat = this.elmtId2Repeat_.get(elmtId); 6238 if (!repeat) { 6239 repeat = new __Repeat(this, arr); 6240 this.elmtId2Repeat_.set(elmtId, repeat); 6241 } 6242 else { 6243 repeat.updateArr(arr); 6244 } 6245 return repeat; 6246 }; 6247 // if set use the elmtId also as the ViewPU object's subscribable id. 6248 // these matching is requirement for updateChildViewById(elmtId) being able to 6249 // find the child ViewPU object by given elmtId 6250 //this.id_ = elmtId == UINodeRegisterProxy.notRecordingDependencies ? SubscriberManager.MakeId() : elmtId; 6251 this.localStoragebackStore_ = undefined; 6252 6253 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onViewPUCreate(this); 6254 if (localStorage) { 6255 this.localStorage_ = localStorage; 6256 6257 } 6258 SubscriberManager.Add(this); 6259 6260 } 6261 get ownObservedPropertiesStore_() { 6262 if (!this.ownObservedPropertiesStore__) { 6263 // lazy init 6264 this.ownObservedPropertiesStore__ = new Set(); 6265 this.obtainOwnObservedProperties(); 6266 } 6267 return this.ownObservedPropertiesStore__; 6268 } 6269 obtainOwnObservedProperties() { 6270 let usesStateMgmtVersion = 0; 6271 Object.getOwnPropertyNames(this) 6272 .filter((propName) => { 6273 // do not include backing store, and ObserveV2/MonitorV2/ComputedV2 meta data objects 6274 return (propName.startsWith('__') && 6275 !propName.startsWith(ObserveV2.OB_PREFIX) && 6276 !propName.startsWith(MonitorV2.WATCH_PREFIX) && 6277 !propName.startsWith(ComputedV2.COMPUTED_PREFIX)); 6278 }) 6279 .forEach((propName) => { 6280 const stateVar = Reflect.get(this, propName); 6281 if (stateVar && typeof stateVar === 'object' && 'notifyPropertyHasChangedPU' in stateVar) { 6282 6283 this.ownObservedPropertiesStore_.add(stateVar); 6284 usesStateMgmtVersion = 2; 6285 } 6286 else { 6287 6288 } 6289 }); 6290 if (this.isViewV3 === true) { 6291 if (usesStateMgmtVersion === 2) { 6292 const error = `${this.debugInfo__()}: mixed use of stateMgmt V2 and V3 variable decorators. Application error!`; 6293 stateMgmtConsole.applicationError(error); 6294 throw new Error(error); 6295 } 6296 } 6297 6298 } 6299 get localStorage_() { 6300 if (!this.localStoragebackStore_ && this.getParent()) { 6301 6302 this.localStoragebackStore_ = this.getParent().localStorage_; 6303 } 6304 if (!this.localStoragebackStore_) { 6305 6306 this.localStoragebackStore_ = new LocalStorage({ /* empty */}); 6307 } 6308 return this.localStoragebackStore_; 6309 } 6310 set localStorage_(instance) { 6311 if (!instance) { 6312 // setting to undefined not allowed 6313 return; 6314 } 6315 if (this.localStoragebackStore_) { 6316 stateMgmtConsole.applicationError(`${this.debugInfo__()}: constructor: is setting LocalStorage instance twice. Application error.`); 6317 } 6318 this.localStoragebackStore_ = instance; 6319 } 6320 // FIXME 6321 // indicate if this is V2 or a V3 component 6322 // V2 by default, changed to V3 by the first V3 decorated variable 6323 // when splitting ViewPU and ViewV3 6324 // use instanceOf. Until then, this is a workaround. 6325 // @state, @track, etc V3 decorator functions modify isViewV3 to return true 6326 // (decorator can modify functions in prototype) 6327 // FIXME 6328 get isViewV3() { 6329 return false; 6330 } 6331 onGlobalThemeChanged() { 6332 this.onWillApplyThemeInternally(); 6333 this.forceCompleteRerender(false); 6334 this.childrenWeakrefMap_.forEach((weakRefChild) => { 6335 const child = weakRefChild.deref(); 6336 if (child) { 6337 child.onGlobalThemeChanged(); 6338 } 6339 }); 6340 } 6341 aboutToReuse(params) { } 6342 aboutToRecycle() { } 6343 onWillApplyThemeInternally() { 6344 var _a; 6345 const theme = (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.getFinalTheme(this.id__()); 6346 if (theme) { 6347 this.onWillApplyTheme(theme); 6348 } 6349 } 6350 onWillApplyTheme(theme) { } 6351 // super class will call this function from 6352 // its aboutToBeDeleted implementation 6353 aboutToBeDeletedInternal() { 6354 var _a; 6355 6356 // if this isDeleting_ is true already, it may be set delete status recursively by its parent, so it is not necessary 6357 // to set and recursively set its children any more 6358 if (!this.isDeleting_) { 6359 this.isDeleting_ = true; 6360 this.setDeleteStatusRecursively(); 6361 } 6362 // tell UINodeRegisterProxy that all elmtIds under 6363 // this ViewPU should be treated as already unregistered 6364 6365 // purge the elmtIds owned by this viewPU from the updateFuncByElmtId and also the state variable dependent elmtIds 6366 Array.from(this.updateFuncByElmtId.keys()).forEach((elmtId) => { 6367 this.purgeDeleteElmtId(elmtId); 6368 }); 6369 if (this.hasRecycleManager()) { 6370 this.getRecycleManager().purgeAllCachedRecycleNode(); 6371 } 6372 // un-registration of ElementIDs 6373 6374 // it will unregister removed elmtIds from all ViewPu, equals purgeDeletedElmtIdsRecursively 6375 this.purgeDeletedElmtIds(); 6376 // un-registers its own id once its children are unregistered above 6377 //FIXME: Uncomment once photos app avoids rerendering of removed elementIds 6378 //UINodeRegisterProxy unregisterRemovedElmtsFromViewPUs([this id__()]); 6379 6380 // in case this ViewPU is currently frozen 6381 PUV2ViewBase.inactiveComponents_.delete(`${this.constructor.name}[${this.id__()}]`); 6382 // FIXME needed ? 6383 MonitorV2.clearWatchesFromTarget(this); 6384 this.updateFuncByElmtId.clear(); 6385 this.watchedProps.clear(); 6386 this.providedVars_.clear(); 6387 if (this.ownObservedPropertiesStore__) { 6388 this.ownObservedPropertiesStore__.clear(); 6389 } 6390 if (this.getParent()) { 6391 this.getParent().removeChild(this); 6392 } 6393 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onViewPUDelete(this); 6394 this.localStoragebackStore_ = undefined; 6395 } 6396 purgeDeleteElmtId(rmElmtId) { 6397 6398 const result = this.updateFuncByElmtId.delete(rmElmtId); 6399 if (result) { 6400 this.purgeVariableDependenciesOnElmtIdOwnFunc(rmElmtId); 6401 // it means rmElmtId has finished all the unregistration from the js side, ElementIdToOwningViewPU_ does not need to keep it 6402 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(rmElmtId); 6403 } 6404 return result; 6405 } 6406 purgeVariableDependenciesOnElmtIdOwnFunc(elmtId) { 6407 this.ownObservedPropertiesStore_.forEach((stateVar) => { 6408 stateVar.purgeDependencyOnElmtId(elmtId); 6409 }); 6410 } 6411 debugInfoStateVars() { 6412 let result = `|--${this.constructor.name}[${this.id__()}]`; 6413 Object.getOwnPropertyNames(this) 6414 .filter((varName) => varName.startsWith('__') && !varName.startsWith(ObserveV2.OB_PREFIX)) 6415 .forEach((varName) => { 6416 const prop = Reflect.get(this, varName); 6417 if ('debugInfoDecorator' in prop) { 6418 const observedProp = prop; 6419 result += `\n ${observedProp.debugInfoDecorator()} '${observedProp.info()}'[${observedProp.id__()}]`; 6420 result += `\n ${observedProp.debugInfoSubscribers()}`; 6421 result += `\n ${observedProp.debugInfoSyncPeers()}`; 6422 result += `\n ${observedProp.debugInfoDependentElmtIds()}`; 6423 result += `\n ${observedProp.debugInfoDependentComponents()}`; 6424 } 6425 }); 6426 return result; 6427 } 6428 /** 6429 * Indicate if this @Component is allowed to freeze by calling with freezeState=true 6430 * Called with value of the @Component decorator 'freezeWhenInactive' parameter 6431 * or depending how UI compiler works also with 'undefined' 6432 * @param freezeState only value 'true' will be used, otherwise inherits from parent 6433 * if not parent, set to false. 6434 */ 6435 initAllowComponentFreeze(freezeState) { 6436 // set to true if freeze parameter set for this @Component to true 6437 // otherwise inherit from parent @Component (if it exists). 6438 this.isCompFreezeAllowed_ = freezeState || this.isCompFreezeAllowed_; 6439 6440 } 6441 /** 6442 * ArkUI engine will call this function when the corresponding CustomNode's active status change. 6443 * ArkUI engine will not recurse children nodes to inform the stateMgmt for the performance reason. 6444 * So the stateMgmt needs to recurse the children although the isCompFreezeAllowed is false because the children nodes 6445 * may enable the freezeWhenInActive. 6446 * @param active true for active, false for inactive 6447 */ 6448 setActiveInternal(active) { 6449 6450 if (this.isCompFreezeAllowed()) { 6451 this.isActive_ = active; 6452 if (this.isActive_) { 6453 this.onActiveInternal(); 6454 } 6455 else { 6456 this.onInactiveInternal(); 6457 } 6458 } 6459 for (const child of this.childrenWeakrefMap_.values()) { 6460 const childView = child.deref(); 6461 if (childView) { 6462 childView.setActiveInternal(active); 6463 } 6464 } 6465 6466 } 6467 onActiveInternal() { 6468 if (!this.isActive_) { 6469 return; 6470 } 6471 6472 this.performDelayedUpdate(); 6473 // Remove the active component from the Map for Dfx 6474 ViewPU.inactiveComponents_.delete(`${this.constructor.name}[${this.id__()}]`); 6475 } 6476 onInactiveInternal() { 6477 if (this.isActive_) { 6478 return; 6479 } 6480 6481 for (const stateLinkProp of this.ownObservedPropertiesStore_) { 6482 stateLinkProp.enableDelayedNotification(); 6483 } 6484 // Add the inactive Components to Map for Dfx listing 6485 ViewPU.inactiveComponents_.add(`${this.constructor.name}[${this.id__()}]`); 6486 } 6487 initialRenderView() { 6488 6489 this.onWillApplyThemeInternally(); 6490 this.obtainOwnObservedProperties(); 6491 this.isRenderInProgress = true; 6492 this.initialRender(); 6493 this.isRenderInProgress = false; 6494 this.isInitialRenderDone = true; 6495 6496 } 6497 UpdateElement(elmtId) { 6498 6499 if (elmtId === this.id__()) { 6500 // do not attempt to update itself. 6501 // a @Prop can add a dependency of the ViewPU onto itself. Ignore it. 6502 6503 return; 6504 } 6505 // do not process an Element that has been marked to be deleted 6506 const entry = this.updateFuncByElmtId.get(elmtId); 6507 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 6508 if (typeof updateFunc !== 'function') { 6509 6510 } 6511 else { 6512 6513 this.isRenderInProgress = true; 6514 6515 updateFunc(elmtId, /* isFirstRender */ false); 6516 6517 6518 this.finishUpdateFunc(elmtId); 6519 6520 this.isRenderInProgress = false; 6521 6522 } 6523 6524 } 6525 delayCompleteRerender(deep = false) { 6526 this.delayRecycleNodeRerender = true; 6527 this.delayRecycleNodeRerenderDeep = deep; 6528 } 6529 flushDelayCompleteRerender() { 6530 this.forceCompleteRerender(this.delayRecycleNodeRerenderDeep); 6531 this.delayRecycleNodeRerender = false; 6532 } 6533 /** 6534 * force a complete rerender / update on specific node by executing update function. 6535 * 6536 * @param elmtId which node needs to update. 6537 * 6538 * framework internal functions, apps must not call 6539 */ 6540 forceRerenderNode(elmtId) { 6541 6542 // see which elmtIds are managed by this View 6543 // and clean up all book keeping for them 6544 this.purgeDeletedElmtIds(); 6545 this.UpdateElement(elmtId); 6546 // remove elemtId from dirtDescendantElementIds. 6547 this.dirtDescendantElementIds_.delete(elmtId); 6548 6549 } 6550 // implements IMultiPropertiesChangeSubscriber 6551 viewPropertyHasChanged(varName, dependentElmtIds) { 6552 6553 aceTrace.begin('ViewPU.viewPropertyHasChanged', this.constructor.name, varName, dependentElmtIds.size); 6554 if (this.isRenderInProgress) { 6555 stateMgmtConsole.applicationError(`${this.debugInfo__()}: State variable '${varName}' has changed during render! It's illegal to change @Component state while build (initial render or re-render) is on-going. Application error!`); 6556 } 6557 this.syncInstanceId(); 6558 if (dependentElmtIds.size && !this.isFirstRender()) { 6559 if (!this.dirtDescendantElementIds_.size && !this.runReuse_) { 6560 // mark ComposedElement dirty when first elmtIds are added 6561 // do not need to do this every time 6562 this.markNeedUpdate(); 6563 } 6564 6565 for (const elmtId of dependentElmtIds) { 6566 if (this.hasRecycleManager()) { 6567 this.dirtDescendantElementIds_.add(this.recycleManager_.proxyNodeId(elmtId)); 6568 } 6569 else { 6570 this.dirtDescendantElementIds_.add(elmtId); 6571 } 6572 } 6573 6574 } 6575 else { 6576 6577 6578 } 6579 let cb = this.watchedProps.get(varName); 6580 if (cb && typeof cb === 'function') { 6581 6582 cb.call(this, varName); 6583 } 6584 this.restoreInstanceId(); 6585 aceTrace.end(); 6586 6587 } 6588 /** 6589 * inform that UINode with given elmtId needs rerender 6590 * does NOT exec @Watch function. 6591 * only used on V3 code path from ObserveV2.fireChange. 6592 * 6593 * FIXME will still use in the future? 6594 */ 6595 uiNodeNeedUpdateV3(elmtId) { 6596 if (this.isFirstRender()) { 6597 return; 6598 } 6599 6600 if (!this.dirtDescendantElementIds_.size) { // && !this runReuse_) { 6601 // mark ComposedElement dirty when first elmtIds are added 6602 // do not need to do this every time 6603 this.syncInstanceId(); 6604 this.markNeedUpdate(); 6605 this.restoreInstanceId(); 6606 } 6607 if (this.hasRecycleManager()) { 6608 this.dirtDescendantElementIds_.add(this.recycleManager_.proxyNodeId(elmtId)); 6609 } 6610 else { 6611 this.dirtDescendantElementIds_.add(elmtId); 6612 } 6613 6614 6615 } 6616 performDelayedUpdate() { 6617 if (!this.ownObservedPropertiesStore_.size && !this.elmtIdsDelayedUpdate.size) { 6618 return; 6619 } 6620 6621 aceTrace.begin('ViewPU.performDelayedUpdate', this.constructor.name); 6622 6623 this.syncInstanceId(); 6624 for (const stateLinkPropVar of this.ownObservedPropertiesStore_) { 6625 const changedElmtIds = stateLinkPropVar.moveElmtIdsForDelayedUpdate(); 6626 if (changedElmtIds) { 6627 const varName = stateLinkPropVar.info(); 6628 if (changedElmtIds.size && !this.isFirstRender()) { 6629 for (const elmtId of changedElmtIds) { 6630 this.dirtDescendantElementIds_.add(elmtId); 6631 } 6632 } 6633 6634 const cb = this.watchedProps.get(varName); 6635 if (cb) { 6636 6637 cb.call(this, varName); 6638 } 6639 } 6640 } // for all ownStateLinkProps_ 6641 for (let elementId of this.elmtIdsDelayedUpdate) { 6642 this.dirtDescendantElementIds_.add(elementId); 6643 } 6644 this.elmtIdsDelayedUpdate.clear(); 6645 this.restoreInstanceId(); 6646 if (this.dirtDescendantElementIds_.size) { 6647 this.markNeedUpdate(); 6648 } 6649 aceTrace.end(); 6650 6651 } 6652 /** 6653 * Function to be called from the constructor of the sub component 6654 * to register a @Watch variable 6655 * @param propStr name of the variable. Note from @Provide and @Consume this is 6656 * the variable name and not the alias! 6657 * @param callback application defined member function of sub-class 6658 */ 6659 declareWatch(propStr, callback) { 6660 this.watchedProps.set(propStr, callback); 6661 } 6662 /** 6663 * This View @Provide's a variable under given name 6664 * Call this function from the constructor of the sub class 6665 * @param providedPropName either the variable name or the alias defined as 6666 * decorator param 6667 * @param store the backing store object for this variable (not the get/set variable!) 6668 */ 6669 addProvidedVar(providedPropName, store, allowOverride = false) { 6670 if (!allowOverride && this.findProvidePU(providedPropName)) { 6671 throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}. Property with this name is provided by one of the ancestor Views already. @Provide override not allowed.`); 6672 } 6673 store.setDecoratorInfo('@Provide'); 6674 this.providedVars_.set(providedPropName, store); 6675 } 6676 /* 6677 findProvidePU finds @Provided property recursively by traversing ViewPU's towards that of the UI tree root @Component: 6678 if 'this' ViewPU has a @Provide('providedPropName') return it, otherwise ask from its parent ViewPU. 6679 */ 6680 findProvidePU(providedPropName) { 6681 return this.providedVars_.get(providedPropName) || (this.parent_ && this.parent_.findProvidePU(providedPropName)); 6682 } 6683 /** 6684 * Method for the sub-class to call from its constructor for resolving 6685 * a @Consume variable and initializing its backing store 6686 * with the SyncedPropertyTwoWay<T> object created from the 6687 * @Provide variable's backing store. 6688 * @param providedPropName the name of the @Provide'd variable. 6689 * This is either the @Consume decorator parameter, or variable name. 6690 * @param consumeVarName the @Consume variable name (not the 6691 * @Consume decorator parameter) 6692 * @returns initializing value of the @Consume backing store 6693 */ 6694 initializeConsume(providedPropName, consumeVarName) { 6695 let providedVarStore = this.findProvidePU(providedPropName); 6696 if (providedVarStore === undefined) { 6697 throw new ReferenceError(`${this.debugInfo__()} missing @Provide property with name ${providedPropName}. 6698 Fail to resolve @Consume(${providedPropName}).`); 6699 } 6700 const factory = (source) => { 6701 const result = new SynchedPropertyTwoWayPU(source, this, consumeVarName); 6702 result.setDecoratorInfo('@Consume'); 6703 6704 return result; 6705 }; 6706 return providedVarStore.createSync(factory); 6707 } 6708 /** 6709 * given the elmtId of a child or child of child within this custom component 6710 * remember this component needs a partial update 6711 * @param elmtId 6712 */ 6713 markElemenDirtyById(elmtId) { 6714 // TODO ace-ets2bundle, framework, compiled apps need to update together 6715 // this function will be removed after a short transition period 6716 stateMgmtConsole.applicationError(`${this.debugInfo__()}: markElemenDirtyById no longer supported. 6717 Please update your ace-ets2bundle and recompile your application. Application error!`); 6718 } 6719 /** 6720 * For each recorded dirty Element in this custom component 6721 * run its update function 6722 * 6723 */ 6724 updateDirtyElements() { 6725 6726 do { 6727 6728 // see which elmtIds are managed by this View 6729 // and clean up all book keeping for them 6730 this.purgeDeletedElmtIds(); 6731 // process all elmtIds marked as needing update in ascending order. 6732 // ascending order ensures parent nodes will be updated before their children 6733 // prior cleanup ensure no already deleted Elements have their update func executed 6734 const dirtElmtIdsFromRootNode = Array.from(this.dirtDescendantElementIds_).sort(ViewPU.compareNumber); 6735 // if state changed during exec update lambda inside UpdateElement, then the dirty elmtIds will be added 6736 // to newly created this.dirtDescendantElementIds_ Set 6737 dirtElmtIdsFromRootNode.forEach(elmtId => { 6738 if (this.hasRecycleManager()) { 6739 this.UpdateElement(this.recycleManager_.proxyNodeId(elmtId)); 6740 } 6741 else { 6742 this.UpdateElement(elmtId); 6743 } 6744 this.dirtDescendantElementIds_.delete(elmtId); 6745 }); 6746 if (this.dirtDescendantElementIds_.size) { 6747 stateMgmtConsole.applicationError(`${this.debugInfo__()}: New UINode objects added to update queue while re-render! - Likely caused by @Component state change during build phase, not allowed. Application error!`); 6748 } 6749 } while (this.dirtDescendantElementIds_.size); 6750 6751 6752 } 6753 // executed on first render only 6754 // kept for backward compatibility with old ace-ets2bundle 6755 observeComponentCreation(compilerAssignedUpdateFunc) { 6756 if (this.isDeleting_) { 6757 stateMgmtConsole.error(`View ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation `); 6758 return; 6759 } 6760 const updateFunc = (elmtId, isFirstRender) => { 6761 6762 this.currentlyRenderedElmtIdStack_.push(elmtId); 6763 compilerAssignedUpdateFunc(elmtId, isFirstRender); 6764 this.currentlyRenderedElmtIdStack_.pop(); 6765 6766 }; 6767 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 6768 // in observeComponentCreation function we do not get info about the component name, in 6769 // observeComponentCreation2 we do. 6770 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc }); 6771 // add element id -> owning ViewPU 6772 UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this)); 6773 try { 6774 updateFunc(elmtId, /* is first render */ true); 6775 } 6776 catch (error) { 6777 // avoid the incompatible change that move set function before updateFunc. 6778 this.updateFuncByElmtId.delete(elmtId); 6779 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId); 6780 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${error.message}`); 6781 throw error; 6782 } 6783 } 6784 observeComponentCreation2(compilerAssignedUpdateFunc, classObject) { 6785 if (this.isDeleting_) { 6786 stateMgmtConsole.error(`View ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation2 `); 6787 return; 6788 } 6789 const _componentName = (classObject && ('name' in classObject)) ? Reflect.get(classObject, 'name') : 'unspecified UINode'; 6790 if (_componentName === '__Recycle__') { 6791 return; 6792 } 6793 const _popFunc = (classObject && 'pop' in classObject) ? classObject.pop : () => { }; 6794 const updateFunc = (elmtId, isFirstRender) => { 6795 var _a, _b; 6796 this.syncInstanceId(); 6797 6798 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onComponentCreateEnter(_componentName, elmtId, isFirstRender, this); 6799 ViewStackProcessor.StartGetAccessRecordingFor(elmtId); 6800 if (!this.isViewV3) { 6801 // Enable PU state tracking only in PU @Components 6802 this.currentlyRenderedElmtIdStack_.push(elmtId); 6803 stateMgmtDFX.inRenderingElementId.push(elmtId); 6804 } 6805 // if V2 @Observed/@Track used anywhere in the app (there is no more fine grained criteria), 6806 // enable V2 object deep observation 6807 // FIXME: A @Component should only use PU or V2 state, but ReactNative dynamic viewer uses both. 6808 if (this.isViewV3 || ConfigureStateMgmt.instance.needsV2Observe()) { 6809 // FIXME: like in V2 setting bindId_ in ObserveV2 does not work with 'stacked' 6810 // update + initial render calls, like in if and ForEach case, convert to stack as well 6811 ObserveV2.getObserve().startRecordDependencies(this, elmtId); 6812 } 6813 compilerAssignedUpdateFunc(elmtId, isFirstRender); 6814 if (!isFirstRender) { 6815 _popFunc(); 6816 } 6817 let node = this.getNodeById(elmtId); 6818 if (node !== undefined) { 6819 node.cleanStageValue(); 6820 } 6821 if (this.isViewV3 || ConfigureStateMgmt.instance.needsV2Observe()) { 6822 ObserveV2.getObserve().stopRecordDependencies(); 6823 } 6824 if (!this.isViewV3) { 6825 this.currentlyRenderedElmtIdStack_.pop(); 6826 stateMgmtDFX.inRenderingElementId.pop(); 6827 } 6828 ViewStackProcessor.StopGetAccessRecording(); 6829 (_b = PUV2ViewBase.arkThemeScopeManager) === null || _b === void 0 ? void 0 : _b.onComponentCreateExit(elmtId); 6830 6831 this.restoreInstanceId(); 6832 }; 6833 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 6834 // needs to move set before updateFunc. 6835 // make sure the key and object value exist since it will add node in attributeModifier during updateFunc. 6836 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc, classObject: classObject }); 6837 // add element id -> owning ViewPU 6838 UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this)); 6839 try { 6840 updateFunc(elmtId, /* is first render */ true); 6841 } 6842 catch (error) { 6843 // avoid the incompatible change that move set function before updateFunc. 6844 this.updateFuncByElmtId.delete(elmtId); 6845 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId); 6846 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${error.message}`); 6847 throw error; 6848 } 6849 6850 } 6851 getOrCreateRecycleManager() { 6852 if (!this.recycleManager_) { 6853 this.recycleManager_ = new RecycleManager; 6854 } 6855 return this.recycleManager_; 6856 } 6857 getRecycleManager() { 6858 return this.recycleManager_; 6859 } 6860 hasRecycleManager() { 6861 return !(this.recycleManager_ === undefined); 6862 } 6863 initRecycleManager() { 6864 if (this.recycleManager_) { 6865 stateMgmtConsole.error(`${this.debugInfo__()}: init recycleManager multiple times. Internal error.`); 6866 return; 6867 } 6868 this.recycleManager_ = new RecycleManager; 6869 } 6870 rebuildUpdateFunc(elmtId, compilerAssignedUpdateFunc) { 6871 const updateFunc = (elmtId, isFirstRender) => { 6872 this.currentlyRenderedElmtIdStack_.push(elmtId); 6873 compilerAssignedUpdateFunc(elmtId, isFirstRender); 6874 this.currentlyRenderedElmtIdStack_.pop(); 6875 }; 6876 if (this.updateFuncByElmtId.has(elmtId)) { 6877 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc }); 6878 } 6879 } 6880 /** 6881 * @function observeRecycleComponentCreation 6882 * @description custom node recycle creation 6883 * @param name custom node name 6884 * @param recycleUpdateFunc custom node recycle update which can be converted to a normal update function 6885 * @return void 6886 */ 6887 observeRecycleComponentCreation(name, recycleUpdateFunc) { 6888 // convert recycle update func to update func 6889 const compilerAssignedUpdateFunc = (element, isFirstRender) => { 6890 recycleUpdateFunc(element, isFirstRender, undefined); 6891 }; 6892 let node; 6893 // if there is no suitable recycle node, run a normal creation function. 6894 if (!this.hasRecycleManager() || !(node = this.getRecycleManager().popRecycleNode(name))) { 6895 6896 this.observeComponentCreation(compilerAssignedUpdateFunc); 6897 return; 6898 } 6899 // if there is a suitable recycle node, run a recycle update function. 6900 const newElmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 6901 const oldElmtId = node.id__(); 6902 this.recycleManager_.updateNodeId(oldElmtId, newElmtId); 6903 node.hasBeenRecycled_ = false; 6904 this.rebuildUpdateFunc(oldElmtId, compilerAssignedUpdateFunc); 6905 recycleUpdateFunc(oldElmtId, /* is first render */ true, node); 6906 } 6907 // param is used by BuilderNode 6908 aboutToReuseInternal(param) { 6909 this.runReuse_ = true; 6910 stateMgmtTrace.scopedTrace(() => { 6911 if (this.paramsGenerator_ && typeof this.paramsGenerator_ === 'function') { 6912 const params = param ? param : this.paramsGenerator_(); 6913 this.updateStateVars(params); 6914 this.aboutToReuse(params); 6915 } 6916 }, 'aboutToReuse', this.constructor.name); 6917 for (const stateLinkPropVar of this.ownObservedPropertiesStore_) { 6918 const changedElmtIds = stateLinkPropVar.moveElmtIdsForDelayedUpdate(true); 6919 if (changedElmtIds) { 6920 if (changedElmtIds.size && !this.isFirstRender()) { 6921 for (const elmtId of changedElmtIds) { 6922 this.dirtDescendantElementIds_.add(elmtId); 6923 } 6924 } 6925 } 6926 } 6927 if (!this.delayRecycleNodeRerender) { 6928 this.updateDirtyElements(); 6929 } 6930 else { 6931 this.flushDelayCompleteRerender(); 6932 } 6933 this.childrenWeakrefMap_.forEach((weakRefChild) => { 6934 const child = weakRefChild.deref(); 6935 if (child) { 6936 if (child instanceof ViewPU) { 6937 if (!child.hasBeenRecycled_) { 6938 child.aboutToReuseInternal(); 6939 } 6940 } 6941 else { 6942 // FIXME fix for mixed V2 - V3 Hierarchies 6943 throw new Error('aboutToReuseInternal: Recycle not implemented for ViewV2, yet'); 6944 } 6945 } // if child 6946 }); 6947 this.runReuse_ = false; 6948 } 6949 stopRecursiveRecycle() { 6950 this.preventRecursiveRecycle_ = true; 6951 } 6952 aboutToRecycleInternal() { 6953 this.runReuse_ = true; 6954 stateMgmtTrace.scopedTrace(() => { 6955 this.aboutToRecycle(); 6956 }, 'aboutToRecycle', this.constructor.name); 6957 if (this.preventRecursiveRecycle_) { 6958 this.preventRecursiveRecycle_ = false; 6959 return; 6960 } 6961 this.childrenWeakrefMap_.forEach((weakRefChild) => { 6962 const child = weakRefChild.deref(); 6963 if (child) { 6964 if (child instanceof ViewPU) { 6965 if (!child.hasBeenRecycled_) { 6966 child.aboutToRecycleInternal(); 6967 } 6968 } 6969 else { 6970 // FIXME fix for mixed V2 - V3 Hierarchies 6971 throw new Error('aboutToRecycleInternal: Recycle not yet implemented for ViewV2'); 6972 } 6973 } // if child 6974 }); 6975 this.runReuse_ = false; 6976 } 6977 // add current JS object to it's parent recycle manager 6978 recycleSelf(name) { 6979 if (this.getParent() && this.getParent() instanceof ViewPU && !this.getParent().isDeleting_) { 6980 const parentPU = this.getParent(); 6981 parentPU.getOrCreateRecycleManager().pushRecycleNode(name, this); 6982 this.hasBeenRecycled_ = true; 6983 } 6984 else { 6985 this.resetRecycleCustomNode(); 6986 } 6987 } 6988 isRecycled() { 6989 return this.hasBeenRecycled_; 6990 } 6991 UpdateLazyForEachElements(elmtIds) { 6992 if (!Array.isArray(elmtIds)) { 6993 return; 6994 } 6995 Array.from(elmtIds).sort(ViewPU.compareNumber).forEach((elmtId) => { 6996 const entry = this.updateFuncByElmtId.get(elmtId); 6997 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 6998 if (typeof updateFunc !== 'function') { 6999 7000 } 7001 else { 7002 this.isRenderInProgress = true; 7003 updateFunc(elmtId, false); 7004 this.finishUpdateFunc(elmtId); 7005 this.isRenderInProgress = false; 7006 } 7007 }); 7008 } 7009 /** 7010 * CreateStorageLink and CreateStorageLinkPU are used by the implementation of @StorageLink and 7011 * @LocalStotrageLink in full update and partial update solution respectively. 7012 * These are not part of the public AppStorage API , apps should not use. 7013 * @param storagePropName - key in LocalStorage 7014 * @param defaultValue - value to use when creating a new prop in the LocalStotage 7015 * @param owningView - the View/ViewPU owning the @StorageLink/@LocalStorageLink variable 7016 * @param viewVariableName - @StorageLink/@LocalStorageLink variable name 7017 * @returns SynchedPropertySimple/ObjectTwoWay/PU 7018 */ 7019 createStorageLink(storagePropName, defaultValue, viewVariableName) { 7020 const appStorageLink = AppStorage.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7021 ? undefined 7022 : new SynchedPropertyTwoWayPU(source, this, viewVariableName)); 7023 appStorageLink === null || appStorageLink === void 0 ? void 0 : appStorageLink.setDecoratorInfo('@StorageLink'); 7024 return appStorageLink; 7025 } 7026 createStorageProp(storagePropName, defaultValue, viewVariableName) { 7027 const appStorageProp = AppStorage.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7028 ? undefined 7029 : new SynchedPropertyOneWayPU(source, this, viewVariableName)); 7030 appStorageProp === null || appStorageProp === void 0 ? void 0 : appStorageProp.setDecoratorInfo('@StorageProp'); 7031 return appStorageProp; 7032 } 7033 createLocalStorageLink(storagePropName, defaultValue, viewVariableName) { 7034 const localStorageLink = this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7035 ? undefined 7036 : new SynchedPropertyTwoWayPU(source, this, viewVariableName)); 7037 localStorageLink === null || localStorageLink === void 0 ? void 0 : localStorageLink.setDecoratorInfo('@LocalStorageLink'); 7038 return localStorageLink; 7039 } 7040 createLocalStorageProp(storagePropName, defaultValue, viewVariableName) { 7041 const localStorageProp = this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7042 ? undefined 7043 : new SynchedPropertyObjectOneWayPU(source, this, viewVariableName)); 7044 localStorageProp === null || localStorageProp === void 0 ? void 0 : localStorageProp.setDecoratorInfo('@LocalStorageProp'); 7045 return localStorageProp; 7046 } 7047 /** 7048 * onDumpInfo is used to process commands delivered by the hidumper process 7049 * @param commands - list of commands provided in the shell 7050 * @returns void 7051 */ 7052 onDumpInfo(commands) { 7053 let dfxCommands = this.processOnDumpCommands(commands); 7054 dfxCommands.forEach((command) => { 7055 let view = undefined; 7056 if (command.viewId) { 7057 view = this.findViewPUInHierarchy(command.viewId); 7058 if (!view) { 7059 DumpLog.print(0, `\nTarget view: ${command.viewId} not found for command: ${command.what}\n`); 7060 return; 7061 } 7062 } 7063 else { 7064 view = this; 7065 command.viewId = view.id__(); 7066 } 7067 switch (command.what) { 7068 case '-dumpAll': 7069 view.printDFXHeader('ViewPU Info', command); 7070 DumpLog.print(0, view.debugInfoView(command.isRecursive)); 7071 break; 7072 case '-viewHierarchy': 7073 view.printDFXHeader('ViewPU Hierarchy', command); 7074 DumpLog.print(0, view.debugInfoViewHierarchy(command.isRecursive)); 7075 break; 7076 case '-stateVariables': 7077 view.printDFXHeader('ViewPU State Variables', command); 7078 DumpLog.print(0, view.debugInfoStateVars()); 7079 break; 7080 case '-registeredElementIds': 7081 view.printDFXHeader('ViewPU Registered Element IDs', command); 7082 DumpLog.print(0, view.debugInfoUpdateFuncByElmtId(command.isRecursive)); 7083 break; 7084 case '-dirtyElementIds': 7085 view.printDFXHeader('ViewPU Dirty Registered Element IDs', command); 7086 DumpLog.print(0, view.debugInfoDirtDescendantElementIds(command.isRecursive)); 7087 break; 7088 case '-inactiveComponents': 7089 view.printDFXHeader('List of Inactive Components', command); 7090 DumpLog.print(0, view.debugInfoInactiveComponents()); 7091 break; 7092 case '-profiler': 7093 view.printDFXHeader('Profiler Info', command); 7094 view.dumpReport(); 7095 this.sendStateInfo('{}'); 7096 break; 7097 default: 7098 DumpLog.print(0, `\nUnsupported JS DFX dump command: [${command.what}, viewId=${command.viewId}, isRecursive=${command.isRecursive}]\n`); 7099 } 7100 }); 7101 } 7102 printDFXHeader(header, command) { 7103 let length = 50; 7104 let remainder = length - header.length < 0 ? 0 : length - header.length; 7105 DumpLog.print(0, `\n${'-'.repeat(remainder / 2)}${header}${'-'.repeat(remainder / 2)}`); 7106 DumpLog.print(0, `[${command.what}, viewId=${command.viewId}, isRecursive=${command.isRecursive}]\n`); 7107 } 7108 processOnDumpCommands(commands) { 7109 let isFlag = (param) => { 7110 return '-r'.match(param) != null || param.startsWith('-viewId='); 7111 }; 7112 let dfxCommands = []; 7113 for (var i = 0; i < commands.length; i++) { 7114 let command = commands[i]; 7115 if (isFlag(command)) { 7116 if (command.startsWith('-viewId=')) { 7117 let dfxCommand = dfxCommands[dfxCommands.length - 1]; 7118 if (dfxCommand) { 7119 let input = command.split('='); 7120 if (input[1]) { 7121 let viewId = Number.parseInt(input[1]); 7122 dfxCommand.viewId = Number.isNaN(viewId) ? UINodeRegisterProxy.notRecordingDependencies : viewId; 7123 } 7124 } 7125 } 7126 else if (command.match('-r')) { 7127 let dfxCommand = dfxCommands[dfxCommands.length - 1]; 7128 if (dfxCommand) { 7129 dfxCommand.isRecursive = true; 7130 } 7131 } 7132 } 7133 else { 7134 dfxCommands.push({ 7135 what: command, 7136 viewId: undefined, 7137 isRecursive: false, 7138 }); 7139 } 7140 } 7141 return dfxCommands; 7142 } 7143 findViewPUInHierarchy(id) { 7144 let weakChild = this.childrenWeakrefMap_.get(id); 7145 if (weakChild) { 7146 const child = weakChild.deref(); 7147 // found child with id, is it a ViewPU? 7148 return (child instanceof ViewPU) ? child : undefined; 7149 } 7150 // did not find, continue searching 7151 let retVal = undefined; 7152 for (const [key, value] of this.childrenWeakrefMap_.entries()) { 7153 retVal = value.deref().findViewPUInHierarchy(id); 7154 if (retVal) { 7155 break; 7156 } 7157 } 7158 return retVal; 7159 } 7160 debugInfoView(recursive = false) { 7161 return this.debugInfoViewInternal(recursive); 7162 } 7163 debugInfoViewInternal(recursive = false) { 7164 let retVal = `@Component\n${this.constructor.name}[${this.id__()}]`; 7165 retVal += `\n\nView Hierarchy:\n${this.debugInfoViewHierarchy(recursive)}`; 7166 retVal += `\n\nState variables:\n${this.debugInfoStateVars()}`; 7167 retVal += `\n\nRegistered Element IDs:\n${this.debugInfoUpdateFuncByElmtId(recursive)}`; 7168 retVal += `\n\nDirty Registered Element IDs:\n${this.debugInfoDirtDescendantElementIds(recursive)}`; 7169 return retVal; 7170 } 7171 debugInfoDirtDescendantElementIds(recursive = false) { 7172 return this.debugInfoDirtDescendantElementIdsInternal(0, recursive, { total: 0 }); 7173 } 7174 debugInfoDirtDescendantElementIdsInternal(depth = 0, recursive = false, counter) { 7175 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`; 7176 this.dirtDescendantElementIds_.forEach((value) => { 7177 retVaL += `${value}, `; 7178 }); 7179 counter.total += this.dirtDescendantElementIds_.size; 7180 retVaL += `\n${' '.repeat(depth + 1)}}[${this.dirtDescendantElementIds_.size}]`; 7181 if (recursive) { 7182 this.childrenWeakrefMap_.forEach((value, key, map) => { 7183 var _a; 7184 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoDirtDescendantElementIdsInternal(depth + 1, recursive, counter); 7185 }); 7186 } 7187 if (recursive && depth == 0) { 7188 retVaL += `\nTotal: ${counter.total}`; 7189 } 7190 return retVaL; 7191 } 7192 /** 7193 * onDumpInspector is invoked by native side to create Inspector tree including state variables 7194 * @returns dump info 7195 */ 7196 onDumpInspector() { 7197 let res = new DumpInfo(); 7198 res.viewInfo = { componentName: this.constructor.name, id: this.id__() }; 7199 Object.getOwnPropertyNames(this) 7200 .filter((varName) => varName.startsWith('__') && !varName.startsWith(ObserveV2.OB_PREFIX)) 7201 .forEach((varName) => { 7202 const prop = Reflect.get(this, varName); 7203 if (typeof prop === 'object' && 'debugInfoDecorator' in prop) { 7204 const observedProp = prop; 7205 res.observedPropertiesInfo.push(stateMgmtDFX.getObservedPropertyInfo(observedProp, false)); 7206 } 7207 }); 7208 let resInfo = ''; 7209 try { 7210 resInfo = JSON.stringify(res); 7211 } 7212 catch (error) { 7213 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in getInspector: ${error.message}`); 7214 } 7215 return resInfo; 7216 } 7217} // class ViewPU 7218/* 7219 * Copyright (c) 2023 Huawei Device Co., Ltd. 7220 * Licensed under the Apache License, Version 2.0 (the "License"); 7221 * you may not use this file except in compliance with the License. 7222 * You may obtain a copy of the License at 7223 * 7224 * http://www.apache.org/licenses/LICENSE-2.0 7225 * 7226 * Unless required by applicable law or agreed to in writing, software 7227 * distributed under the License is distributed on an "AS IS" BASIS, 7228 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7229 * See the License for the specific language governing permissions and 7230 * limitations under the License. 7231 * 7232 * * RecycleManager - Recycle cache manager 7233 * 7234* all definitions in this file are framework internal 7235*/ 7236/** 7237 * @class RecycleManager 7238 * @description manage the JS object cached of current node 7239 */ 7240class RecycleManager { 7241 constructor() { 7242 // key: recycle node name 7243 // value: recycle node JS object 7244 this.cachedRecycleNodes_ = undefined; 7245 this.biMap_ = undefined; 7246 this.cachedRecycleNodes_ = new Map(); 7247 this.biMap_ = new BidirectionalMap(); 7248 } 7249 updateNodeId(oldElmtId, newElmtId) { 7250 this.biMap_.delete(oldElmtId); 7251 this.biMap_.add([oldElmtId, newElmtId]); 7252 } 7253 proxyNodeId(oldElmtId) { 7254 const proxy = this.biMap_.get(oldElmtId); 7255 if (!proxy) { 7256 return oldElmtId; 7257 } 7258 return proxy; 7259 } 7260 pushRecycleNode(name, node) { 7261 var _a; 7262 if (!this.cachedRecycleNodes_.get(name)) { 7263 this.cachedRecycleNodes_.set(name, new Array()); 7264 } 7265 (_a = this.cachedRecycleNodes_.get(name)) === null || _a === void 0 ? void 0 : _a.push(node); 7266 } 7267 popRecycleNode(name) { 7268 var _a; 7269 return (_a = this.cachedRecycleNodes_.get(name)) === null || _a === void 0 ? void 0 : _a.pop(); 7270 } 7271 // When parent JS View is deleted, release all cached nodes 7272 purgeAllCachedRecycleNode() { 7273 this.cachedRecycleNodes_.forEach((nodes, _) => { 7274 nodes.forEach((node) => { 7275 node.resetRecycleCustomNode(); 7276 }); 7277 }); 7278 this.cachedRecycleNodes_.clear(); 7279 } 7280 // Set active status for all cached nodes 7281 setActive(active) { 7282 this.cachedRecycleNodes_.forEach((nodes, _) => { 7283 nodes.forEach((node) => { 7284 node.setActiveInternal(active); 7285 }); 7286 }); 7287 } 7288} 7289class BidirectionalMap { 7290 constructor() { 7291 this.fwdMap_ = undefined; 7292 this.revMap_ = undefined; 7293 this.fwdMap_ = new Map(); 7294 this.revMap_ = new Map(); 7295 } 7296 delete(key) { 7297 if (!this.fwdMap_[key]) { 7298 return; 7299 } 7300 const rev = this.fwdMap_[key]; 7301 this.fwdMap_.delete(key); 7302 this.revMap_.delete(rev); 7303 } 7304 get(key) { 7305 return this.fwdMap_[key] || this.revMap_[key]; 7306 } 7307 add(pair) { 7308 this.fwdMap_[pair[0]] = pair[1]; 7309 this.revMap_[pair[1]] = pair[0]; 7310 } 7311} 7312/* 7313 * Copyright (c) 2022 Huawei Device Co., Ltd. 7314 * Licensed under the Apache License, Version 2.0 (the "License"); 7315 * you may not use this file except in compliance with the License. 7316 * You may obtain a copy of the License at 7317 * 7318 * http://www.apache.org/licenses/LICENSE-2.0 7319 * 7320 * Unless required by applicable law or agreed to in writing, software 7321 * distributed under the License is distributed on an "AS IS" BASIS, 7322 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7323 * See the License for the specific language governing permissions and 7324 * limitations under the License. 7325 * 7326 * * ViewPU - View for Partial Update 7327 * 7328* all definitions in this file are framework internal 7329*/ 7330/** 7331 given parameters for calling a @Builder function 7332 this function wraps the Object of type T inside a ES6 Proxy. 7333 Each param, i.e. Object property is either a function or a value. 7334 If it is a function the function can either return a value of expected 7335 parameter type or an ObservedPropertyabstract<T> where T is the exected 7336 parameter type. The latter is the case when passing a state variable by 7337 reference. 7338 7339 Two purposes: 7340 1 - @Builder function boxy accesses params a '$$.paramA' 7341 However paramA can be a function, so to obtain the value the 7342 access would need to be '$$.param()' The proxy executes 7343 the function and return s the result 7344 2 - said function returns to ObservedPropertyAbstract backing store of 7345 a calling @Component state variable (whenever the state var is 7346 provided to the @Builder function). For this case the proxy can provide 7347 - the value by executing paramA() to return the ObservedPropertyAbstract 7348 and further (monitored!) get() to read its value 7349 - when requested to return '__param1' it returns the ObservedPropertyAbstract 7350 object. The scenario is to use to init a @Link source. 7351 */ 7352function makeBuilderParameterProxy(builderName, source) { 7353 return new Proxy(source, { 7354 set(target, prop, val) { 7355 throw Error(`@Builder '${builderName}': Invalid attempt to set(write to) parameter '${prop.toString()}' error!`); 7356 }, 7357 get(target, prop) { 7358 const prop1 = prop.toString().trim().startsWith('__') 7359 ? prop.toString().trim().substring(2) 7360 : prop.toString().trim(); 7361 7362 if (!(typeof target === 'object') && (prop1 in target)) { 7363 throw Error(`@Builder '${builderName}': '${prop1}' used but not a function parameter error!`); 7364 } 7365 const value = target[prop1]; 7366 if (typeof value !== 'function') { 7367 7368 return value; 7369 } 7370 const funcRet = value(); 7371 if ((typeof funcRet === 'object') && ('get' in funcRet)) { 7372 if (prop1 !== prop) { 7373 7374 return funcRet; 7375 } 7376 else { 7377 7378 const result = funcRet.get(); 7379 7380 return result; 7381 } 7382 } 7383 7384 return funcRet; 7385 } // get 7386 }); // new Proxy 7387} 7388/* 7389 * Copyright (c) 2024 Huawei Device Co., Ltd. 7390 * Licensed under the Apache License, Version 2.0 (the "License"); 7391 * you may not use this file except in compliance with the License. 7392 * You may obtain a copy of the License at 7393 * 7394 * http://www.apache.org/licenses/LICENSE-2.0 7395 * 7396 * Unless required by applicable law or agreed to in writing, software 7397 * distributed under the License is distributed on an "AS IS" BASIS, 7398 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7399 * See the License for the specific language governing permissions and 7400 * limitations under the License. 7401 */ 7402/** 7403 * Common Proxy handler for objects and dates for both decorators and makeObserved 7404 */ 7405class ObjectProxyHandler { 7406 constructor(isMakeObserved = false) { 7407 this.isMakeObserved_ = isMakeObserved; 7408 } 7409 // decorators work on object that holds the dependencies directly 7410 // makeObserved can't modify the object itself, so it creates a 7411 // wrapper object around it and that will hold the references 7412 // 7413 // this function is used to get the correct object that can be observed 7414 getTarget(obj) { 7415 return this.isMakeObserved_ ? RefInfo.get(obj) : obj; 7416 } 7417 get(target, key, receiver) { 7418 if (typeof key === 'symbol') { 7419 if (key === Symbol.iterator) { 7420 const conditionalTarget = this.getTarget(target); 7421 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7422 return (...args) => target[key](...args); 7423 } 7424 if (key === ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7425 return target; 7426 } 7427 if (this.isMakeObserved_ && key === ObserveV2.SYMBOL_MAKE_OBSERVED) { 7428 return true; 7429 } 7430 return target[key]; 7431 } 7432 7433 const conditionalTarget = this.getTarget(target); 7434 // makeObserved logic adds wrapper proxy later 7435 let ret = this.isMakeObserved_ ? target[key] : ObserveV2.autoProxyObject(target, key); 7436 if (typeof (ret) !== 'function') { 7437 ObserveV2.getObserve().addRef(conditionalTarget, key); 7438 return (typeof (ret) === 'object' && this.isMakeObserved_) ? RefInfo.get(ret).proxy : ret; 7439 } 7440 if (target instanceof Date) { 7441 if (ObjectProxyHandler.dateSetFunctions.has(key)) { 7442 return function (...args) { 7443 // execute original function with given arguments 7444 let result = ret.call(this, ...args); 7445 ObserveV2.getObserve().fireChange(conditionalTarget, ObjectProxyHandler.OB_DATE); 7446 return result; 7447 // bind 'this' to target inside the function 7448 }.bind(target); 7449 } 7450 else { 7451 ObserveV2.getObserve().addRef(conditionalTarget, ObjectProxyHandler.OB_DATE); 7452 } 7453 return ret.bind(target); 7454 } 7455 // function 7456 return ret.bind(receiver); 7457 } 7458 set(target, key, value) { 7459 if (typeof key === 'symbol') { 7460 if (!this.isMakeObserved_ && key !== ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7461 target[key] = value; 7462 } 7463 return true; 7464 } 7465 if (target[key] === value) { 7466 return true; 7467 } 7468 target[key] = value; 7469 ObserveV2.getObserve().fireChange(this.getTarget(target), key.toString()); 7470 return true; 7471 } 7472} 7473ObjectProxyHandler.OB_DATE = '__date__'; 7474ObjectProxyHandler.dateSetFunctions = new Set(['setFullYear', 'setMonth', 'setDate', 'setHours', 'setMinutes', 7475 'setSeconds', 'setMilliseconds', 'setTime', 'setUTCFullYear', 'setUTCMonth', 'setUTCDate', 'setUTCHours', 7476 'setUTCMinutes', 'setUTCSeconds', 'setUTCMilliseconds']); 7477; 7478/** 7479 * Common Proxy handler for Arrays for both decorators and makeObserved 7480 */ 7481class ArrayProxyHandler { 7482 constructor(isMakeObserved = false) { 7483 this.isMakeObserved_ = isMakeObserved; 7484 } 7485 // decorators work on object that holds the dependencies directly 7486 // makeObserved can't modify the object itself, so it creates a 7487 // wrapper object around it and that will hold the references 7488 // 7489 // this function is used to get the correct object that can be observed 7490 getTarget(obj) { 7491 return this.isMakeObserved_ ? RefInfo.get(obj) : obj; 7492 } 7493 get(target, key, receiver) { 7494 if (typeof key === 'symbol') { 7495 if (key === Symbol.iterator) { 7496 const conditionalTarget = this.getTarget(target); 7497 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7498 return (...args) => target[key](...args); 7499 } 7500 if (key === ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7501 return target; 7502 } 7503 if (this.isMakeObserved_ && key === ObserveV2.SYMBOL_MAKE_OBSERVED) { 7504 return true; 7505 } 7506 return target[key]; 7507 } 7508 7509 const conditionalTarget = this.getTarget(target); 7510 if (key === 'length') { 7511 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7512 return target[key]; 7513 } 7514 // makeObserved logic adds wrapper proxy later 7515 let ret = this.isMakeObserved_ ? target[key] : ObserveV2.autoProxyObject(target, key); 7516 if (typeof (ret) !== 'function') { 7517 ObserveV2.getObserve().addRef(conditionalTarget, key); 7518 return (typeof (ret) === 'object' && this.isMakeObserved_) ? RefInfo.get(ret).proxy : ret; 7519 } 7520 if (ArrayProxyHandler.arrayMutatingFunctions.has(key)) { 7521 return function (...args) { 7522 ret.call(target, ...args); 7523 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7524 // returning the 'receiver(proxied object)' ensures that when chain calls also 2nd function call 7525 // operates on the proxied object. 7526 return receiver; 7527 }; 7528 } 7529 else if (ArrayProxyHandler.arrayLengthChangingFunctions.has(key)) { 7530 return function (...args) { 7531 const result = ret.call(target, ...args); 7532 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7533 return result; 7534 }; 7535 } 7536 else if (!SendableType.isArray(target)) { 7537 return ret.bind(receiver); 7538 } 7539 else if (key === 'forEach') { 7540 // to make ForEach Component and its Item can addref 7541 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7542 return function (callbackFn) { 7543 const result = ret.call(target, (value, index, array) => { 7544 // Collections.Array will report BusinessError: The foreach cannot be bound if call "receiver". 7545 // because the passed parameter is not the instance of the container class. 7546 // so we must call "target" here to deal with the collections situations. 7547 // But we also need to addref for each index. 7548 ObserveV2.getObserve().addRef(conditionalTarget, index.toString()); 7549 callbackFn(typeof value == 'object' ? RefInfo.get(value).proxy : value, index, receiver); 7550 }); 7551 return result; 7552 }; 7553 } 7554 else { 7555 return ret.bind(target); // SendableArray can't be bound -> functions not observed 7556 } 7557 } 7558 set(target, key, value) { 7559 if (typeof key === 'symbol') { 7560 if (!this.isMakeObserved_ && key !== ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7561 target[key] = value; 7562 } 7563 return true; 7564 } 7565 if (target[key] === value) { 7566 return true; 7567 } 7568 const originalLength = target.length; 7569 target[key] = value; 7570 const arrayLenChanged = target.length !== originalLength; 7571 ObserveV2.getObserve().fireChange(this.getTarget(target), arrayLenChanged ? ObserveV2.OB_LENGTH : key.toString()); 7572 return true; 7573 } 7574} 7575// shrinkTo and extendTo is collection.Array api. 7576ArrayProxyHandler.arrayLengthChangingFunctions = new Set(['push', 'pop', 'shift', 'splice', 'unshift', 'shrinkTo', 'extendTo']); 7577ArrayProxyHandler.arrayMutatingFunctions = new Set(['copyWithin', 'fill', 'reverse', 'sort']); 7578; 7579/** 7580 * Common Proxy handler for Maps and Sets for both decorators and makeObserved 7581 */ 7582class SetMapProxyHandler { 7583 constructor(isMakeObserved = false) { 7584 this.isMakeObserved_ = isMakeObserved; 7585 } 7586 // decorators work on object that holds the dependencies directly 7587 // makeObserved can't modify the object itself, so it creates a 7588 // wrapper object around it and that will hold the references 7589 // 7590 // this function is used to get the correct object that can be observed 7591 getTarget(obj) { 7592 return this.isMakeObserved_ ? RefInfo.get(obj) : obj; 7593 } 7594 get(target, key, receiver) { 7595 if (typeof key === 'symbol') { 7596 if (key === Symbol.iterator) { 7597 const conditionalTarget = this.getTarget(target); 7598 ObserveV2.getObserve().fireChange(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7599 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7600 return (...args) => target[key](...args); 7601 } 7602 if (key === ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7603 return target; 7604 } 7605 if (this.isMakeObserved_ && key === ObserveV2.SYMBOL_MAKE_OBSERVED) { 7606 return true; 7607 } 7608 return target[key]; 7609 } 7610 7611 const conditionalTarget = this.getTarget(target); 7612 if (key === 'size') { 7613 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7614 return target[key]; 7615 } 7616 // makeObserved logic adds wrapper proxy later 7617 let ret = this.isMakeObserved_ ? target[key] : ObserveV2.autoProxyObject(target, key); 7618 if (typeof (ret) !== 'function') { 7619 ObserveV2.getObserve().addRef(conditionalTarget, key); 7620 return (typeof (ret) === 'object' && this.isMakeObserved_) ? RefInfo.get(ret).proxy : ret; 7621 } 7622 if (key === 'has') { 7623 return (prop) => { 7624 const ret = target.has(prop); 7625 if (ret) { 7626 ObserveV2.getObserve().addRef(conditionalTarget, prop); 7627 } 7628 else { 7629 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7630 } 7631 return ret; 7632 }; 7633 } 7634 if (key === 'delete') { 7635 return (prop) => { 7636 if (target.has(prop)) { 7637 ObserveV2.getObserve().fireChange(conditionalTarget, prop); 7638 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7639 return target.delete(prop); 7640 } 7641 else { 7642 return false; 7643 } 7644 }; 7645 } 7646 if (key === 'clear') { 7647 return () => { 7648 if (target.size > 0) { 7649 target.forEach((_, prop) => { 7650 ObserveV2.getObserve().fireChange(conditionalTarget, prop.toString()); 7651 }); 7652 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7653 ObserveV2.getObserve().addRef(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7654 target.clear(); 7655 } 7656 }; 7657 } 7658 if (key === 'keys' || key === 'values' || key === 'entries') { 7659 return () => { 7660 ObserveV2.getObserve().addRef(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7661 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7662 return target[key](); 7663 }; 7664 } 7665 if (target instanceof Set || (this.isMakeObserved_ && SendableType.isSet(target))) { 7666 if (key === 'add') { 7667 return (val) => { 7668 ObserveV2.getObserve().fireChange(conditionalTarget, val.toString()); 7669 ObserveV2.getObserve().fireChange(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7670 if (!target.has(val)) { 7671 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7672 target.add(val); 7673 } 7674 return receiver; 7675 }; 7676 } 7677 if (key === 'forEach') { 7678 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7679 return function (callbackFn) { 7680 // need to execute it target because it is the internal function for build-in type, and proxy does not have such slot. 7681 // if necessary, addref for each item in Set and also wrap proxy for makeObserved if it is Object. 7682 // currently, just execute it in target because there is no Component need to iterate Set, only Array 7683 const result = ret.call(target, callbackFn); 7684 return result; 7685 }; 7686 } 7687 // Bind to receiver ==> functions are observed 7688 return (typeof ret === 'function') ? ret.bind(receiver) : ret; 7689 } 7690 if (target instanceof Map || (this.isMakeObserved_ && SendableType.isMap(target))) { 7691 if (key === 'get') { 7692 return (prop) => { 7693 if (target.has(prop)) { 7694 ObserveV2.getObserve().addRef(conditionalTarget, prop); 7695 } 7696 else { 7697 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7698 } 7699 let item = target.get(prop); 7700 return (typeof item === 'object' && this.isMakeObserved_) ? RefInfo.get(item).proxy : item; 7701 }; 7702 } 7703 if (key === 'set') { 7704 return (prop, val) => { 7705 if (!target.has(prop)) { 7706 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7707 } 7708 else if (target.get(prop) !== val) { 7709 ObserveV2.getObserve().fireChange(conditionalTarget, prop); 7710 } 7711 ObserveV2.getObserve().fireChange(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7712 target.set(prop, val); 7713 return receiver; 7714 }; 7715 } 7716 if (key === 'forEach') { 7717 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7718 return function (callbackFn) { 7719 // need to execute it target because it is the internal function for build-in type, and proxy does not have such slot. 7720 // if necessary, addref for each item in Map and also wrap proxy for makeObserved if it is Object. 7721 // currently, just execute it in target because there is no Component need to iterate Map, only Array 7722 const result = ret.call(target, callbackFn); 7723 return result; 7724 }; 7725 } 7726 } 7727 // Bind to receiver ==> functions are observed 7728 return (typeof ret === 'function') ? ret.bind(receiver) : ret; 7729 } 7730 set(target, key, value) { 7731 if (typeof key === 'symbol') { 7732 if (!this.isMakeObserved_ && key !== ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7733 target[key] = value; 7734 } 7735 return true; 7736 } 7737 if (target[key] === value) { 7738 return true; 7739 } 7740 target[key] = value; 7741 ObserveV2.getObserve().fireChange(this.getTarget(target), key.toString()); 7742 return true; 7743 } 7744} 7745SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY = '___ob_map_set'; 7746; 7747/* 7748 * Copyright (c) 2024 Huawei Device Co., Ltd. 7749 * Licensed under the Apache License, Version 2.0 (the "License"); 7750 * you may not use this file except in compliance with the License. 7751 * You may obtain a copy of the License at 7752 * 7753 * http://www.apache.org/licenses/LICENSE-2.0 7754 * 7755 * Unless required by applicable law or agreed to in writing, software 7756 * distributed under the License is distributed on an "AS IS" BASIS, 7757 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7758 * See the License for the specific language governing permissions and 7759 * limitations under the License. 7760 */ 7761/** 7762 * 7763 * This file includes only framework internal classes and functions 7764 * non are part of SDK. Do not access from app. 7765 * 7766 * 7767 * ObserveV2 is the singleton object for observing state variable access and 7768 * change 7769 */ 7770// in the case of ForEach, Repeat, AND If, two or more UINodes / elmtIds can render at the same time 7771// e.g. ForEach -> ForEach child Text, Repeat -> Nested Repeat, child Text 7772// Therefore, ObserveV2 needs to keep a strack of currently renderign ids / components 7773// in the same way as thsi is also done for PU stateMgmt with ViewPU.currentlyRenderedElmtIdStack_ 7774class StackOfRenderedComponents { 7775 constructor() { 7776 this.stack_ = new Array(); 7777 } 7778 push(id, cmp) { 7779 this.stack_.push(new StackOfRenderedComponentsItem(id, cmp)); 7780 } 7781 pop() { 7782 const item = this.stack_.pop(); 7783 return item ? [item.id_, item.cmp_] : undefined; 7784 } 7785 top() { 7786 if (this.stack_.length) { 7787 const item = this.stack_[this.stack_.length - 1]; 7788 return [item.id_, item.cmp_]; 7789 } 7790 else { 7791 return undefined; 7792 } 7793 } 7794} 7795class StackOfRenderedComponentsItem { 7796 constructor(id, cmp) { 7797 this.id_ = id; 7798 this.cmp_ = cmp; 7799 } 7800} 7801class ObserveV2 { 7802 constructor() { 7803 // see MonitorV2.observeObjectAccess: bindCmp is the MonitorV2 7804 // see modified ViewV2 and ViewPU observeComponentCreation, bindCmp is the ViewV2 or ViewPU 7805 // bindId: UINode elmtId or watchId, depending on what is being observed 7806 this.stackOfRenderedComponents_ = new StackOfRenderedComponents(); 7807 // Map bindId to WeakRef<ViewPU> | MonitorV2 7808 this.id2cmp_ = {}; 7809 // Map bindId -> Set of @observed class objects 7810 // reverse dependency map for quickly removing all dependencies of a bindId 7811 this.id2targets_ = {}; 7812 // queued up Set of bindId 7813 // elmtIds of UINodes need re-render 7814 // @monitor functions that need to execute 7815 this.elmtIdsChanged_ = new Set(); 7816 this.computedPropIdsChanged_ = new Set(); 7817 this.monitorIdsChanged_ = new Set(); 7818 this.persistenceChanged_ = new Set(); 7819 // avoid recursive execution of updateDirty 7820 // by state changes => fireChange while 7821 // UINode rerender or @monitor function execution 7822 this.startDirty_ = false; 7823 // flag to indicate change observation is disabled 7824 this.disabled_ = false; 7825 // flag to indicate ComputedV2 calculation is ongoing 7826 this.calculatingComputedProp_ = false; 7827 } 7828 static getObserve() { 7829 if (!this.obsInstance_) { 7830 this.obsInstance_ = new ObserveV2(); 7831 } 7832 return this.obsInstance_; 7833 } 7834 // return true given value is @observed object 7835 static IsObservedObjectV2(value) { 7836 return (value && typeof (value) === 'object' && value[ObserveV2.V2_DECO_META]); 7837 } 7838 // return true if given value is proxied observed object, either makeObserved or autoProxyObject 7839 static IsProxiedObservedV2(value) { 7840 return (value && typeof value === 'object' && value[ObserveV2.SYMBOL_PROXY_GET_TARGET]); 7841 } 7842 // return true given value is the return value of makeObserved 7843 static IsMakeObserved(value) { 7844 return (value && typeof (value) === 'object' && value[ObserveV2.SYMBOL_MAKE_OBSERVED]); 7845 } 7846 static getCurrentRecordedId() { 7847 const bound = ObserveV2.getObserve().stackOfRenderedComponents_.top(); 7848 return bound ? bound[0] : -1; 7849 } 7850 // At the start of observeComponentCreation or 7851 // MonitorV2 observeObjectAccess 7852 startRecordDependencies(cmp, id, doClearBinding = true) { 7853 if (cmp != null) { 7854 doClearBinding && this.clearBinding(id); 7855 this.stackOfRenderedComponents_.push(id, cmp); 7856 } 7857 } 7858 // At the start of observeComponentCreation or 7859 // MonitorV2 observeObjectAccess 7860 stopRecordDependencies() { 7861 const bound = this.stackOfRenderedComponents_.pop(); 7862 if (bound === undefined) { 7863 stateMgmtConsole.error('stopRecordDependencies finds empty stack. Internal error!'); 7864 return; 7865 } 7866 let targetsSet; 7867 if ((targetsSet = this.id2targets_[bound[0]]) !== undefined && targetsSet.size) { 7868 // only add IView | MonitorV2 | ComputedV2 if at least one dependency was 7869 // recorded when rendering this ViewPU/ViewV2/Monitor/ComputedV2 7870 // ViewPU is the likely case where no dependecy gets recorded 7871 // for others no dependencies are unlikely to happen 7872 this.id2cmp_[bound[0]] = new WeakRef(bound[1]); 7873 } 7874 } 7875 // clear any previously created dependency view model object to elmtId 7876 // find these view model objects with the reverse map id2targets_ 7877 clearBinding(id) { 7878 var _a; 7879 // multiple weakRefs might point to the same target - here we get Set of unique targets 7880 const targetSet = new Set(); 7881 (_a = this.id2targets_[id]) === null || _a === void 0 ? void 0 : _a.forEach((weak) => { 7882 if (weak.deref() instanceof Object) { 7883 targetSet.add(weak.deref()); 7884 } 7885 }); 7886 targetSet.forEach((target) => { 7887 var _a, _b; 7888 const idRefs = target[ObserveV2.ID_REFS]; 7889 const symRefs = target[ObserveV2.SYMBOL_REFS]; 7890 if (idRefs) { 7891 (_a = idRefs[id]) === null || _a === void 0 ? void 0 : _a.forEach(key => { var _a; return (_a = symRefs === null || symRefs === void 0 ? void 0 : symRefs[key]) === null || _a === void 0 ? void 0 : _a.delete(id); }); 7892 delete idRefs[id]; 7893 } 7894 else { 7895 for (let key in symRefs) { 7896 (_b = symRefs[key]) === null || _b === void 0 ? void 0 : _b.delete(id); 7897 } 7898 ; 7899 } 7900 }); 7901 delete this.id2targets_[id]; 7902 delete this.id2cmp_[id]; 7903 7904 7905 } 7906 /** 7907 * 7908 * this cleanUpId2CmpDeadReferences() 7909 * id2cmp is a 'map' object id => WeakRef<Object> where object is ViewV2, ViewPU, MonitorV2 or ComputedV2 7910 * This method iterates over the object entries and deleted all those entries whose value can no longer 7911 * be deref'ed. 7912 * 7913 * cleanUpId2TargetsDeadReferences() 7914 * is2targets is a 'map' object id => Set<WeakRef<Object>> 7915 * the method traverses over the object entries and for each value of type 7916 * Set<WeakRef<Object>> removes all those items from the set that can no longer be deref'ed. 7917 * 7918 * According to JS specifications, it is up to ArlTS runtime GC implementation when to collect unreferences objects. 7919 * Parameters such as available memory, ArkTS processing load, number and size of all JS objects for GC collection 7920 * can impact the time delay between an object loosing last reference and GC collecting this object. 7921 * 7922 * WeakRef deref() returns the object until GC has collected it. 7923 * The id2cmp and is2targets cleanup herein depends on WeakRef.deref() to return undefined, i.e. it depends on GC 7924 * collecting 'cmp' or 'target' objects. Only then the algorithm can remove the entry from id2cmp / from id2target. 7925 * It is therefore to be expected behavior that these map objects grow and they a contain a larger number of 7926 * MonitorV2, ComputedV2, and/or view model @Observed class objects that are no longer used / referenced by the application. 7927 * Only after ArkTS runtime GC has collected them, this function is able to clean up the id2cmp and is2targets. 7928 * 7929 * This cleanUpDeadReferences() function gets called from UINodeRegisterProxy.uiNodeCleanUpIdleTask() 7930 * 7931 */ 7932 cleanUpDeadReferences() { 7933 this.cleanUpId2CmpDeadReferences(); 7934 this.cleanUpId2TargetsDeadReferences(); 7935 } 7936 cleanUpId2CmpDeadReferences() { 7937 7938 for (const id in this.id2cmp_) { 7939 7940 let weakRef = this.id2cmp_[id]; 7941 if (weakRef && typeof weakRef === 'object' && 'deref' in weakRef && weakRef.deref() === undefined) { 7942 7943 delete this.id2cmp_[id]; 7944 } 7945 } 7946 } 7947 cleanUpId2TargetsDeadReferences() { 7948 for (const id in this.id2targets_) { 7949 const targetSet = this.id2targets_[id]; 7950 if (targetSet && targetSet instanceof Set) { 7951 for (let weakTarget of targetSet) { 7952 if (weakTarget.deref() === undefined) { 7953 7954 targetSet.delete(weakTarget); 7955 } 7956 } // for targetSet 7957 } 7958 } // for id2targets_ 7959 } 7960 /** 7961 * counts number of WeakRef<Object> entries in id2cmp_ 'map' object 7962 * @returns total count and count of WeakRefs that can be deref'ed 7963 * Methods only for testing 7964 */ 7965 get id2CompDeRefSize() { 7966 let totalCount = 0; 7967 let aliveCount = 0; 7968 let comp; 7969 for (const id in this.id2cmp_) { 7970 totalCount++; 7971 let weakRef = this.id2cmp_[id]; 7972 if (weakRef && 'deref' in weakRef && (comp = weakRef.deref()) && comp instanceof Object) { 7973 aliveCount++; 7974 } 7975 } 7976 return [totalCount, aliveCount]; 7977 } 7978 /** counts number of target WeakRef<object> entries in all the Sets inside id2targets 'map' object 7979 * @returns total count and those can be dereferenced 7980 * Methods only for testing 7981 */ 7982 get id2TargetsDerefSize() { 7983 let totalCount = 0; 7984 let aliveCount = 0; 7985 for (const id in this.id2targets_) { 7986 const targetSet = this.id2targets_[id]; 7987 if (targetSet && targetSet instanceof Set) { 7988 for (let weakTarget of targetSet) { 7989 totalCount++; 7990 if (weakTarget.deref()) { 7991 aliveCount++; 7992 } 7993 } // for targetSet 7994 } 7995 } // for id2targets_ 7996 return [totalCount, aliveCount]; 7997 } 7998 // add dependency view model object 'target' property 'attrName' 7999 // to current this.bindId 8000 addRef(target, attrName) { 8001 const bound = this.stackOfRenderedComponents_.top(); 8002 if (!bound) { 8003 return; 8004 } 8005 if (bound[0] === UINodeRegisterProxy.monitorIllegalV2V3StateAccess) { 8006 const error = `${attrName}: ObserveV2.addRef: trying to use V3 state '${attrName}' to init/update child V2 @Component. Application error`; 8007 stateMgmtConsole.applicationError(error); 8008 throw new TypeError(error); 8009 } 8010 8011 this.addRef4IdInternal(bound[0], target, attrName); 8012 } 8013 addRef4Id(id, target, attrName) { 8014 8015 this.addRef4IdInternal(id, target, attrName); 8016 } 8017 addRef4IdInternal(id, target, attrName) { 8018 var _a, _b, _c, _d; 8019 var _e, _f; 8020 // Map: attribute/symbol -> dependent id 8021 const symRefs = (_a = target[_e = ObserveV2.SYMBOL_REFS]) !== null && _a !== void 0 ? _a : (target[_e] = {}); 8022 (_b = symRefs[attrName]) !== null && _b !== void 0 ? _b : (symRefs[attrName] = new Set()); 8023 symRefs[attrName].add(id); 8024 // Map id -> attribute/symbol 8025 // optimization for faster clearBinding 8026 const idRefs = target[ObserveV2.ID_REFS]; 8027 if (idRefs) { 8028 (_c = idRefs[id]) !== null && _c !== void 0 ? _c : (idRefs[id] = new Set()); 8029 idRefs[id].add(attrName); 8030 } 8031 const targetSet = (_d = (_f = this.id2targets_)[id]) !== null && _d !== void 0 ? _d : (_f[id] = new Set()); 8032 targetSet.add(new WeakRef(target)); 8033 } 8034 /** 8035 * 8036 * @param target set tracked attribute to new value without notifying the change 8037 * !! use with caution !! 8038 * @param attrName 8039 * @param newValue 8040 */ 8041 setUnmonitored(target, attrName, newValue) { 8042 const storeProp = ObserveV2.OB_PREFIX + attrName; 8043 if (storeProp in target) { 8044 // @track attrName 8045 8046 target[storeProp] = newValue; 8047 } 8048 else { 8049 8050 // untracked attrName 8051 target[attrName] = newValue; 8052 } 8053 } 8054 /** 8055 * Execute given task while state change observation is disabled 8056 * A state mutation caused by the task will NOT trigger UI rerender 8057 * and @monitor function execution. 8058 * 8059 * !!! Use with Caution !!! 8060 * 8061 * @param task a function to execute without monitoring state changes 8062 * @returns task function return value 8063 */ 8064 executeUnobserved(task) { 8065 8066 this.disabled_ = true; 8067 let ret; 8068 try { 8069 ret = task(); 8070 } 8071 catch (e) { 8072 stateMgmtConsole.applicationError(`executeUnobserved - task execution caused error ${e} !`); 8073 } 8074 this.disabled_ = false; 8075 8076 return ret; 8077 } 8078 // mark view model object 'target' property 'attrName' as changed 8079 // notify affected watchIds and elmtIds 8080 fireChange(target, attrName) { 8081 // enable to get more fine grained traces 8082 // including 2 (!) .end calls. 8083 if (!target[ObserveV2.SYMBOL_REFS] || this.disabled_) { 8084 return; 8085 } 8086 const bound = this.stackOfRenderedComponents_.top(); 8087 if (this.calculatingComputedProp_) { 8088 const prop = bound ? bound[1].getProp() : 'unknown computed property'; 8089 const error = `Usage of ILLEGAL @Computed function detected for ${prop}! The @Computed function MUST NOT change the state of any observed state variable!`; 8090 stateMgmtConsole.applicationError(error); 8091 throw new Error(error); 8092 } 8093 // enable this trace marker for more fine grained tracing of the update pipeline 8094 // note: two (!) end markers need to be enabled 8095 let changedIdSet = target[ObserveV2.SYMBOL_REFS][attrName]; 8096 if (!changedIdSet || !(changedIdSet instanceof Set)) { 8097 return; 8098 } 8099 8100 for (const id of changedIdSet) { 8101 // Cannot fireChange the object that is being created. 8102 if (bound && id === bound[0]) { 8103 continue; 8104 } 8105 // if this is the first id to be added to any Set of changed ids, 8106 // schedule an 'updateDirty' task 8107 // that will run after the current call stack has unwound. 8108 // purpose of check for startDirty_ is to avoid going into recursion. This could happen if 8109 // exec a re-render or exec a monitor function changes some state -> calls fireChange -> ... 8110 if ((this.elmtIdsChanged_.size + this.monitorIdsChanged_.size + this.computedPropIdsChanged_.size === 0) && 8111 /* update not already in progress */ !this.startDirty_) { 8112 Promise.resolve() 8113 .then(this.updateDirty.bind(this)) 8114 .catch(error => { 8115 stateMgmtConsole.applicationError(`Exception occurred during the update process involving @Computed properties, @Monitor functions or UINode re-rendering`, error); 8116 _arkUIUncaughtPromiseError(error); 8117 }); 8118 } 8119 // add bindId to the correct Set of pending changes. 8120 if (id < ComputedV2.MIN_COMPUTED_ID) { 8121 this.elmtIdsChanged_.add(id); 8122 } 8123 else if (id < MonitorV2.MIN_WATCH_ID) { 8124 this.computedPropIdsChanged_.add(id); 8125 } 8126 else if (id < PersistenceV2Impl.MIN_PERSISTENCE_ID) { 8127 this.monitorIdsChanged_.add(id); 8128 } 8129 else { 8130 this.persistenceChanged_.add(id); 8131 } 8132 } // for 8133 } 8134 updateDirty() { 8135 this.startDirty_ = true; 8136 this.updateDirty2(false); 8137 this.startDirty_ = false; 8138 } 8139 /** 8140 * execute /update in this order 8141 * - @Computed variables 8142 * - @Monitor functions 8143 * - UINode re-render 8144 * three nested loops, means: 8145 * process @Computed until no more @Computed need update 8146 * process @Monitor until no more @Computed and @Monitor 8147 * process UINode update until no more @Computed and @Monitor and UINode rerender 8148 * 8149 * @param updateUISynchronously should be set to true if called during VSYNC only 8150 * 8151 */ 8152 updateDirty2(updateUISynchronously = false) { 8153 aceTrace.begin('updateDirty2'); 8154 8155 // obtain and unregister the removed elmtIds 8156 UINodeRegisterProxy.obtainDeletedElmtIds(); 8157 UINodeRegisterProxy.unregisterElmtIdsFromIViews(); 8158 // priority order of processing: 8159 // 1- update computed properties until no more need computed props update 8160 // 2- update monitors until no more monitors and no more computed props 8161 // 3- update UINodes until no more monitors, no more computed props, and no more UINodes 8162 // FIXME prevent infinite loops 8163 do { 8164 do { 8165 while (this.computedPropIdsChanged_.size) { 8166 // sort the ids and update in ascending order 8167 // If a @Computed property depends on other @Computed properties, their 8168 // ids will be smaller as they are defined first. 8169 const computedProps = Array.from(this.computedPropIdsChanged_).sort((id1, id2) => id1 - id2); 8170 this.computedPropIdsChanged_ = new Set(); 8171 this.updateDirtyComputedProps(computedProps); 8172 } 8173 if (this.persistenceChanged_.size) { 8174 const persistKeys = Array.from(this.persistenceChanged_); 8175 this.persistenceChanged_ = new Set(); 8176 PersistenceV2Impl.instance().onChangeObserved(persistKeys); 8177 } 8178 if (this.monitorIdsChanged_.size) { 8179 const monitors = this.monitorIdsChanged_; 8180 this.monitorIdsChanged_ = new Set(); 8181 this.updateDirtyMonitors(monitors); 8182 } 8183 } while (this.monitorIdsChanged_.size + this.persistenceChanged_.size + this.computedPropIdsChanged_.size > 0); 8184 if (this.elmtIdsChanged_.size) { 8185 const elmtIds = Array.from(this.elmtIdsChanged_).sort((elmtId1, elmtId2) => elmtId1 - elmtId2); 8186 this.elmtIdsChanged_ = new Set(); 8187 updateUISynchronously ? this.updateUINodesSynchronously(elmtIds) : this.updateUINodes(elmtIds); 8188 } 8189 } while (this.elmtIdsChanged_.size + this.monitorIdsChanged_.size + this.computedPropIdsChanged_.size > 0); 8190 8191 aceTrace.end(); 8192 } 8193 updateDirtyComputedProps(computed) { 8194 8195 aceTrace.begin(`ObservedV2.updateDirtyComputedProps ${computed.length} @Computed`); 8196 computed.forEach((id) => { 8197 let comp; 8198 let weakComp = this.id2cmp_[id]; 8199 if (weakComp && 'deref' in weakComp && (comp = weakComp.deref()) && comp instanceof ComputedV2) { 8200 const target = comp.getTarget(); 8201 if (target instanceof ViewV2 && !target.isViewActive()) { 8202 // add delayed ComputedIds id 8203 target.addDelayedComputedIds(id); 8204 } 8205 else { 8206 comp.fireChange(); 8207 } 8208 } 8209 }); 8210 aceTrace.end(); 8211 } 8212 updateDirtyMonitors(monitors) { 8213 8214 aceTrace.begin(`ObservedV3.updateDirtyMonitors: ${Array.from(monitors).length} @monitor`); 8215 let weakMonitor; 8216 let monitor; 8217 let monitorTarget; 8218 monitors.forEach((watchId) => { 8219 weakMonitor = this.id2cmp_[watchId]; 8220 if (weakMonitor && 'deref' in weakMonitor && (monitor = weakMonitor.deref()) && monitor instanceof MonitorV2) { 8221 if (((monitorTarget = monitor.getTarget()) instanceof ViewV2) && !monitorTarget.isViewActive()) { 8222 // monitor notifyChange delayed if target is a View that is not active 8223 monitorTarget.addDelayedMonitorIds(watchId); 8224 } 8225 else { 8226 monitor.notifyChange(); 8227 } 8228 } 8229 }); 8230 aceTrace.end(); 8231 } 8232 /** 8233 * This version of UpdateUINodes does not wait for VSYNC, violates rules 8234 * calls UpdateElement, thereby avoids the long and frequent code path from 8235 * FlushDirtyNodesUpdate to CustomNode to ViewV2.updateDirtyElements to UpdateElement 8236 * Code left here to reproduce benchmark measurements, compare with future optimisation 8237 * @param elmtIds 8238 * 8239 */ 8240 updateUINodesSynchronously(elmtIds) { 8241 8242 aceTrace.begin(`ObserveV2.updateUINodesSynchronously: ${elmtIds.length} elmtId`); 8243 let view; 8244 let weak; 8245 elmtIds.forEach((elmtId) => { 8246 if ((weak = this.id2cmp_[elmtId]) && (typeof weak === 'object') && ('deref' in weak) && 8247 (view = weak.deref()) && ((view instanceof ViewV2) || (view instanceof ViewPU))) { 8248 if (view.isViewActive()) { 8249 // FIXME need to call syncInstanceId before update? 8250 view.UpdateElement(elmtId); 8251 } 8252 else { 8253 // schedule delayed update once the view gets active 8254 view.scheduleDelayedUpdate(elmtId); 8255 } 8256 } // if ViewV2 or ViewPU 8257 }); 8258 aceTrace.end(); 8259 } 8260 // This is the code path similar to V2, follows the rule that UI updates on VSYNC. 8261 // ViewPU/ViewV2 queues the elmtId that need update, marks the CustomNode dirty in RenderContext 8262 // On next VSYNC runs FlushDirtyNodesUpdate to call rerender to call UpdateElement. Much longer code path 8263 // much slower 8264 updateUINodes(elmtIds) { 8265 8266 aceTrace.begin(`ObserveV2.updateUINodes: ${elmtIds.length} elmtId`); 8267 let viewWeak; 8268 let view; 8269 elmtIds.forEach((elmtId) => { 8270 viewWeak = this.id2cmp_[elmtId]; 8271 if (viewWeak && 'deref' in viewWeak && (view = viewWeak.deref()) && 8272 ((view instanceof ViewV2) || (view instanceof ViewPU))) { 8273 if (view.isViewActive()) { 8274 view.uiNodeNeedUpdateV3(elmtId); 8275 } 8276 else if (view instanceof ViewV2) { 8277 // schedule delayed update once the view gets active 8278 view.scheduleDelayedUpdate(elmtId); 8279 } 8280 } 8281 }); 8282 aceTrace.end(); 8283 } 8284 constructMonitor(owningObject, owningObjectName) { 8285 let watchProp = Symbol.for(MonitorV2.WATCH_PREFIX + owningObjectName); 8286 if (owningObject && (typeof owningObject === 'object') && owningObject[watchProp]) { 8287 Object.entries(owningObject[watchProp]).forEach(([pathString, monitorFunc]) => { 8288 var _a; 8289 var _b; 8290 if (monitorFunc && pathString && typeof monitorFunc === 'function') { 8291 const monitor = new MonitorV2(owningObject, pathString, monitorFunc); 8292 monitor.InitRun(); 8293 const refs = (_a = owningObject[_b = ObserveV2.MONITOR_REFS]) !== null && _a !== void 0 ? _a : (owningObject[_b] = {}); 8294 // store a reference inside owningObject 8295 // thereby MonitorV2 will share lifespan as owning @ComponentV2 or @ObservedV2 8296 // remember: id2cmp only has a WeakRef to MonitorV2 obj 8297 refs[monitorFunc.name] = monitor; 8298 } 8299 // FIXME Else handle error 8300 }); 8301 } // if target[watchProp] 8302 } 8303 constructComputed(owningObject, owningObjectName) { 8304 const computedProp = Symbol.for(ComputedV2.COMPUTED_PREFIX + owningObjectName); 8305 if (owningObject && (typeof owningObject === 'object') && owningObject[computedProp]) { 8306 Object.entries(owningObject[computedProp]).forEach(([computedPropertyName, computeFunc]) => { 8307 var _a, _b; 8308 var _c; 8309 8310 const computed = new ComputedV2(owningObject, computedPropertyName, computeFunc); 8311 computed.InitRun(); 8312 const refs = (_b = owningObject[_c = ObserveV2.COMPUTED_REFS]) !== null && _b !== void 0 ? _b : (owningObject[_c] = {}); 8313 // store a reference inside owningObject 8314 // thereby ComputedV2 will share lifespan as owning @ComponentV2 or @ObservedV2 8315 // remember: id2cmp only has a WeakRef to ComputedV2 obj 8316 refs[computedPropertyName] = computed; 8317 }); 8318 } 8319 } 8320 clearWatch(id) { 8321 this.clearBinding(id); 8322 } 8323 static autoProxyObject(target, key) { 8324 let val = target[key]; 8325 // Not an object, not a collection, no proxy required 8326 if (!val || typeof (val) !== 'object' || 8327 !(Array.isArray(val) || val instanceof Set || val instanceof Map || val instanceof Date)) { 8328 return val; 8329 } 8330 // Only collections require proxy observation, and if it has been observed, it does not need to be observed again. 8331 if (!val[ObserveV2.SYMBOL_PROXY_GET_TARGET]) { 8332 if (Array.isArray(val)) { 8333 target[key] = new Proxy(val, ObserveV2.arrayProxy); 8334 } 8335 else if (val instanceof Set || val instanceof Map) { 8336 target[key] = new Proxy(val, ObserveV2.setMapProxy); 8337 } 8338 else { 8339 target[key] = new Proxy(val, ObserveV2.objectProxy); 8340 } 8341 val = target[key]; 8342 } 8343 // If the return value is an Array, Set, Map 8344 if (!(val instanceof Date)) { 8345 ObserveV2.getObserve().addRef(ObserveV2.IsMakeObserved(val) ? RefInfo.get(UIUtilsImpl.instance().getTarget(val)) : 8346 val, ObserveV2.OB_LENGTH); 8347 } 8348 return val; 8349 } 8350 /** 8351 * Helper function to add meta data about decorator to ViewPU or ViewV2 8352 * @param proto prototype object of application class derived from ViewPU or ViewV2 8353 * @param varName decorated variable 8354 * @param deco '@state', '@event', etc (note '@model' gets transpiled in '@param' and '@event') 8355 */ 8356 static addVariableDecoMeta(proto, varName, deco) { 8357 var _a; 8358 var _b; 8359 // add decorator meta data 8360 const meta = (_a = proto[_b = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (proto[_b] = {}); 8361 meta[varName] = {}; 8362 meta[varName].deco = deco; 8363 // FIXME 8364 // when splitting ViewPU and ViewV3 8365 // use instanceOf. Until then, this is a workaround. 8366 // any @state, @track, etc V3 event handles this function to return false 8367 Reflect.defineProperty(proto, 'isViewV3', { 8368 get() { return true; }, 8369 enumerable: false 8370 }); 8371 } 8372 static addParamVariableDecoMeta(proto, varName, deco, deco2) { 8373 var _a, _b; 8374 var _c; 8375 // add decorator meta data 8376 const meta = (_a = proto[_c = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (proto[_c] = {}); 8377 (_b = meta[varName]) !== null && _b !== void 0 ? _b : (meta[varName] = {}); 8378 if (deco) { 8379 meta[varName].deco = deco; 8380 } 8381 if (deco2) { 8382 meta[varName].deco2 = deco2; 8383 } 8384 // FIXME 8385 // when splitting ViewPU and ViewV3 8386 // use instanceOf. Until then, this is a workaround. 8387 // any @state, @track, etc V3 event handles this function to return false 8388 Reflect.defineProperty(proto, 'isViewV3', { 8389 get() { return true; }, 8390 enumerable: false 8391 }); 8392 } 8393 static usesV3Variables(proto) { 8394 return (proto && typeof proto === 'object' && proto[ObserveV2.V2_DECO_META]); 8395 } 8396} // class ObserveV2 8397// meta data about decorated variable inside prototype 8398ObserveV2.V2_DECO_META = Symbol('__v2_deco_meta__'); 8399ObserveV2.SYMBOL_REFS = Symbol('__use_refs__'); 8400ObserveV2.ID_REFS = Symbol('__id_refs__'); 8401ObserveV2.MONITOR_REFS = Symbol('___monitor_refs_'); 8402ObserveV2.COMPUTED_REFS = Symbol('___computed_refs_'); 8403ObserveV2.SYMBOL_PROXY_GET_TARGET = Symbol('__proxy_get_target'); 8404ObserveV2.SYMBOL_MAKE_OBSERVED = Symbol('___make_observed__'); 8405ObserveV2.OB_PREFIX = '__ob_'; // OB_PREFIX + attrName => backing store attribute name 8406ObserveV2.OB_PREFIX_LEN = 5; 8407// used by array Handler to create dependency on artificial 'length' 8408// property of array, mark it as changed when array has changed. 8409ObserveV2.OB_LENGTH = '___obj_length'; 8410ObserveV2.setMapProxy = new SetMapProxyHandler(); 8411ObserveV2.arrayProxy = new ArrayProxyHandler(); 8412ObserveV2.objectProxy = new ObjectProxyHandler(); 8413const trackInternal = (target, propertyKey) => { 8414 var _a; 8415 var _b; 8416 if (typeof target === 'function' && !Reflect.has(target, propertyKey)) { 8417 // dynamic track,and it not a static attribute 8418 target = target.prototype; 8419 } 8420 const storeProp = ObserveV2.OB_PREFIX + propertyKey; 8421 target[storeProp] = target[propertyKey]; 8422 Reflect.defineProperty(target, propertyKey, { 8423 get() { 8424 ObserveV2.getObserve().addRef(this, propertyKey); 8425 return ObserveV2.autoProxyObject(this, ObserveV2.OB_PREFIX + propertyKey); 8426 }, 8427 set(val) { 8428 // If the object has not been observed, you can directly assign a value to it. This improves performance. 8429 if (val !== this[storeProp]) { 8430 this[storeProp] = val; 8431 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 8432 ObserveV2.getObserve().fireChange(this, propertyKey); 8433 } 8434 } 8435 }, 8436 enumerable: true 8437 }); 8438 // this marks the proto as having at least one @track property inside 8439 // used by IsObservedObjectV2 8440 (_a = target[_b = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (target[_b] = {}); 8441}; // trackInternal 8442/* 8443 * Copyright (c) 2024 Huawei Device Co., Ltd. 8444 * Licensed under the Apache License, Version 2.0 (the "License"); 8445 * you may not use this file except in compliance with the License. 8446 * You may obtain a copy of the License at 8447 * 8448 * http://www.apache.org/licenses/LICENSE-2.0 8449 * 8450 * Unless required by applicable law or agreed to in writing, software 8451 * distributed under the License is distributed on an "AS IS" BASIS, 8452 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8453 * See the License for the specific language governing permissions and 8454 * limitations under the License. 8455 */ 8456/** 8457 * 8458 * This file includes only framework internal classes and functions 8459 * non are part of SDK. Do not access from app. 8460 * 8461 * 8462 * Helper class for handling V2 decorated variables 8463 */ 8464class VariableUtilV3 { 8465 /** 8466 * setReadOnlyAttr - helper function used to update @param 8467 * from parent @Component. Not allowed for @param @once . 8468 * @param target - the object, usually the ViewV2 8469 * @param attrName - @param variable name 8470 * @param newValue - update to new value 8471 */ 8472 static initParam(target, attrName, newValue) { 8473 var _a; 8474 const meta = (_a = target[ObserveV2.V2_DECO_META]) === null || _a === void 0 ? void 0 : _a[attrName]; 8475 if (!meta || meta.deco !== '@param') { 8476 const error = `Use initParam(${attrName}) only to init @param. Internal error!`; 8477 stateMgmtConsole.error(error); 8478 throw new Error(error); 8479 } 8480 // prevent update for @param @once 8481 const storeProp = ObserveV2.OB_PREFIX + attrName; 8482 8483 target[storeProp] = newValue; 8484 ObserveV2.getObserve().addRef(target, attrName); 8485 } 8486 /** 8487 * setReadOnlyAttr - helper function used to update @param 8488 * from parent @Component. Not allowed for @param @once . 8489 * @param target - the object, usually the ViewV2 8490 * @param attrName - @param variable name 8491 * @param newValue - update to new value 8492 */ 8493 static updateParam(target, attrName, newValue) { 8494 var _a; 8495 // prevent update for @param @once 8496 const meta = (_a = target[ObserveV2.V2_DECO_META]) === null || _a === void 0 ? void 0 : _a[attrName]; 8497 if (!meta || meta.deco !== '@param') { 8498 const error = `Use updateParm(${attrName}) only to update @param. Internal error!`; 8499 stateMgmtConsole.error(error); 8500 throw new Error(error); 8501 } 8502 const storeProp = ObserveV2.OB_PREFIX + attrName; 8503 // @observed class and @track attrName 8504 if (newValue === target[storeProp]) { 8505 8506 return; 8507 } 8508 if (meta.deco2 === '@once') { 8509 // @param @once - init but no update 8510 8511 } 8512 else { 8513 8514 target[storeProp] = newValue; 8515 ObserveV2.getObserve().fireChange(target, attrName); 8516 } 8517 } 8518} 8519class ProviderConsumerUtilV2 { 8520 /** 8521 * meta added to the ViewV2 8522 * varName: { deco: '@Provider' | '@Consumer', aliasName: ..... } 8523 * prefix_@Provider_aliasName: {'varName': ..., 'aliasName': ...., 'deco': '@Provider' | '@Consumer' 8524 */ 8525 static metaAliasKey(aliasName, deco) { 8526 return `${ProviderConsumerUtilV2.ALIAS_PREFIX}_${deco}_${aliasName}`; 8527 } 8528 /** 8529 * Helper function to add meta data about @Provider and @Consumer decorators to ViewV2 8530 * similar to @see addVariableDecoMeta, but adds the alias to allow search from @Consumer for @Provider counterpart 8531 * @param proto prototype object of application class derived from ViewV2 8532 * @param varName decorated variable 8533 * @param deco '@state', '@event', etc (note '@model' gets transpiled in '@param' and '@event') 8534 */ 8535 static addProvideConsumeVariableDecoMeta(proto, varName, aliasName, deco) { 8536 var _a; 8537 var _b; 8538 // add decorator meta data to prototype 8539 const meta = (_a = proto[_b = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (proto[_b] = {}); 8540 // note: aliasName is the actual alias not the prefixed version 8541 meta[varName] = { 'deco': deco, 'aliasName': aliasName }; 8542 // prefix to avoid name collisions with variable of same name as the alias! 8543 const aliasProp = ProviderConsumerUtilV2.metaAliasKey(aliasName, deco); 8544 meta[aliasProp] = { 'varName': varName, 'aliasName': aliasName, 'deco': deco }; 8545 } 8546 /** 8547 * find a @Provider'ed variable from its nearest ancestor ViewV2. 8548 * @param searchingAliasName The key name to search for. 8549 * @returns A tuple containing the ViewPU instance where the provider is found 8550 * and the provider name 8551 * If root @Component reached without finding, returns undefined. 8552 */ 8553 static findProvider(view, aliasName) { 8554 var _a; 8555 let checkView = view === null || view === void 0 ? void 0 : view.getParent(); 8556 const searchingPrefixedAliasName = ProviderConsumerUtilV2.metaAliasKey(aliasName, '@Provider'); 8557 8558 while (checkView) { 8559 const meta = (_a = checkView.constructor) === null || _a === void 0 ? void 0 : _a.prototype[ObserveV2.V2_DECO_META]; 8560 if (checkView instanceof ViewV2 && meta && meta[searchingPrefixedAliasName]) { 8561 const aliasMeta = meta[searchingPrefixedAliasName]; 8562 const providedVarName = (aliasMeta && (aliasMeta.deco === '@Provider') ? aliasMeta.varName : undefined); 8563 if (providedVarName) { 8564 8565 return [checkView, providedVarName]; 8566 } 8567 } 8568 checkView = checkView.getParent(); 8569 } 8570 ; // while 8571 stateMgmtConsole.warn(`findProvider: ${view.debugInfo__()} @Consumer('${aliasName}'), no matching @Provider found amongst ancestor @ComponentV2's!`); 8572 return undefined; 8573 } 8574 /** 8575 * Connects a consumer property of a view (`consumeView`) to a provider property of another view (`provideView`). 8576 * This function establishes a link between the consumer and provider, allowing the consumer to access and update 8577 * the provider's value directly. If the provider view is garbage collected, attempts to access the provider 8578 * property will throw an error. 8579 * 8580 * @param consumeView - The view object that consumes data from the provider. 8581 * @param consumeVarName - The name of the property in the consumer view that will be linked to the provider. 8582 * @param provideView - The view object that provides the data to the consumer. 8583 * @param provideVarName - The name of the property in the provider view that the consumer will access. 8584 * 8585 */ 8586 static connectConsumer2Provider(consumeView, consumeVarName, provideView, provideVarName) { 8587 var _a; 8588 const weakView = new WeakRef(provideView); 8589 const provideViewName = (_a = provideView.constructor) === null || _a === void 0 ? void 0 : _a.name; 8590 Reflect.defineProperty(consumeView, consumeVarName, { 8591 get() { 8592 let view = weakView.deref(); 8593 8594 ObserveV2.getObserve().addRef(this, consumeVarName); 8595 if (!view) { 8596 const error = `${this.debugInfo__()}: get() on @Consumer ${consumeVarName}: providing @ComponentV2 with @Provider ${provideViewName} no longer exists. Application error.`; 8597 stateMgmtConsole.error(error); 8598 throw new Error(error); 8599 } 8600 return view[provideVarName]; 8601 }, 8602 set(val) { 8603 let view = weakView.deref(); 8604 // If the object has not been observed, you can directly assign a value to it. This improves performance. 8605 8606 if (!view) { 8607 const error = `${this.debugInfo__()}: set() on @Consumer ${consumeVarName}: providing @ComponentV2 with @Provider ${provideViewName} no longer exists. Application error.`; 8608 stateMgmtConsole.error(error); 8609 throw new Error(error); 8610 } 8611 if (val !== view[provideVarName]) { 8612 8613 view[provideVarName] = val; 8614 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 8615 ObserveV2.getObserve().fireChange(this, consumeVarName); 8616 } 8617 } 8618 }, 8619 enumerable: true 8620 }); 8621 return provideView[provideVarName]; 8622 } 8623 static defineConsumerWithoutProvider(consumeView, consumeVarName, consumerLocalVal) { 8624 8625 const storeProp = ObserveV2.OB_PREFIX + consumeVarName; 8626 consumeView[storeProp] = consumerLocalVal; // use local init value, also as backing store 8627 Reflect.defineProperty(consumeView, consumeVarName, { 8628 get() { 8629 ObserveV2.getObserve().addRef(this, consumeVarName); 8630 return ObserveV2.autoProxyObject(this, ObserveV2.OB_PREFIX + consumeVarName); 8631 }, 8632 set(val) { 8633 if (val !== this[storeProp]) { 8634 this[storeProp] = val; 8635 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 8636 ObserveV2.getObserve().fireChange(this, consumeVarName); 8637 } 8638 } 8639 }, 8640 enumerable: true 8641 }); 8642 return consumeView[storeProp]; 8643 } 8644} 8645ProviderConsumerUtilV2.ALIAS_PREFIX = '___pc_alias_'; 8646/* 8647 Internal decorator for @Trace without usingV2ObservedTrack call. 8648 Real @Trace decorator function is in v2_decorators.ts 8649*/ 8650const Trace_Internal = (target, propertyKey) => { 8651 return trackInternal(target, propertyKey); 8652}; 8653/* 8654 Internal decorator for @ObservedV2 without usingV2ObservedTrack call. 8655 Real @ObservedV2 decorator function is in v2_decorators.ts 8656*/ 8657function ObservedV2_Internal(BaseClass) { 8658 return observedV2Internal(BaseClass); 8659} 8660/* 8661 @ObservedV2 decorator function uses this in v2_decorators.ts 8662*/ 8663function observedV2Internal(BaseClass) { 8664 // prevent @Track inside @observed class 8665 if (BaseClass.prototype && Reflect.has(BaseClass.prototype, TrackedObject.___IS_TRACKED_OPTIMISED)) { 8666 const error = `'@observed class ${BaseClass === null || BaseClass === void 0 ? void 0 : BaseClass.name}': invalid use of V2 @Track decorator inside V3 @observed class. Need to fix class definition to use @track.`; 8667 stateMgmtConsole.applicationError(error); 8668 throw new Error(error); 8669 } 8670 if (BaseClass.prototype && !Reflect.has(BaseClass.prototype, ObserveV2.V2_DECO_META)) { 8671 // not an error, suspicious of developer oversight 8672 stateMgmtConsole.warn(`'@observed class ${BaseClass === null || BaseClass === void 0 ? void 0 : BaseClass.name}': no @track property inside. Is this intended? Check our application.`); 8673 } 8674 // Use ID_REFS only if number of observed attrs is significant 8675 const attrList = Object.getOwnPropertyNames(BaseClass.prototype); 8676 const count = attrList.filter(attr => attr.startsWith(ObserveV2.OB_PREFIX)).length; 8677 if (count > 5) { 8678 8679 BaseClass.prototype[ObserveV2.ID_REFS] = {}; 8680 } 8681 const observedClass = class extends BaseClass { 8682 constructor(...args) { 8683 super(...args); 8684 AsyncAddComputedV2.addComputed(this, BaseClass.name); 8685 AsyncAddMonitorV2.addMonitor(this, BaseClass.name); 8686 } 8687 }; 8688 Object.defineProperty(observedClass, 'name', { value: BaseClass.name }); 8689 return observedClass; 8690} 8691/* 8692 * Copyright (c) 2024 Huawei Device Co., Ltd. 8693 * Licensed under the Apache License, Version 2.0 (the "License"); 8694 * you may not use this file except in compliance with the License. 8695 * You may obtain a copy of the License at 8696 * 8697 * http://www.apache.org/licenses/LICENSE-2.0 8698 * 8699 * Unless required by applicable law or agreed to in writing, software 8700 * distributed under the License is distributed on an "AS IS" BASIS, 8701 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8702 * See the License for the specific language governing permissions and 8703 * limitations under the License. 8704 */ 8705/** 8706 * 8707 * This file includes only framework internal classes and functions 8708 * non are part of SDK. Do not access from app. 8709 * 8710 * It includes @Monitor function decorator supporting classes MonitorV2 and AsyncMonitorV2 8711 * 8712 */ 8713class MonitorValueV2 { 8714 constructor(path) { 8715 this.path = path; 8716 this.dirty = false; 8717 this.props = path.split('.'); 8718 } 8719 setValue(isInit, newValue) { 8720 this.now = newValue; 8721 if (isInit) { 8722 this.before = this.now; 8723 } 8724 this.dirty = this.before !== this.now; 8725 return this.dirty; 8726 } 8727 // mv newValue to oldValue, set dirty to false 8728 reset() { 8729 this.before = this.now; 8730 this.dirty = false; 8731 } 8732 isDirty() { 8733 return this.dirty; 8734 } 8735} 8736/** 8737 * MonitorV2 8738 * one MonitorV2 object per @monitor function 8739 * watchId - similar to elmtId, identify one MonitorV2 in Observe.idToCmp Map 8740 * observeObjectAccess = get each object on the 'path' to create dependency and add them with Observe.addRef 8741 * fireChange - exec @Monitor function and re-new dependencies with observeObjectAccess 8742 */ 8743class MonitorV2 { 8744 constructor(target, pathsString, func) { 8745 var _a; 8746 var _b; 8747 this.values_ = new Array(); 8748 this.target_ = target; 8749 this.monitorFunction = func; 8750 this.watchId_ = ++MonitorV2.nextWatchId_; 8751 // split space separated array of paths 8752 let paths = pathsString.split(/\s+/g); 8753 paths.forEach(path => this.values_.push(new MonitorValueV2(path))); 8754 // add watchId to owning ViewV2 or view model data object 8755 // ViewV2 uses to call clearBinding(id) 8756 // FIXME data object leave data inside ObservedV3, because they can not 8757 // call clearBinding(id) before they get deleted. 8758 const meta = (_a = target[_b = MonitorV2.WATCH_INSTANCE_PREFIX]) !== null && _a !== void 0 ? _a : (target[_b] = {}); 8759 meta[pathsString] = this.watchId_; 8760 } 8761 getTarget() { 8762 return this.target_; 8763 } 8764 /** 8765 Return array of those monitored paths 8766 that changed since previous invocation 8767 */ 8768 get dirty() { 8769 let ret = new Array(); 8770 this.values_.forEach(monitorValue => { 8771 if (monitorValue.isDirty()) { 8772 ret.push(monitorValue.path); 8773 } 8774 }); 8775 return ret; 8776 } 8777 /** 8778 * return IMonitorValue for given path 8779 * or if no path is specified any dirty (changed) monitor value 8780 */ 8781 value(path) { 8782 for (let monitorValue of this.values_) { 8783 if ((path === undefined && monitorValue.isDirty()) || monitorValue.path === path) { 8784 return monitorValue; 8785 } 8786 } 8787 return undefined; 8788 } 8789 InitRun() { 8790 this.bindRun(true); 8791 return this; 8792 } 8793 notifyChange() { 8794 if (this.bindRun(/* is init / first run */ false)) { 8795 8796 try { 8797 // exec @Monitor function 8798 this.monitorFunction.call(this.target_, this); 8799 } 8800 catch (e) { 8801 stateMgmtConsole.applicationError(`@Monitor exception caught for ${this.monitorFunction.name}`, e.toString()); 8802 throw e; 8803 } 8804 finally { 8805 this.reset(); 8806 } 8807 } 8808 } 8809 // called after @Monitor function call 8810 reset() { 8811 this.values_.forEach(item => item.reset()); 8812 } 8813 // analysisProp for each monitored path 8814 bindRun(isInit = false) { 8815 ObserveV2.getObserve().startRecordDependencies(this, this.watchId_); 8816 let ret = false; 8817 this.values_.forEach((item) => { 8818 const [success, value] = this.analysisProp(isInit, item); 8819 if (!success) { 8820 8821 return; 8822 } 8823 let dirty = item.setValue(isInit, value); 8824 ret = ret || dirty; 8825 }); 8826 ObserveV2.getObserve().stopRecordDependencies(); 8827 return ret; 8828 } 8829 // record / update object dependencies by reading each object along the path 8830 // return the value, i.e. the value of the last path item 8831 analysisProp(isInit, monitoredValue) { 8832 let obj = this.target_; 8833 for (let prop of monitoredValue.props) { 8834 if (typeof obj === 'object' && Reflect.has(obj, prop)) { 8835 obj = obj[prop]; 8836 } 8837 else { 8838 isInit && stateMgmtConsole.warn(`watch prop ${monitoredValue.path} initialize not found, make sure it exists!`); 8839 return [false, undefined]; 8840 } 8841 } 8842 return [true, obj]; 8843 } 8844 static clearWatchesFromTarget(target) { 8845 var _a; 8846 let meta; 8847 if (!target || typeof target !== 'object' || 8848 !(meta = target[MonitorV2.WATCH_INSTANCE_PREFIX]) || typeof meta !== 'object') { 8849 return; 8850 } 8851 8852 Array.from(Object.values(meta)).forEach((watchId) => ObserveV2.getObserve().clearWatch(watchId)); 8853 } 8854} 8855MonitorV2.WATCH_PREFIX = '___watch_'; 8856MonitorV2.WATCH_INSTANCE_PREFIX = '___watch__obj_'; 8857// start with high number to avoid same id as elmtId for components. 8858MonitorV2.MIN_WATCH_ID = 0x1000000000000; 8859MonitorV2.nextWatchId_ = MonitorV2.MIN_WATCH_ID; 8860// Performance Improvement 8861class AsyncAddMonitorV2 { 8862 static addMonitor(target, name) { 8863 if (AsyncAddMonitorV2.watches.length === 0) { 8864 Promise.resolve(true) 8865 .then(AsyncAddMonitorV2.run) 8866 .catch(error => { 8867 stateMgmtConsole.applicationError(`Exception caught in @Monitor function ${name}`, error); 8868 _arkUIUncaughtPromiseError(error); 8869 }); 8870 } 8871 AsyncAddMonitorV2.watches.push([target, name]); 8872 } 8873 static run() { 8874 for (let item of AsyncAddMonitorV2.watches) { 8875 ObserveV2.getObserve().constructMonitor(item[0], item[1]); 8876 } 8877 AsyncAddMonitorV2.watches = []; 8878 } 8879} 8880AsyncAddMonitorV2.watches = []; 8881/* 8882 * Copyright (c) 2024 Huawei Device Co., Ltd. 8883 * Licensed under the Apache License, Version 2.0 (the "License"); 8884 * you may not use this file except in compliance with the License. 8885 * You may obtain a copy of the License at 8886 * 8887 * http://www.apache.org/licenses/LICENSE-2.0 8888 * 8889 * Unless required by applicable law or agreed to in writing, software 8890 * distributed under the License is distributed on an "AS IS" BASIS, 8891 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8892 * See the License for the specific language governing permissions and 8893 * limitations under the License. 8894 */ 8895/** 8896 * 8897 * This file includes only framework internal classes and functions 8898 * non are part of SDK. Do not access from app. 8899 * 8900 */ 8901/** 8902 * ComputedV2 8903 * one ComputedV2 object per @Computed variable 8904 * computedId_ - similar to elmtId, identify one ComputedV2 in Observe.idToCmp Map 8905 * observeObjectAccess = calculate the compute function and create dependencies to 8906 * source variables 8907 * fireChange - execute compute function and re-new dependencies with observeObjectAccess 8908 */ 8909class ComputedV2 { 8910 constructor(target, prop, func) { 8911 this.target_ = target; 8912 this.propertyComputeFunc_ = func; 8913 this.computedId_ = ++ComputedV2.nextCompId_; 8914 this.prop_ = prop; 8915 } 8916 InitRun() { 8917 let cachedProp = ComputedV2.COMPUTED_CACHED_PREFIX + this.prop_; 8918 let propertyKey = this.prop_; 8919 Reflect.defineProperty(this.target_, propertyKey, { 8920 get() { 8921 ObserveV2.getObserve().addRef(this, propertyKey); 8922 return ObserveV2.autoProxyObject(this, cachedProp); 8923 }, 8924 enumerable: true 8925 }); 8926 this.target_[cachedProp] = this.observeObjectAccess(); 8927 return this.computedId_; 8928 } 8929 fireChange() { 8930 let newVal = this.observeObjectAccess(); 8931 let cachedProp = ComputedV2.COMPUTED_CACHED_PREFIX + this.prop_; 8932 if (this.target_[cachedProp] !== newVal) { 8933 this.target_[cachedProp] = newVal; 8934 ObserveV2.getObserve().fireChange(this.target_, this.prop_); 8935 } 8936 } 8937 getTarget() { 8938 return this.target_; 8939 } 8940 getProp() { 8941 return this.prop_; 8942 } 8943 // register current watchId while executing compute function 8944 observeObjectAccess() { 8945 ObserveV2.getObserve().startRecordDependencies(this, this.computedId_); 8946 let ret; 8947 try { 8948 ret = this.propertyComputeFunc_.call(this.target_); 8949 } 8950 catch (e) { 8951 stateMgmtConsole.applicationError(`@Computed Exception caught for ${this.propertyComputeFunc_.name}`, e.toString()); 8952 ret = undefined; 8953 throw e; 8954 } 8955 finally { 8956 ObserveV2.getObserve().stopRecordDependencies(); 8957 } 8958 return ret; 8959 } 8960} 8961// start with high number to avoid same id as elmtId for components. 8962ComputedV2.MIN_COMPUTED_ID = 0x1000000000; 8963ComputedV2.nextCompId_ = ComputedV2.MIN_COMPUTED_ID; 8964ComputedV2.COMPUTED_PREFIX = '___comp_'; 8965ComputedV2.COMPUTED_CACHED_PREFIX = '___comp_cached_'; 8966class AsyncAddComputedV2 { 8967 static addComputed(target, name) { 8968 if (AsyncAddComputedV2.computedVars.length === 0) { 8969 Promise.resolve(true) 8970 .then(AsyncAddComputedV2.run) 8971 .catch(error => { 8972 stateMgmtConsole.applicationError(`Exception caught in @Computed ${name}`, error); 8973 _arkUIUncaughtPromiseError(error); 8974 }); 8975 } 8976 AsyncAddComputedV2.computedVars.push({ target: target, name: name }); 8977 } 8978 static run() { 8979 AsyncAddComputedV2.computedVars.forEach((computedVar) => ObserveV2.getObserve().constructComputed(computedVar.target, computedVar.name)); 8980 // according to stackoverflow this is the fastest way to clear an Array 8981 // ref https://stackoverflow.com/questions/1232040/how-do-i-empty-an-array-in-javascript 8982 AsyncAddComputedV2.computedVars.length = 0; 8983 } 8984} 8985AsyncAddComputedV2.computedVars = new Array(); 8986/* 8987 * Copyright (c) 2024 Huawei Device Co., Ltd. 8988 * Licensed under the Apache License, Version 2.0 (the "License"); 8989 * you may not use this file except in compliance with the License. 8990 * You may obtain a copy of the License at 8991 * 8992 * http://www.apache.org/licenses/LICENSE-2.0 8993 * 8994 * Unless required by applicable law or agreed to in writing, software 8995 * distributed under the License is distributed on an "AS IS" BASIS, 8996 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8997 * See the License for the specific language governing permissions and 8998 * limitations under the License. 8999 */ 9000/** 9001 * 9002 * This file includes only framework internal classes and functions 9003 * non are part of SDK. Do not access from app. 9004 * 9005 * Implementation of @ComponentV2 is ViewV2 9006 * When transpiling @ComponentV2, the transpiler generates a class that extends from ViewV2. 9007 * 9008 */ 9009class ViewV2 extends PUV2ViewBase { 9010 constructor(parent, elmtId = UINodeRegisterProxy.notRecordingDependencies, extraInfo = undefined) { 9011 super(parent, elmtId, extraInfo); 9012 // Set of elmtIds that need re-render 9013 this.dirtDescendantElementIds_ = new Set(); 9014 this.monitorIdsDelayedUpdate = new Set(); 9015 this.computedIdsDelayedUpdate = new Set(); 9016 /** 9017 * on first render create a new Instance of Repeat 9018 * on re-render connect to existing instance 9019 * @param arr 9020 * @returns 9021 */ 9022 this.__mkRepeatAPI = (arr) => { 9023 // factory is for future extensions, currently always return the same 9024 const elmtId = ObserveV2.getCurrentRecordedId(); 9025 let repeat = this.elmtId2Repeat_.get(elmtId); 9026 if (!repeat) { 9027 repeat = new __Repeat(this, arr); 9028 this.elmtId2Repeat_.set(elmtId, repeat); 9029 } 9030 else { 9031 repeat.updateArr(arr); 9032 } 9033 return repeat; 9034 }; 9035 this.setIsV2(true); 9036 9037 } 9038 /** 9039 * The `freezeState` parameter determines whether this @ComponentV2 is allowed to freeze, when inactive 9040 * Its called with value of the `freezeWhenInactive` parameter from the @ComponentV2 decorator, 9041 * or it may be called with `undefined` depending on how the UI compiler works. 9042 * 9043 * @param freezeState Only the value `true` will be used to set the freeze state, 9044 * otherwise it inherits from its parent instance if its freezeState is true 9045 */ 9046 finalizeConstruction(freezeState) { 9047 ObserveV2.getObserve().constructComputed(this, this.constructor.name); 9048 ObserveV2.getObserve().constructMonitor(this, this.constructor.name); 9049 // Always use ID_REFS in ViewV2 9050 this[ObserveV2.ID_REFS] = {}; 9051 // set to true if freeze parameter set for this @ComponentV2 to true 9052 // otherwise inherit from its parentComponent (if it exists). 9053 this.isCompFreezeAllowed_ = freezeState || this.isCompFreezeAllowed_; 9054 9055 } 9056 debugInfo__() { 9057 return `@ComponentV2 '${this.constructor.name}'[${this.id__()}]`; 9058 } 9059 get isViewV3() { 9060 return true; 9061 } 9062 /** 9063 * Virtual function implemented in ViewPU and ViewV2 9064 * Unregisters and purges all child elements associated with the specified Element ID in ViewV2. 9065 * 9066 * @param rmElmtId - The Element ID to be purged and deleted 9067 * @returns {boolean} - Returns `true` if the Element ID was successfully deleted, `false` otherwise. 9068 */ 9069 purgeDeleteElmtId(rmElmtId) { 9070 9071 const result = this.updateFuncByElmtId.delete(rmElmtId); 9072 if (result) { 9073 const childOpt = this.getChildViewV2ForElmtId(rmElmtId); 9074 if (childOpt) { 9075 childOpt.setDeleting(); 9076 childOpt.setDeleteStatusRecursively(); 9077 } 9078 // it means rmElmtId has finished all the unregistration from the js side, ElementIdToOwningViewPU_ does not need to keep it 9079 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(rmElmtId); 9080 } 9081 // Needed only for V2 9082 ObserveV2.getObserve().clearBinding(rmElmtId); 9083 return result; 9084 } 9085 // super class will call this function from 9086 // its aboutToBeDeleted implementation 9087 aboutToBeDeletedInternal() { 9088 9089 // if this isDeleting_ is true already, it may be set delete status recursively by its parent, so it is not necessary 9090 // to set and resursively set its children any more 9091 if (!this.isDeleting_) { 9092 this.isDeleting_ = true; 9093 this.setDeleteStatusRecursively(); 9094 } 9095 // tell UINodeRegisterProxy that all elmtIds under 9096 // this ViewV2 should be treated as already unregistered 9097 9098 // purge the elmtIds owned by this ViewV2 from the updateFuncByElmtId and also the state variable dependent elmtIds 9099 Array.from(this.updateFuncByElmtId.keys()).forEach((elmtId) => { 9100 // FIXME split View: enable delete this purgeDeleteElmtId(elmtId); 9101 }); 9102 // unregistration of ElementIDs 9103 9104 // it will unregister removed elementids from all the ViewV2, equals purgeDeletedElmtIdsRecursively 9105 this.purgeDeletedElmtIds(); 9106 // unregisters its own id once its children are unregistered above 9107 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs([this.id__()]); 9108 9109 /* in case ViewPU is currently frozen 9110 ViewPU inactiveComponents_ delete(`${this.constructor.name}[${this.id__()}]`); 9111 */ 9112 MonitorV2.clearWatchesFromTarget(this); 9113 this.updateFuncByElmtId.clear(); 9114 if (this.parent_) { 9115 this.parent_.removeChild(this); 9116 } 9117 } 9118 initialRenderView() { 9119 9120 this.initialRender(); 9121 9122 } 9123 observeComponentCreation2(compilerAssignedUpdateFunc, classObject) { 9124 if (this.isDeleting_) { 9125 stateMgmtConsole.error(`@ComponentV2 ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation2 `); 9126 return; 9127 } 9128 const _componentName = (classObject && ('name' in classObject)) ? Reflect.get(classObject, 'name') : 'unspecified UINode'; 9129 const _popFunc = (classObject && 'pop' in classObject) ? classObject.pop : () => { }; 9130 const updateFunc = (elmtId, isFirstRender) => { 9131 this.syncInstanceId(); 9132 9133 ViewStackProcessor.StartGetAccessRecordingFor(elmtId); 9134 ObserveV2.getObserve().startRecordDependencies(this, elmtId); 9135 compilerAssignedUpdateFunc(elmtId, isFirstRender); 9136 if (!isFirstRender) { 9137 _popFunc(); 9138 } 9139 let node = this.getNodeById(elmtId); 9140 if (node !== undefined) { 9141 node.cleanStageValue(); 9142 } 9143 ObserveV2.getObserve().stopRecordDependencies(); 9144 ViewStackProcessor.StopGetAccessRecording(); 9145 9146 this.restoreInstanceId(); 9147 }; 9148 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 9149 // needs to move set before updateFunc. 9150 // make sure the key and object value exist since it will add node in attributeModifier during updateFunc. 9151 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc, classObject: classObject }); 9152 // add element id -> owning ViewV2 9153 UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this)); 9154 try { 9155 updateFunc(elmtId, /* is first render */ true); 9156 } 9157 catch (error) { 9158 // avoid the incompatible change that move set function before updateFunc. 9159 this.updateFuncByElmtId.delete(elmtId); 9160 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId); 9161 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${error.message}`); 9162 throw error; 9163 } 9164 9165 } 9166 /** 9167 * 9168 * @param paramVariableName 9169 * @param @once paramVariableName 9170 * @param is read only, therefore, init from parent needs to be done without 9171 * causing property setter() to be called 9172 * @param newValue 9173 */ 9174 initParam(paramVariableName, newValue) { 9175 this.checkIsV1Proxy(paramVariableName, newValue); 9176 VariableUtilV3.initParam(this, paramVariableName, newValue); 9177 } 9178 /** 9179 * 9180 * @param paramVariableName 9181 * @param @once paramVariableName 9182 * @param is read only, therefore, update from parent needs to be done without 9183 * causing property setter() to be called 9184 * @param @once reject any update 9185 * @param newValue 9186 */ 9187 updateParam(paramVariableName, newValue) { 9188 this.checkIsV1Proxy(paramVariableName, newValue); 9189 VariableUtilV3.updateParam(this, paramVariableName, newValue); 9190 } 9191 checkIsV1Proxy(paramVariableName, value) { 9192 if (ObservedObject.IsObservedObject(value)) { 9193 throw new Error(`Cannot assign the ComponentV1 value to the ComponentV2 for the property '${paramVariableName}'`); 9194 } 9195 } 9196 /** 9197 * inform that UINode with given elmtId needs rerender 9198 * does NOT exec @Watch function. 9199 * only used on V3 code path from ObserveV2.fireChange. 9200 * 9201 * FIXME will still use in the future? 9202 */ 9203 uiNodeNeedUpdateV3(elmtId) { 9204 if (this.isFirstRender()) { 9205 return; 9206 } 9207 9208 if (!this.isActive_) { 9209 this.scheduleDelayedUpdate(elmtId); 9210 return; 9211 } 9212 if (!this.dirtDescendantElementIds_.size) { // && !this runReuse_) { 9213 // mark ComposedElement dirty when first elmtIds are added 9214 // do not need to do this every time 9215 this.syncInstanceId(); 9216 this.markNeedUpdate(); 9217 this.restoreInstanceId(); 9218 } 9219 this.dirtDescendantElementIds_.add(elmtId); 9220 9221 9222 } 9223 /** 9224 * For each recorded dirty Element in this custom component 9225 * run its update function 9226 * 9227 */ 9228 updateDirtyElements() { 9229 9230 do { 9231 9232 // see which elmtIds are managed by this View 9233 // and clean up all book keeping for them 9234 this.purgeDeletedElmtIds(); 9235 // process all elmtIds marked as needing update in ascending order. 9236 // ascending order ensures parent nodes will be updated before their children 9237 // prior cleanup ensure no already deleted Elements have their update func executed 9238 const dirtElmtIdsFromRootNode = Array.from(this.dirtDescendantElementIds_).sort(ViewV2.compareNumber); 9239 // if state changed during exec update lambda inside UpdateElement, then the dirty elmtIds will be added 9240 // to newly created this.dirtDescendantElementIds_ Set 9241 dirtElmtIdsFromRootNode.forEach(elmtId => { 9242 this.UpdateElement(elmtId); 9243 this.dirtDescendantElementIds_.delete(elmtId); 9244 }); 9245 if (this.dirtDescendantElementIds_.size) { 9246 stateMgmtConsole.applicationError(`${this.debugInfo__()}: New UINode objects added to update queue while re-render! - Likely caused by @Component state change during build phase, not allowed. Application error!`); 9247 } 9248 } while (this.dirtDescendantElementIds_.size); 9249 9250 9251 } 9252 UpdateElement(elmtId) { 9253 if (this.isDeleting_) { 9254 9255 return; 9256 } 9257 9258 if (elmtId === this.id__()) { 9259 // do not attempt to update itself 9260 9261 return; 9262 } 9263 // do not process an Element that has been marked to be deleted 9264 const entry = this.updateFuncByElmtId.get(elmtId); 9265 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 9266 if (typeof updateFunc !== 'function') { 9267 9268 } 9269 else { 9270 const componentName = entry.getComponentName(); 9271 9272 9273 try { 9274 updateFunc(elmtId, /* isFirstRender */ false); 9275 } 9276 catch (e) { 9277 stateMgmtConsole.applicationError(`Exception caught in update function of ${componentName} for elmtId ${elmtId}`, e.toString()); 9278 throw e; 9279 } 9280 finally { 9281 9282 } 9283 9284 this.finishUpdateFunc(elmtId); 9285 9286 9287 } 9288 9289 } 9290 /** 9291 * Retrieve child by given id 9292 * @param id 9293 * @returns child if child with this id exists and it is instance of ViewV2 9294 */ 9295 getViewV2ChildById(id) { 9296 const childWeakRef = this.childrenWeakrefMap_.get(id); 9297 const child = childWeakRef ? childWeakRef.deref() : undefined; 9298 return (child && child instanceof ViewV2) ? child : undefined; 9299 } 9300 /** 9301 * findViewPUInHierarchy function needed for @Component and @ComponentV2 mixed 9302 * parent - child hierarchies. Not used by ViewV2 9303 */ 9304 findViewPUInHierarchy(id) { 9305 // this ViewV2 is not a ViewPU, continue searching amongst children 9306 let retVal = undefined; 9307 for (const [key, value] of this.childrenWeakrefMap_.entries()) { 9308 retVal = value.deref().findViewPUInHierarchy(id); 9309 if (retVal) { 9310 break; 9311 } 9312 } 9313 return retVal; 9314 } 9315 // WatchIds that needs to be fired later gets added to monitorIdsDelayedUpdate 9316 // monitor fireChange will be triggered for all these watchIds once this view gets active 9317 addDelayedMonitorIds(watchId) { 9318 9319 this.monitorIdsDelayedUpdate.add(watchId); 9320 } 9321 addDelayedComputedIds(watchId) { 9322 9323 this.computedIdsDelayedUpdate.add(watchId); 9324 } 9325 setActiveInternal(newState) { 9326 9327 if (!this.isCompFreezeAllowed()) { 9328 9329 9330 return; 9331 } 9332 9333 this.isActive_ = newState; 9334 if (this.isActive_) { 9335 this.onActiveInternal(); 9336 } 9337 else { 9338 this.onInactiveInternal(); 9339 } 9340 9341 } 9342 onActiveInternal() { 9343 if (!this.isActive_) { 9344 return; 9345 } 9346 9347 this.performDelayedUpdate(); 9348 // Set 'isActive_' state for all descendant child Views 9349 for (const child of this.childrenWeakrefMap_.values()) { 9350 const childView = child.deref(); 9351 if (childView) { 9352 childView.setActiveInternal(this.isActive_); 9353 } 9354 } 9355 } 9356 onInactiveInternal() { 9357 if (this.isActive_) { 9358 return; 9359 } 9360 9361 // Set 'isActive_' state for all descendant child Views 9362 for (const child of this.childrenWeakrefMap_.values()) { 9363 const childView = child.deref(); 9364 if (childView) { 9365 childView.setActiveInternal(this.isActive_); 9366 } 9367 } 9368 } 9369 performDelayedUpdate() { 9370 9371 if (this.computedIdsDelayedUpdate.size) { 9372 // exec computed functions 9373 ObserveV2.getObserve().updateDirtyComputedProps([...this.computedIdsDelayedUpdate]); 9374 } 9375 if (this.monitorIdsDelayedUpdate.size) { 9376 // exec monitor functions 9377 ObserveV2.getObserve().updateDirtyMonitors(this.monitorIdsDelayedUpdate); 9378 } 9379 if (this.elmtIdsDelayedUpdate.size) { 9380 // update re-render of updated element ids once the view gets active 9381 if (this.dirtDescendantElementIds_.size === 0) { 9382 this.dirtDescendantElementIds_ = new Set(this.elmtIdsDelayedUpdate); 9383 } 9384 else { 9385 this.elmtIdsDelayedUpdate.forEach((element) => { 9386 this.dirtDescendantElementIds_.add(element); 9387 }); 9388 } 9389 } 9390 this.markNeedUpdate(); 9391 this.elmtIdsDelayedUpdate.clear(); 9392 this.monitorIdsDelayedUpdate.clear(); 9393 this.computedIdsDelayedUpdate.clear(); 9394 9395 } 9396 /* 9397 findProvidePU finds @Provided property recursively by traversing ViewPU's towards that of the UI tree root @Component: 9398 if 'this' ViewPU has a @Provide('providedPropName') return it, otherwise ask from its parent ViewPU. 9399 function needed for mixed @Component and @ComponentV2 parent child hierarchies. 9400 */ 9401 findProvidePU(providedPropName) { 9402 var _a; 9403 return (_a = this.getParent()) === null || _a === void 0 ? void 0 : _a.findProvidePU(providedPropName); 9404 } 9405 get localStorage_() { 9406 // FIXME check this also works for root @ComponentV2 9407 return (this.getParent()) ? this.getParent().localStorage_ : new LocalStorage({ /* empty */}); 9408 } 9409 /** 9410 * @function observeRecycleComponentCreation 9411 * @description custom node recycle creation not supported for V2. So a dummy function is implemented to report 9412 * an error message 9413 * @param name custom node name 9414 * @param recycleUpdateFunc custom node recycle update which can be converted to a normal update function 9415 * @return void 9416 */ 9417 observeRecycleComponentCreation(name, recycleUpdateFunc) { 9418 stateMgmtConsole.error(`${this.debugInfo__()}: Recycle not supported for ComponentV2 instances`); 9419 } 9420 debugInfoDirtDescendantElementIdsInternal(depth = 0, recursive = false, counter) { 9421 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`; 9422 retVaL += `ViewV2 keeps no info about dirty elmtIds`; 9423 if (recursive) { 9424 this.childrenWeakrefMap_.forEach((value, key, map) => { 9425 var _a; 9426 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoDirtDescendantElementIdsInternal(depth + 1, recursive, counter); 9427 }); 9428 } 9429 if (recursive && depth === 0) { 9430 retVaL += `\nTotal: ${counter.total}`; 9431 } 9432 return retVaL; 9433 } 9434 debugInfoStateVars() { 9435 return ''; // TODO DFX, read out META 9436 } 9437} 9438/* 9439 * Copyright (c) 2024 Huawei Device Co., Ltd. 9440 * Licensed under the Apache License, Version 2.0 (the "License"); 9441 * you may not use this file except in compliance with the License. 9442 * You may obtain a copy of the License at 9443 * 9444 * http://www.apache.org/licenses/LICENSE-2.0 9445 * 9446 * Unless required by applicable law or agreed to in writing, software 9447 * distributed under the License is distributed on an "AS IS" BASIS, 9448 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9449 * See the License for the specific language governing permissions and 9450 * limitations under the License. 9451 */ 9452function ObservedV2(BaseClass) { 9453 ConfigureStateMgmt.instance.usingV2ObservedTrack(`@observed`, BaseClass === null || BaseClass === void 0 ? void 0 : BaseClass.name); 9454 return observedV2Internal(BaseClass); 9455} 9456/** 9457 * @Trace class property decorator, property inside @ObservedV2 class 9458 * 9459 * turns given property into getter and setter functions 9460 * adds property target[storeProp] as the backing store 9461 * 9462 * part of SDK 9463 * @from 12 9464 */ 9465const Trace = (target, propertyKey) => { 9466 ConfigureStateMgmt.instance.usingV2ObservedTrack(`@track`, propertyKey); 9467 return trackInternal(target, propertyKey); 9468}; 9469/** 9470 * @Local @ComponentV2/ViewV2 variable decorator 9471 * 9472 * allowed value: simple or object type value allowed. Objects must be instances of 9473 * ObservedV2, Array, Map, Set, or Date for changes to be observed. No functions allowed 9474 * local init required 9475 * no init or update from parent @ComponentV2 9476 * new value assignment allowed = has setter 9477 * 9478 * part of SDK 9479 * @from 12 9480 * 9481 */ 9482const Local = (target, propertyKey) => { 9483 ObserveV2.addVariableDecoMeta(target, propertyKey, '@state'); 9484 return trackInternal(target, propertyKey); 9485}; 9486/** 9487 * @Param class property decorator 9488 * 9489 * allowed value: simple or object type value allowed. Objects must be instances of 9490 * ObservedV2, Array, Map, Set, or Date for changes to be observed. No functions allowed 9491 * local init optional 9492 * init from parent @ComponentV2 is mandatory when no local init, otherwise optional 9493 * updates from parent @ComponentV2 if initialized from parent @ComponentV2, 9494 * no update when @Once is used. 9495 * new value assignment not allowed = has no setter. 9496 * 9497 * part of SDK 9498 * @from 12 9499 * 9500 */ 9501const Param = (proto, propertyKey) => { 9502 9503 ObserveV2.addParamVariableDecoMeta(proto, propertyKey, '@param', undefined); 9504 let storeProp = ObserveV2.OB_PREFIX + propertyKey; 9505 proto[storeProp] = proto[propertyKey]; 9506 Reflect.defineProperty(proto, propertyKey, { 9507 get() { 9508 ObserveV2.getObserve().addRef(this, propertyKey); 9509 return ObserveV2.autoProxyObject(this, ObserveV2.OB_PREFIX + propertyKey); 9510 }, 9511 set(val) { 9512 var _a; 9513 const meta = (_a = proto[ObserveV2.V2_DECO_META]) === null || _a === void 0 ? void 0 : _a[propertyKey]; 9514 if (meta && meta.deco2 !== '@once') { 9515 stateMgmtConsole.applicationError(`@param ${propertyKey.toString()}: can not assign a new value, application error.`); 9516 return; 9517 } 9518 if (val !== this[storeProp]) { 9519 this[storeProp] = val; 9520 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 9521 ObserveV2.getObserve().fireChange(this, propertyKey); 9522 } 9523 } 9524 }, 9525 // @param can not be assigned, no setter 9526 enumerable: true 9527 }); 9528}; // Param 9529/** 9530 * @Once supplementary @ComponentV2 variable decorator to @Param decorator 9531 * must use like this @Param @Once varName. Can not be used without @param. 9532 * prevents @Param variable updates from parent component 9533 * 9534 * @param proto 9535 * @param propertyKey 9536 * 9537 * part of SDK 9538 * @from 12 9539 * 9540 */ 9541const Once = (proto, propertyKey) => { 9542 9543 ObserveV2.addParamVariableDecoMeta(proto, propertyKey, undefined, '@once'); 9544}; 9545/** 9546 * @Event class variable decorator, class must be @ComponentV2 9547 * 9548 * Allowed value: Function, can have parameters and return a value. 9549 * local init: optional for functions without return value, default is () => void 9550 * Local init is mandatory for functions with return value. 9551 * init from parent @Component: optional. 9552 * update from parent @Component: never 9553 * new value assignment not allowed 9554 * 9555 * part of SDK 9556 * @from 12 9557 * 9558 */ 9559const Event = (target, propertyKey) => { 9560 var _a; 9561 ObserveV2.addVariableDecoMeta(target, propertyKey, '@event'); 9562 (_a = target[propertyKey]) !== null && _a !== void 0 ? _a : (target[propertyKey] = () => { }); 9563}; 9564/** 9565 * @Provider variable decorator of @ComponentV2 variable 9566 * 9567 * @Provider(alias? : string) varName : typeName = initValue 9568 * 9569 * @param alias defaults to varName 9570 * 9571 * allowed value: simple or object type value allowed. Objects must be instances of 9572 * ObservedV2, Array, Map, Set, or Date for changes to be observed. No functions allowed 9573 * local init required 9574 * no init or update from parent @ComponentV2 9575 * provides its value to any @Consumer counter part 9576 * new value assignment allowed = has setter 9577 * 9578 * part of SDK 9579 * @since 12 9580 */ 9581const Provider = (aliasName) => { 9582 return (proto, varName) => { 9583 const providedUnderName = aliasName || varName; 9584 ProviderConsumerUtilV2.addProvideConsumeVariableDecoMeta(proto, varName, providedUnderName, '@Provider'); 9585 trackInternal(proto, varName); 9586 }; 9587}; // @Provider 9588/** 9589 * @Consumer variable decorator of @ComponentV2 variable 9590 * 9591 * @Consumer(alias? : string) varName : typeName = defaultValue 9592* 9593 * @param alias defaults to varName 9594 * 9595 * allowed value: simple or object type value allowed. Objects must be instances of 9596 * ObservedV2, Array, Map, Set, or Date for changes to be observed. No functions allowed 9597 * syncs two-way with the @Provider variable with same `alias` name in nearest ancestor @ComponentV2 9598 * local init required, used only if no @Provider counter part is found. 9599 * no init or update from parent @ComponentV2 via constructor allowed 9600 * new value assignment allowed, changes sys back to @Provider of one exists, otherwise update local value. 9601 * 9602 * part of SDK 9603 * @since 12 9604 */ 9605const Consumer = (aliasName) => { 9606 return (proto, varName) => { 9607 const searchForProvideWithName = aliasName || varName; 9608 // redefining the property happens when owning ViewV2 gets constructed 9609 // and @Consumer gets connected to @provide counterpart 9610 ProviderConsumerUtilV2.addProvideConsumeVariableDecoMeta(proto, varName, searchForProvideWithName, '@Consumer'); 9611 const providerName = (aliasName === undefined || aliasName === null || 9612 (typeof aliasName === 'string' && aliasName.trim() === '')) ? varName : aliasName; 9613 Reflect.defineProperty(proto, varName, { 9614 get() { 9615 // this get function should never be called, 9616 // because transpiler will always assign it a value first. 9617 stateMgmtConsole.warn('@Consumer outer "get" should never be called, internal error!'); 9618 return undefined; 9619 }, 9620 set(val) { 9621 let providerInfo = ProviderConsumerUtilV2.findProvider(this, providerName); 9622 if (providerInfo && providerInfo[0] && providerInfo[1]) { 9623 ProviderConsumerUtilV2.connectConsumer2Provider(this, varName, providerInfo[0], providerInfo[1]); 9624 } 9625 else { 9626 ProviderConsumerUtilV2.defineConsumerWithoutProvider(this, varName, val); 9627 } 9628 }, 9629 enumerable: true 9630 }); 9631 }; 9632}; // @Consumer 9633/** 9634 * @Monitor class function decorator, inside either @ComponentV2 or @ObservedV2 class 9635 * 9636 * @Monitor(path: string, paths: string[]) functionName (m : IMonitor) : void 9637 * 9638 * @param path : string , path of monitored object properties (strictly objects, no arrays, maps etc) 9639 * property names separated by '.'. 9640 * @param paths : string[] , further, optional paths to monitor 9641 * 9642 * 9643 * The decorated function must have one parameter of type IMonitor and no return value. 9644 * 9645 * Example: @Monitor('varName.obj', 'varName.obj.proA', 'varName2') onChange(m : IMonitor) : void { ... } 9646 * monitors assignments to this.varName.obj, this.varName.obj.propA, and this.varName2 . 9647 * 9648 * part of SDK 9649 * @since 12 9650 */ 9651const Monitor = function (key, ...keys) { 9652 const pathsUniqueString = keys ? [key, ...keys].join(' ') : key; 9653 return function (target, _, descriptor) { 9654 9655 let watchProp = Symbol.for(MonitorV2.WATCH_PREFIX + target.constructor.name); 9656 const monitorFunc = descriptor.value; 9657 target[watchProp] ? target[watchProp][pathsUniqueString] = monitorFunc : target[watchProp] = { [pathsUniqueString]: monitorFunc }; 9658 }; 9659}; 9660/** 9661 * @Computed TS computed class member variable decorator, inside either @ComponentV2 or @ObservedV2 class 9662 * 9663 * must be a computed class property following TS syntax, e.g. @Computed get varName() { return this.state1 + this.state2 } 9664 * value assignment / set not allowed = has no setter. 9665 * The framework updates the value of the @Computed variable whenever its input changes 9666 * Therefore, the getter function must only use variables whose changes can be observed. 9667 * The getter function implementation must not mutate any state. 9668 * Changes of the return value of the getter function must be observable to use for constructing UI. 9669 * This means if the return value is an object, it must be @ObservedV2 class instance with @Trace 'ed properties, 9670 * or of Array, Map, Set, or Date type. 9671 * The app should not modify the return value because re-execution of the getter function would overwrite these changes. 9672 * 9673 * part of SDK 9674 * @from 12 9675 * 9676 */ 9677const Computed = (target, propertyKey, descriptor) => { 9678 9679 let watchProp = Symbol.for(ComputedV2.COMPUTED_PREFIX + target.constructor.name); 9680 const computeFunction = descriptor.get; 9681 target[watchProp] ? target[watchProp][propertyKey] = computeFunction 9682 : target[watchProp] = { [propertyKey]: computeFunction }; 9683}; 9684/* 9685 * Copyright (c) 2024 Huawei Device Co., Ltd. 9686 * Licensed under the Apache License, Version 2.0 (the "License"); 9687 * you may not use this file except in compliance with the License. 9688 * You may obtain a copy of the License at 9689 * 9690 * http://www.apache.org/licenses/LICENSE-2.0 9691 * 9692 * Unless required by applicable law or agreed to in writing, software 9693 * distributed under the License is distributed on an "AS IS" BASIS, 9694 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9695 * See the License for the specific language governing permissions and 9696 * limitations under the License. 9697 */ 9698const V2_STATE_PREFIX = '__ob_'; 9699const V2_PREFIX_LENGTH = V2_STATE_PREFIX.length; 9700; 9701class Meta { 9702 static define(proto, prop, value) { 9703 const meta = Meta.proto2props.get(proto); 9704 if (!meta) { 9705 Meta.proto2props.set(proto, { [prop]: value }); 9706 } 9707 else { 9708 meta[prop] = value; 9709 } 9710 } 9711 static get(obj, prop) { 9712 let proto = obj.__proto__; 9713 while (proto) { 9714 let meta = Meta.proto2props.get(proto); 9715 if (meta && meta[prop]) { 9716 return meta[prop]; 9717 } 9718 proto = proto.__proto__; 9719 } 9720 return undefined; 9721 } 9722 static gets(obj) { 9723 const ret = {}; 9724 let proto = obj.__proto__; 9725 while (proto) { 9726 let meta = Meta.proto2props.get(proto); 9727 Object.assign(ret, meta); 9728 proto = proto.__proto__; 9729 } 9730 return ret; 9731 } 9732 static getOwn(obj, prop) { 9733 const meta = Meta.proto2props.get(obj.__proto__); 9734 return meta && meta[prop]; 9735 } 9736} 9737Meta.proto2props = new WeakMap(); 9738function __Type__(type, alias) { 9739 const options = JSONCoder.getOptions(type); 9740 if (alias) { 9741 options.alias = alias; 9742 } 9743 return (target, prop) => { 9744 const tar = typeof target === 'function' ? target.prototype : target; 9745 Meta.define(tar, prop, options); 9746 }; 9747} 9748function ObservedReplacer(replacer) { 9749 const defaultReplacer = function (key, value) { 9750 return value; 9751 }; 9752 const realReplacer = replacer || defaultReplacer; 9753 return function (key, value) { 9754 if (typeof value !== 'object' || Array.isArray(value)) { 9755 return realReplacer.call(this, key, value); 9756 } 9757 if (value instanceof Set) { 9758 return realReplacer.call(this, key, Array.from(value)); 9759 } 9760 if (value instanceof Map) { 9761 return realReplacer.call(this, key, Array.from(value.entries())); 9762 } 9763 const ret = {}; 9764 const meta = Meta.gets(value); 9765 Object.keys(value).forEach(key => { 9766 let saveKey = key.startsWith(V2_STATE_PREFIX) ? key.substring(V2_PREFIX_LENGTH) : key; 9767 let options = meta && meta[saveKey]; 9768 if (options && options.disabled) { 9769 return; 9770 } 9771 ret[(options && options.alias) || saveKey] = value[saveKey]; 9772 }); 9773 return realReplacer.call(this, key, ret); 9774 }; 9775} 9776/** 9777 * JSONCoder 9778 * 9779 * The JSONCoder utility enhances the serialization and deserialization capabilities beyond 9780 * the standard JSON.stringify and JSON.parse methods. While JSON.stringify serializes 9781 * object properties and their values, it drops functions, class, property, and function decorators, 9782 * and does not support Map, Set, or Date serialization. JSONCoder addresses these limitations, 9783 * providing robust support for a wider range of JavaScript features. 9784 * 9785 * Main Features: 9786 * - Adds support for serializing and deserializing class instances, including methods and decorators. 9787 * - Supports serialization of complex data structures like Map, Set, and Date. 9788 * - Provides full reconstruction of class instances through the JSONCoder.parseTo method. 9789 * 9790 * Usage Scenarios: 9791 * - Serializing class instances to JSON for network transmission or storage. 9792 * - Deserializing JSON data back into fully functional class instances, preserving methods and decorators. 9793 * - Converting JSON data received from network or database into state management observable view models (e.g., @ObservedV2 class objects). 9794 * 9795 * The name 'JSONCoder' is derived from the 'JSON stringify/parse', reflecting its purpose to enhance JSON serialization and deserialization for classes. 9796 * 9797 */ 9798class JSONCoder { 9799 /** 9800 * Serializes the given object into a string. This string includes additional meta info 9801 * allowing `stringify` to fully reconstruct the original object, including its class 9802 * type and properties. 9803 * 9804 * @template T - The type of the object being serialized. 9805 * @param { T } value - The object to serialize. 9806 * @param { (this: JSONAny, key: string, value: JSONAny) => JSONAny } [replacer] - A function that alters the behavior when stringify 9807 * @param { string | number } [space] - For format 9808 * @returns { string } The serialized string representation of the object. 9809 */ 9810 static stringify(value, replacer, space) { 9811 return JSON.stringify(value, ObservedReplacer(replacer), space); 9812 } 9813 /** 9814 * Parses a JSON string or object and applies the nested key-values to a class object. 9815 * The main usage scenario is to convert JSON data received from a network or database 9816 * to a state management observable view model. 9817 * 9818 * @template T - The type of the object being parsed. 9819 * @param { TypeConstructor<T> | TransformOptions<T> } type - The class prototype or constructor function that has no parameters. 9820 * @param { object | string } source - The JSON string or JSON object. 9821 * @returns { T | T[] } The parsed object of type T or T[]. 9822 */ 9823 static parse(type, source) { 9824 const json = typeof source === 'string' ? JSON.parse(source) : source; 9825 const options = JSONCoder.getOptions(type); 9826 return Array.isArray(json) ? 9827 JSONCoder.parseIntoArray([], json, options) : 9828 JSONCoder.parseInto(JSONCoder.newItem(json, options), json); 9829 } 9830 /** 9831 * Deserializes a string produced by `parseTo` back into the original object, 9832 * fully reconstructing its class type and properties. 9833 * 9834 * @template T - The original object being parsed. 9835 * @param { T | T[] } type - The original object. 9836 * @param { object | string } source - The JSON string or JSON object. 9837 * @param { TypeConstructor<T> | TransformOptions<T> } [type] - The class prototype or constructor function that has no parameters. 9838 * @returns { T | T[] } The parsed object of type T or T[]. 9839 */ 9840 static parseTo(target, source, type) { 9841 const json = typeof source === 'string' ? JSON.parse(source) : source; 9842 const t1 = Array.isArray(json); 9843 const t2 = Array.isArray(target); 9844 const options = JSONCoder.getOptions(type); 9845 if (t1 && t2) { 9846 JSONCoder.parseIntoArray(target, json, options); 9847 } 9848 else if (!t1 && !t2) { 9849 JSONCoder.parseInto(target, json); 9850 } 9851 else { 9852 throw new Error(`The type of target '${t2}' mismatches the type of source '${t1}'`); 9853 } 9854 return target; 9855 } 9856 /** 9857 * Get the type options from the object creator. 9858 * 9859 * @template T - The object being parsed. 9860 * @param { TypeConstructor<T> | TransformOptions<T> | string } [type] - The type info of the object creator. 9861 * @returns { TransformOptions<T> } The options of the type info. 9862 */ 9863 static getOptions(type) { 9864 const paramType = typeof type; 9865 const options = {}; 9866 if (paramType === 'object') { 9867 Object.assign(options, type); 9868 } 9869 else if (paramType === 'function') { 9870 options.factory = (_) => type; 9871 } 9872 else if (paramType === 'string') { 9873 options.alias = type; 9874 } 9875 return options; 9876 } 9877 static getAlias2Prop(meta, target) { 9878 const ret = new Map(); 9879 Object.keys(meta).forEach(prop => { 9880 const options = meta[prop]; 9881 ret.set(options.alias || prop, prop); 9882 }); 9883 return ret; 9884 } 9885 static parseInto(target, source) { 9886 if (typeof source !== 'object') { 9887 throw new Error(`The type of target '${typeof target}' mismatches the type of source '${typeof source}'`); 9888 } 9889 const meta = Meta.gets(target); 9890 const alias2prop = JSONCoder.getAlias2Prop(meta, target); 9891 Object.keys(source).forEach((key) => { 9892 const prop = alias2prop.get(key) || key; 9893 const options = meta && meta[prop]; 9894 if (options && options.disabled) { 9895 return; 9896 } 9897 JSONCoder.parseItemInto(target, prop, source, options); 9898 }); 9899 return target; 9900 } 9901 static parseItemInto(target, targetKey, source, options) { 9902 if (source === null || source === undefined) { 9903 return; 9904 } 9905 let tarType = typeof target[targetKey]; 9906 if (tarType === 'function') { 9907 return; 9908 } 9909 const sourceKey = (options === null || options === void 0 ? void 0 : options.alias) || targetKey; 9910 // Handling invalid values 9911 const value = JSONCoder.getTargetValue(source[sourceKey], options); 9912 if (value === undefined || value === null) { 9913 if (tarType === 'object') { 9914 if (target[targetKey] instanceof Map || target[targetKey] instanceof Set) { 9915 target[targetKey].clear(); 9916 } 9917 else if (Array.isArray(target[targetKey])) { 9918 target[targetKey].splice(0, target[targetKey].length); 9919 } 9920 else if (options && options.factory) { 9921 // if options.factory exists, can be assigned to undefined or null 9922 target[targetKey] = value; 9923 } 9924 } 9925 // other scene ignore all 9926 return; 9927 } 9928 // value is array, it maybe array or map or set 9929 if (Array.isArray(value)) { 9930 target[targetKey] = JSONCoder.parseIntoArray(target[targetKey], value, options); 9931 return; 9932 } 9933 // if target[targetKey] invalid, then attempt create 9934 if (target[targetKey] === null || target[targetKey] === undefined) { 9935 target[targetKey] = JSONCoder.newItem(value, options); 9936 tarType = typeof target[targetKey]; 9937 } 9938 if (typeof value !== 'object') { 9939 // value is Primitive Type 9940 if (target[targetKey] instanceof Date) { 9941 target[targetKey] = new Date(value); 9942 } 9943 else if (tarType === 'string') { 9944 target[targetKey] = value.toString(); 9945 } 9946 else if (tarType === typeof value) { 9947 target[targetKey] = value; 9948 } 9949 else if (target[targetKey] !== undefined) { 9950 throw new Error(`The type of target '${tarType}' mismatches the type of source '${typeof value}'`); 9951 } 9952 return; 9953 } 9954 // value is object, target[targetKey] is undefined or null 9955 if (target[targetKey] === null) { 9956 throw new Error(`Miss @Type in object defined, the property name is ${targetKey}`); 9957 } 9958 else if (target[targetKey] === undefined) { 9959 // ignore target[targetKey] undefined 9960 return; 9961 } 9962 this.parseInto(target[targetKey], value); 9963 } 9964 static newItem(json, options) { 9965 const type = options === null || options === void 0 ? void 0 : options.factory(json); 9966 return type && new type(); 9967 } 9968 static getTargetValue(value, options) { 9969 // future can convert the value to different type or value 9970 return value; 9971 } 9972 static parseIntoArray(target, source, options) { 9973 if (typeof target !== 'object') { 9974 throw new Error(`The type of target '${typeof target}' mismatches the type of source '${typeof source}'`); 9975 } 9976 // here, source maybe a array or map or set 9977 if (target instanceof Map) { 9978 target.clear(); 9979 for (let i = 0; i < source.length; ++i) { 9980 // If target is a map, item must be an array. Otherwise, ignore it 9981 const item = source[i]; 9982 if (!Array.isArray(item) || item.length < 2 || typeof item[0] !== 'string') { 9983 continue; 9984 } 9985 target.set(item[0], typeof item[1] !== 'object' ? item[1] : JSONCoder.parse(options, item[1])); 9986 } 9987 return target; 9988 } 9989 if (target instanceof Set) { 9990 target.clear(); 9991 for (let i = 0; i < source.length; ++i) { 9992 const item = source[i]; 9993 target.add(typeof item !== 'object' ? item : JSONCoder.parse(options, item)); 9994 } 9995 return target; 9996 } 9997 target.length = source.length; 9998 for (let i = 0; i < source.length; ++i) { 9999 const item = source[i]; 10000 if (typeof item !== 'object') { 10001 target[i] = item; 10002 continue; 10003 } 10004 if (i === 0) { 10005 if (!(options === null || options === void 0 ? void 0 : options.factory)) { 10006 target.length = 0; 10007 throw new Error(`Miss @Type in array defined`); 10008 } 10009 } 10010 target[i] = Array.isArray(item) ? 10011 JSONCoder.parseIntoArray(target[i] || [], item, options) : 10012 JSONCoder.parseInto(target[i] || JSONCoder.newItem(item, options), item); 10013 } 10014 return target; 10015 } 10016} 10017/* 10018 * Copyright (c) 2024 Huawei Device Co., Ltd. 10019 * Licensed under the Apache License, Version 2.0 (the "License"); 10020 * you may not use this file except in compliance with the License. 10021 * You may obtain a copy of the License at 10022 * 10023 * http://www.apache.org/licenses/LICENSE-2.0 10024 * 10025 * Unless required by applicable law or agreed to in writing, software 10026 * distributed under the License is distributed on an "AS IS" BASIS, 10027 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10028 * See the License for the specific language governing permissions and 10029 * limitations under the License. 10030 */ 10031class RefInfo { 10032 static get(target) { 10033 if (!target || typeof target !== 'object') { 10034 stateMgmtConsole.warn(`makeObserved target is not a valid object, return target directly`); 10035 return { proxy: target }; 10036 } 10037 // makeObserved does not support @Observed, @ObservedV2/@Trace class or makeObserved proxy, will return target directly 10038 if (ObservedObject.IsObservedObject(target) || ObserveV2.IsObservedObjectV2(target) || ObserveV2.IsMakeObserved(target)) { 10039 stateMgmtConsole.warn(`${target.constructor.name} is Observed ${ObservedObject.IsObservedObject(target)}, IsObservedV2 ${ObserveV2.IsObservedObjectV2(target)} or makeObserved proxy value ${ObserveV2.IsMakeObserved(target)}. makeObserved will stop work`); 10040 return { proxy: target }; 10041 } 10042 let ret = RefInfo.obj2ref.get(target); 10043 if (!ret) { 10044 if (Array.isArray(target) || SendableType.isArray(target)) { 10045 ret = { proxy: new Proxy(target, RefInfo.arrayProxy) }; 10046 } 10047 else if (target instanceof Set || SendableType.isSet(target) || 10048 target instanceof Map || SendableType.isMap(target)) { 10049 ret = { proxy: new Proxy(target, RefInfo.setMapProxy) }; 10050 } 10051 else { 10052 ret = { proxy: new Proxy(target, RefInfo.objectProxy) }; 10053 } 10054 RefInfo.obj2ref.set(target, ret); 10055 } 10056 return ret; 10057 } 10058} 10059RefInfo.obj2ref = new WeakMap(); 10060RefInfo.setMapProxy = new SetMapProxyHandler(true); 10061RefInfo.arrayProxy = new ArrayProxyHandler(true); 10062RefInfo.objectProxy = new ObjectProxyHandler(true); 10063/* 10064 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 10065 * Licensed under the Apache License, Version 2.0 (the "License"); 10066 * you may not use this file except in compliance with the License. 10067 * You may obtain a copy of the License at 10068 * 10069 * http://www.apache.org/licenses/LICENSE-2.0 10070 * 10071 * Unless required by applicable law or agreed to in writing, software 10072 * distributed under the License is distributed on an "AS IS" BASIS, 10073 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10074 * See the License for the specific language governing permissions and 10075 * limitations under the License. 10076 * 10077 * all definitions in this file are framework internal 10078*/ 10079var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 10080 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 10081 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 10082 else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 10083 return c > 3 && r && Object.defineProperty(target, key, r), r; 10084}; 10085// implementation for existing state observation system 10086class __RepeatItemPU { 10087 constructor(owningView, initialItem, initialIndex) { 10088 this._observedItem = new ObservedPropertyPU(initialItem, owningView, 'Repeat item'); 10089 if (initialIndex !== undefined) { 10090 this._observedIndex = new ObservedPropertyPU(initialIndex, owningView, 'Repeat index'); 10091 } 10092 } 10093 get item() { 10094 return this._observedItem.get(); 10095 } 10096 get index() { 10097 var _a; 10098 return (_a = this._observedIndex) === null || _a === void 0 ? void 0 : _a.get(); 10099 } 10100 updateItem(newItemValue) { 10101 this._observedItem.set(newItemValue); 10102 } 10103 updateIndex(newIndex) { 10104 var _a, _b, _c; 10105 if (!((_a = this._observedIndex) === null || _a === void 0 ? void 0 : _a.hasDependencies())) { 10106 return; 10107 } 10108 if (((_b = this._observedIndex) === null || _b === void 0 ? void 0 : _b.getUnmonitored()) !== newIndex) { 10109 (_c = this._observedIndex) === null || _c === void 0 ? void 0 : _c.set(newIndex); 10110 } 10111 } 10112} 10113// Framework internal, deep observation 10114// Using @ObservedV2_Internal instead of @ObservedV2 to avoid forcing V2 usage. 10115let __RepeatItemV2 = class __RepeatItemV2 { 10116 constructor(initialItem, initialIndex) { 10117 this.item = initialItem; 10118 this.index = initialIndex; 10119 } 10120 updateItem(newItemValue) { 10121 this.item = newItemValue; 10122 } 10123 updateIndex(newIndex) { 10124 if (this.index !== undefined) { 10125 this.index = newIndex; 10126 } 10127 } 10128}; 10129__decorate([ 10130 Trace_Internal 10131], __RepeatItemV2.prototype, "item", void 0); 10132__decorate([ 10133 Trace_Internal 10134], __RepeatItemV2.prototype, "index", void 0); 10135__RepeatItemV2 = __decorate([ 10136 ObservedV2_Internal 10137], __RepeatItemV2); 10138// helper 10139class __RepeatDefaultKeyGen { 10140 // Return the same IDs for the same items 10141 static func(item) { 10142 try { 10143 return __RepeatDefaultKeyGen.funcImpl(item); 10144 } 10145 catch (e) { 10146 throw new Error(`Repeat(). Default id gen failed. Application Error!`); 10147 } 10148 } 10149 // Return the same IDs for the same pairs <item, index> 10150 static funcWithIndex(item, index) { 10151 return `${index}__` + __RepeatDefaultKeyGen.func(item); 10152 } 10153 static funcImpl(item) { 10154 // fast keygen logic can be used with objects/symbols only 10155 if (typeof item !== 'object' && typeof item !== 'symbol') { 10156 return JSON.stringify(item); 10157 } 10158 // generate a numeric key, store mappings in WeakMap 10159 if (!this.weakMap_.has(item)) { 10160 return this.weakMap_.set(item, ++this.lastKey_), `${this.lastKey_}`; 10161 } 10162 // use cached key 10163 return `${this.weakMap_.get(item)}`; 10164 } 10165} 10166__RepeatDefaultKeyGen.weakMap_ = new WeakMap(); 10167__RepeatDefaultKeyGen.lastKey_ = 0; 10168; 10169; 10170// __Repeat implements ForEach with child re-use for both existing state observation 10171// and deep observation , for non-virtual and virtual code paths (TODO) 10172class __Repeat { 10173 constructor(owningView, arr) { 10174 this.config = {}; 10175 this.isVirtualScroll = false; 10176 this.config.owningView_ = owningView instanceof ViewV2 ? owningView : undefined; 10177 this.config.arr = arr !== null && arr !== void 0 ? arr : []; 10178 this.config.itemGenFuncs = {}; 10179 this.config.keyGenFunc = __RepeatDefaultKeyGen.funcWithIndex; 10180 this.config.typeGenFunc = (() => ''); 10181 this.config.totalCountSpecified = false; 10182 this.config.totalCount = this.config.arr.length; 10183 this.config.templateOptions = {}; 10184 // to be used with ViewV2 10185 const mkRepeatItemV2 = (item, index) => new __RepeatItemV2(item, index); 10186 // to be used with ViewPU 10187 const mkRepeatItemPU = (item, index) => new __RepeatItemPU(owningView, item, index); 10188 const isViewV2 = (this.config.owningView_ instanceof ViewV2); 10189 this.config.mkRepeatItem = isViewV2 ? mkRepeatItemV2 : mkRepeatItemPU; 10190 } 10191 each(itemGenFunc) { 10192 this.config.itemGenFuncs[''] = itemGenFunc; 10193 this.config.templateOptions[''] = this.normTemplateOptions({}); 10194 return this; 10195 } 10196 key(keyGenFunc) { 10197 this.config.keyGenFunc = keyGenFunc; 10198 return this; 10199 } 10200 virtualScroll(options) { 10201 if (Number.isInteger(options === null || options === void 0 ? void 0 : options.totalCount)) { 10202 this.config.totalCount = options.totalCount; 10203 this.config.totalCountSpecified = true; 10204 } 10205 else { 10206 this.config.totalCountSpecified = false; 10207 } 10208 this.isVirtualScroll = true; 10209 return this; 10210 } 10211 // function to decide which template to use, each template has an id 10212 templateId(typeGenFunc) { 10213 const typeGenFuncImpl = (item, index) => { 10214 try { 10215 return typeGenFunc(item, index); 10216 } 10217 catch (e) { 10218 stateMgmtConsole.applicationError(`Repeat with virtual scroll. Exception in templateId():`, e === null || e === void 0 ? void 0 : e.message); 10219 return ''; 10220 } 10221 }; 10222 // typeGenFunc wrapper with ttype validation 10223 const typeGenFuncSafe = (item, index) => { 10224 const itemType = typeGenFuncImpl(item, index); 10225 const itemFunc = this.config.itemGenFuncs[itemType]; 10226 if (typeof itemFunc !== 'function') { 10227 stateMgmtConsole.applicationError(`Repeat with virtual scroll. Missing Repeat.template for id '${itemType}'`); 10228 return ''; 10229 } 10230 return itemType; 10231 }; 10232 this.config.typeGenFunc = typeGenFuncSafe; 10233 return this; 10234 } 10235 // template: id + builder function to render specific type of data item 10236 template(type, itemGenFunc, options) { 10237 this.config.itemGenFuncs[type] = itemGenFunc; 10238 this.config.templateOptions[type] = this.normTemplateOptions(options); 10239 return this; 10240 } 10241 updateArr(arr) { 10242 this.config.arr = arr !== null && arr !== void 0 ? arr : []; 10243 return this; 10244 } 10245 render(isInitialRender) { 10246 var _a, _b, _c; 10247 if (!((_a = this.config.itemGenFuncs) === null || _a === void 0 ? void 0 : _a[''])) { 10248 throw new Error(`__Repeat item builder function unspecified. Usage error`); 10249 } 10250 if (!this.isVirtualScroll) { 10251 // Repeat 10252 (_b = this.impl) !== null && _b !== void 0 ? _b : (this.impl = new __RepeatImpl()); 10253 this.impl.render(this.config, isInitialRender); 10254 } 10255 else { 10256 // RepeatVirtualScroll 10257 (_c = this.impl) !== null && _c !== void 0 ? _c : (this.impl = new __RepeatVirtualScrollImpl()); 10258 this.impl.render(this.config, isInitialRender); 10259 } 10260 } 10261 // drag and drop API 10262 onMove(handler) { 10263 this.config.onMoveHandler = handler; 10264 return this; 10265 } 10266 // normalize template options 10267 normTemplateOptions(options) { 10268 const value = (options && Number.isInteger(options.cachedCount) && options.cachedCount >= 0) 10269 ? { 10270 cachedCount: Math.max(0, options.cachedCount), 10271 cachedCountSpecified: true 10272 } 10273 : { 10274 cachedCountSpecified: false 10275 }; 10276 return value; 10277 } 10278} 10279; // __Repeat<T> 10280/* 10281 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 10282 * Licensed under the Apache License, Version 2.0 (the "License"); 10283 * you may not use this file except in compliance with the License. 10284 * You may obtain a copy of the License at 10285 * 10286 * http://www.apache.org/licenses/LICENSE-2.0 10287 * 10288 * Unless required by applicable law or agreed to in writing, software 10289 * distributed under the License is distributed on an "AS IS" BASIS, 10290 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10291 * See the License for the specific language governing permissions and 10292 * limitations under the License. 10293 * 10294 * all definitions in this file are framework internal 10295*/ 10296class __RepeatImpl { 10297 /**/ 10298 constructor() { 10299 this.key2Item_ = new Map(); 10300 } 10301 /**/ 10302 render(config, isInitialRender) { 10303 this.arr_ = config.arr; 10304 this.itemGenFuncs_ = config.itemGenFuncs; 10305 this.typeGenFunc_ = config.typeGenFunc; 10306 this.keyGenFunction_ = config.keyGenFunc; 10307 this.mkRepeatItem_ = config.mkRepeatItem; 10308 this.onMoveHandler_ = config.onMoveHandler; 10309 isInitialRender ? this.initialRender() : this.reRender(); 10310 } 10311 genKeys() { 10312 const key2Item = new Map(); 10313 this.arr_.forEach((item, index) => { 10314 const key = this.keyGenFunction_(item, index); 10315 key2Item.set(key, { key, index }); 10316 }); 10317 if (key2Item.size < this.arr_.length) { 10318 stateMgmtConsole.warn('__RepeatImpl: Duplicates detected, fallback to index-based keyGen.'); 10319 // Causes all items to be re-rendered 10320 this.keyGenFunction_ = __RepeatDefaultKeyGen.funcWithIndex; 10321 return this.genKeys(); 10322 } 10323 return key2Item; 10324 } 10325 initialRender() { 10326 //console.log('__RepeatImpl initialRender() 0') 10327 this.key2Item_ = this.genKeys(); 10328 RepeatNative.startRender(); 10329 let index = 0; 10330 this.key2Item_.forEach((itemInfo, key) => { 10331 itemInfo.repeatItem = this.mkRepeatItem_(this.arr_[index], index); 10332 this.initialRenderItem(key, itemInfo.repeatItem); 10333 index++; 10334 }); 10335 let removedChildElmtIds = new Array(); 10336 // Fetch the removedChildElmtIds from C++ to unregister those elmtIds with UINodeRegisterProxy 10337 RepeatNative.onMove(this.onMoveHandler_); 10338 RepeatNative.finishRender(removedChildElmtIds); 10339 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 10340 10341 } 10342 reRender() { 10343 const oldKey2Item = this.key2Item_; 10344 this.key2Item_ = this.genKeys(); 10345 // identify array items that have been deleted 10346 // these are candidates for re-use 10347 const deletedKeysAndIndex = new Array(); 10348 for (const [key, feInfo] of oldKey2Item) { 10349 if (!this.key2Item_.has(key)) { 10350 deletedKeysAndIndex.push(feInfo); 10351 } 10352 } 10353 // C++: mv children_ aside to tempchildren_ 10354 RepeatNative.startRender(); 10355 let index = 0; 10356 this.key2Item_.forEach((itemInfo, key) => { 10357 const item = this.arr_[index]; 10358 let oldItemInfo = oldKey2Item.get(key); 10359 if (oldItemInfo) { 10360 // case #1 retained array item 10361 // moved from oldIndex to index 10362 const oldIndex = oldItemInfo.index; 10363 itemInfo.repeatItem = oldItemInfo.repeatItem; 10364 10365 itemInfo.repeatItem.updateIndex(index); 10366 // C++ mv from tempChildren[oldIndex] to end of children_ 10367 RepeatNative.moveChild(oldIndex); 10368 // TBD moveChild() only when item types are same 10369 } 10370 else if (deletedKeysAndIndex.length) { 10371 // case #2: 10372 // new array item, there is an deleted array items whose 10373 // UINode children cab re-used 10374 const oldItemInfo = deletedKeysAndIndex.pop(); 10375 const reuseKey = oldItemInfo.key; 10376 const oldKeyIndex = oldItemInfo.index; 10377 const oldRepeatItem = oldItemInfo.repeatItem; 10378 itemInfo.repeatItem = oldRepeatItem; 10379 10380 itemInfo.repeatItem.updateItem(item); 10381 itemInfo.repeatItem.updateIndex(index); 10382 // update key2item_ Map 10383 this.key2Item_.set(key, itemInfo); 10384 // TBD moveChild() only when item types are same 10385 // C++ mv from tempChildren[oldIndex] to end of children_ 10386 RepeatNative.moveChild(oldKeyIndex); 10387 } 10388 else { 10389 // case #3: 10390 // new array item, there are no deleted array items 10391 // render new UINode children 10392 itemInfo.repeatItem = this.mkRepeatItem_(item, index); 10393 this.initialRenderItem(key, itemInfo.repeatItem); 10394 } 10395 index++; 10396 }); 10397 // keep this.id2item_. by removing all entries for remaining 10398 // deleted items 10399 deletedKeysAndIndex.forEach(delItem => { 10400 this.key2Item_.delete(delItem.key); 10401 }); 10402 // Finish up for.each update 10403 // C++ tempChildren.clear() , trigger re-layout 10404 let removedChildElmtIds = new Array(); 10405 // Fetch the removedChildElmtIds from C++ to unregister those elmtIds with UINodeRegisterProxy 10406 RepeatNative.onMove(this.onMoveHandler_); 10407 RepeatNative.finishRender(removedChildElmtIds); 10408 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 10409 10410 } 10411 initialRenderItem(key, repeatItem) { 10412 var _a, _b; 10413 //console.log('__RepeatImpl initialRenderItem()') 10414 // render new UINode children 10415 10416 // C++: initial render will render to the end of children_ 10417 RepeatNative.createNewChildStart(key); 10418 // execute the itemGen function 10419 const itemType = (_a = this.typeGenFunc_(repeatItem.item, repeatItem.index)) !== null && _a !== void 0 ? _a : ''; 10420 const itemFunc = (_b = this.itemGenFuncs_[itemType]) !== null && _b !== void 0 ? _b : this.itemGenFuncs_['']; 10421 itemFunc(repeatItem); 10422 RepeatNative.createNewChildFinish(key); 10423 } 10424} 10425; 10426/* 10427 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 10428 * Licensed under the Apache License, Version 2.0 (the "License"); 10429 * you may not use this file except in compliance with the License. 10430 * You may obtain a copy of the License at 10431 * 10432 * http://www.apache.org/licenses/LICENSE-2.0 10433 * 10434 * Unless required by applicable law or agreed to in writing, software 10435 * distributed under the License is distributed on an "AS IS" BASIS, 10436 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10437 * See the License for the specific language governing permissions and 10438 * limitations under the License. 10439 * 10440 * all definitions in this file are framework internal 10441*/ 10442// Implements ForEach with child re-use for both existing state observation and 10443// deep observation. For virtual-scroll code paths 10444class __RepeatVirtualScrollImpl { 10445 constructor() { 10446 this.totalCountSpecified = false; 10447 // index <-> key maps 10448 this.key4Index_ = new Map(); 10449 this.index4Key_ = new Map(); 10450 // Map key -> RepeatItem 10451 // added to closure of following lambdas 10452 this.repeatItem4Key_ = new Map(); 10453 // RepeatVirtualScrollNode elmtId 10454 this.repeatElmtId_ = -1; 10455 // Last known active range (as sparse array) 10456 this.lastActiveRangeData_ = []; 10457 } 10458 render(config, isInitialRender) { 10459 this.arr_ = config.arr; 10460 this.itemGenFuncs_ = config.itemGenFuncs; 10461 this.keyGenFunc_ = config.keyGenFunc; 10462 this.typeGenFunc_ = config.typeGenFunc; 10463 // if totalCountSpecified==false, then need to create dependency on array length 10464 // so when array length changes, will update totalCount 10465 this.totalCountSpecified = config.totalCountSpecified; 10466 this.totalCount_ = (!this.totalCountSpecified || config.totalCount < 0) 10467 ? this.arr_.length 10468 : config.totalCount; 10469 this.templateOptions_ = config.templateOptions; 10470 this.mkRepeatItem_ = config.mkRepeatItem; 10471 this.onMoveHandler_ = config.onMoveHandler; 10472 if (isInitialRender) { 10473 this.initialRender(config.owningView_, ObserveV2.getCurrentRecordedId()); 10474 } 10475 else { 10476 this.reRender(); 10477 } 10478 } 10479 /**/ 10480 initialRender(owningView, repeatElmtId) { 10481 this.repeatElmtId_ = repeatElmtId; 10482 const onCreateNode = (forIndex) => { 10483 10484 if (forIndex < 0 || forIndex >= this.totalCount_ || forIndex >= this.arr_.length) { 10485 // STATE_MGMT_NOTE check also index < totalCount 10486 throw new Error(`__RepeatVirtualScrollImpl (${this.repeatElmtId_}) onCreateNode: for index=${forIndex} \ 10487 with data array length ${this.arr_.length}, totalCount=${this.totalCount_} out of range error.`); 10488 } 10489 // create dependency array item [forIndex] -> Repeat 10490 // so Repeat updates when the array item changes 10491 // STATE_MGMT_NOTE observe dependencies, adding the array is insurgent for Array of objects 10492 ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, forIndex.toString()); 10493 const repeatItem = this.mkRepeatItem_(this.arr_[forIndex], forIndex); 10494 let forKey = this.getOrMakeKey4Index(forIndex); 10495 this.repeatItem4Key_.set(forKey, repeatItem); 10496 // execute the itemGen function 10497 this.initialRenderItem(repeatItem); 10498 10499 }; // onCreateNode 10500 const onUpdateNode = (fromKey, forIndex) => { 10501 if (!fromKey || fromKey === '' || forIndex < 0 || forIndex >= this.totalCount_ || forIndex >= this.arr_.length) { 10502 throw new Error(`__RepeatVirtualScrollImpl (${this.repeatElmtId_}) onUpdateNode: fromKey "${fromKey}", \ 10503 forIndex=${forIndex}, with data array length ${this.arr_.length}, totalCount=${this.totalCount_}, invalid function input error.`); 10504 } 10505 // create dependency array item [forIndex] -> Repeat 10506 // so Repeat updates when the array item changes 10507 // STATE_MGMT_NOTE observe dependencies, adding the array is insurgent for Array of objects 10508 ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, forIndex.toString()); 10509 const repeatItem = this.repeatItem4Key_.get(fromKey); 10510 if (!repeatItem) { 10511 stateMgmtConsole.error(`__RepeatVirtualScrollImpl (${this.repeatElmtId_}) onUpdateNode: fromKey "${fromKey}", forIndex=${forIndex}, can not find RepeatItem for key. Unrecoverable error`); 10512 return; 10513 } 10514 const forKey = this.getOrMakeKey4Index(forIndex); 10515 10516 // update Map according to made update: 10517 // del fromKey entry and add forKey 10518 this.repeatItem4Key_.delete(fromKey); 10519 this.repeatItem4Key_.set(forKey, repeatItem); 10520 if (repeatItem.item !== this.arr_[forIndex] || repeatItem.index !== forIndex) { 10521 // repeatItem needs update, will trigger partial update to using UINodes: 10522 repeatItem.updateItem(this.arr_[forIndex]); 10523 repeatItem.updateIndex(forIndex); 10524 10525 ObserveV2.getObserve().updateDirty2(true); 10526 } 10527 }; // onUpdateNode 10528 const onGetKeys4Range = (from, to) => { 10529 if (to > this.totalCount_ || to > this.arr_.length) { 10530 stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId ${this.repeatElmtId_}: onGetKeys4Range from ${from} to ${to} \ 10531 with data array length ${this.arr_.length}, totalCount=${this.totalCount_} \ 10532 Error!. Application fails to add more items to source data array. on time. Trying with corrected input parameters ...`); 10533 to = this.totalCount_; 10534 from = Math.min(to, from); 10535 } 10536 10537 const result = new Array(); 10538 // deep observe dependencies, 10539 // create dependency array item [i] -> Repeat 10540 // so Repeat updates when the array item or nested objects changes 10541 // not enough: ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, i.toString()); 10542 ViewStackProcessor.StartGetAccessRecordingFor(this.repeatElmtId_); 10543 ObserveV2.getObserve().startRecordDependencies(owningView, this.repeatElmtId_, false); 10544 for (let i = from; i <= to && i < this.arr_.length; i++) { 10545 result.push(this.getOrMakeKey4Index(i)); 10546 } 10547 ObserveV2.getObserve().stopRecordDependencies(); 10548 ViewStackProcessor.StopGetAccessRecording(); 10549 let needsRerender = false; 10550 result.forEach((key, index) => { 10551 const forIndex = index + from; 10552 // if repeatItem exists, and needs update then do the update, and call sync update as well 10553 // thereby ensure cached items are up-to-date on C++ side. C++ does not need to request update 10554 // from TS side 10555 const repeatItem4Key = this.repeatItem4Key_.get(key); 10556 // make sure the index is up-to-date 10557 if (repeatItem4Key && (repeatItem4Key.item !== this.arr_[forIndex] || repeatItem4Key.index !== forIndex)) { 10558 // repeatItem needs update, will trigger partial update to using UINodes: 10559 repeatItem4Key.updateItem(this.arr_[forIndex]); 10560 repeatItem4Key.updateIndex(forIndex); 10561 needsRerender = true; 10562 } 10563 }); // forEach 10564 if (needsRerender) { 10565 10566 ObserveV2.getObserve().updateDirty2(true); 10567 } 10568 10569 return result; 10570 }; // const onGetKeys4Range 10571 const onGetTypes4Range = (from, to) => { 10572 if (to > this.totalCount_ || to > this.arr_.length) { 10573 stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId: ${this.repeatElmtId_}: onGetTypes4Range from ${from} to ${to} \ 10574 with data array length ${this.arr_.length}, totalCount=${this.totalCount_} \ 10575 Error! Application fails to add more items to source data array.on time.Trying with corrected input parameters ...`); 10576 to = this.totalCount_; 10577 from = Math.min(to, from); 10578 } 10579 10580 const result = new Array(); 10581 // deep observe dependencies, 10582 // create dependency array item [i] -> Repeat 10583 // so Repeat updates when the array item or nested objects changes 10584 // not enough: ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, i.toString()); 10585 ViewStackProcessor.StartGetAccessRecordingFor(this.repeatElmtId_); 10586 ObserveV2.getObserve().startRecordDependencies(owningView, this.repeatElmtId_, false); 10587 for (let i = from; i <= to && i < this.arr_.length; i++) { 10588 let ttype = this.typeGenFunc_(this.arr_[i], i); 10589 result.push(ttype); 10590 } // for 10591 ObserveV2.getObserve().stopRecordDependencies(); 10592 ViewStackProcessor.StopGetAccessRecording(); 10593 10594 return result; 10595 }; // const onGetTypes4Range 10596 const onSetActiveRange = (from, to) => { 10597 10598 // make sparse copy of this.arr_ 10599 this.lastActiveRangeData_ = new Array(this.arr_.length); 10600 if (from <= to) { 10601 for (let i = Math.max(0, from); i <= to && i < this.arr_.length; i++) { 10602 const item = this.arr_[i]; 10603 const ttype = this.typeGenFunc_(this.arr_[i], i); 10604 this.lastActiveRangeData_[i] = { item, ttype }; 10605 } 10606 } 10607 else { 10608 for (let i = 0; i <= to && i < this.arr_.length; i++) { 10609 const item = this.arr_[i]; 10610 const ttype = this.typeGenFunc_(this.arr_[i], i); 10611 this.lastActiveRangeData_[i] = { item, ttype }; 10612 } 10613 for (let i = Math.max(0, from); i < this.arr_.length; i++) { 10614 const item = this.arr_[i]; 10615 const ttype = this.typeGenFunc_(this.arr_[i], i); 10616 this.lastActiveRangeData_[i] = { item, ttype }; 10617 } 10618 } 10619 }; 10620 10621 RepeatVirtualScrollNative.create(this.totalCount_, Object.entries(this.templateOptions_), { 10622 onCreateNode, 10623 onUpdateNode, 10624 onGetKeys4Range, 10625 onGetTypes4Range, 10626 onSetActiveRange 10627 }); 10628 RepeatVirtualScrollNative.onMove(this.onMoveHandler_); 10629 10630 } 10631 reRender() { 10632 10633 // When this.totalCount_ == 0 need render to clear visible items 10634 if (this.hasVisibleItemsChanged() || this.totalCount_ === 0) { 10635 this.purgeKeyCache(); 10636 RepeatVirtualScrollNative.updateRenderState(this.totalCount_, true); 10637 10638 } 10639 else { 10640 // avoid re-render when data pushed outside visible area 10641 RepeatVirtualScrollNative.updateRenderState(this.totalCount_, false); 10642 10643 } 10644 } 10645 initialRenderItem(repeatItem) { 10646 // execute the itemGen function 10647 const itemType = this.typeGenFunc_(repeatItem.item, repeatItem.index); 10648 const itemFunc = this.itemGenFuncs_[itemType]; 10649 itemFunc(repeatItem); 10650 } 10651 hasVisibleItemsChanged() { 10652 var _a, _b; 10653 // has any item or ttype in the active range changed? 10654 for (let i in this.lastActiveRangeData_) { 10655 if (!(i in this.arr_)) { 10656 return true; 10657 } 10658 const oldItem = (_a = this.lastActiveRangeData_[+i]) === null || _a === void 0 ? void 0 : _a.item; 10659 const oldType = (_b = this.lastActiveRangeData_[+i]) === null || _b === void 0 ? void 0 : _b.ttype; 10660 const newItem = this.arr_[+i]; 10661 const newType = this.typeGenFunc_(this.arr_[+i], +i); 10662 if (oldItem !== newItem) { 10663 10664 return true; 10665 } 10666 if (oldType !== newType) { 10667 10668 return true; 10669 } 10670 } 10671 10672 return false; 10673 } 10674 /** 10675 * maintain: index <-> key mapping 10676 * create new key from keyGen function if not in cache 10677 * check for duplicate key, and create random key if duplicate found 10678 * @param forIndex 10679 * @returns unique key 10680 */ 10681 getOrMakeKey4Index(forIndex) { 10682 let key = this.key4Index_.get(forIndex); 10683 if (!key) { 10684 key = this.keyGenFunc_(this.arr_[forIndex], forIndex); 10685 const usedIndex = this.index4Key_.get(key); 10686 if (usedIndex !== undefined) { 10687 // duplicate key 10688 stateMgmtConsole.applicationError(`Repeat key gen function elmtId ${this.repeatElmtId_}: Detected duplicate key ${key} for indices ${forIndex} and ${usedIndex}. \ 10689 Generated random key will decrease Repeat performance. Correct the Key gen function in your application!`); 10690 key = `___${forIndex}_+_${key}_+_${Math.random()}`; 10691 } 10692 this.key4Index_.set(forIndex, key); 10693 this.index4Key_.set(key, forIndex); 10694 } 10695 return key; 10696 } 10697 purgeKeyCache() { 10698 this.key4Index_.clear(); 10699 this.index4Key_.clear(); 10700 } 10701} 10702; 10703/* 10704 * Copyright (c) 2024 Huawei Device Co., Ltd. 10705 * Licensed under the Apache License, Version 2.0 (the "License"); 10706 * you may not use this file except in compliance with the License. 10707 * You may obtain a copy of the License at 10708 * 10709 * http://www.apache.org/licenses/LICENSE-2.0 10710 * 10711 * Unless required by applicable law or agreed to in writing, software 10712 * distributed under the License is distributed on an "AS IS" BASIS, 10713 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10714 * See the License for the specific language governing permissions and 10715 * limitations under the License. 10716 */ 10717; 10718class StorageHelper { 10719 constructor() { 10720 this.oldTypeValues_ = new Map(); 10721 } 10722 getConnectedKey(type, keyOrDefaultCreator) { 10723 if (keyOrDefaultCreator === null || keyOrDefaultCreator === undefined) { 10724 stateMgmtConsole.applicationWarn(StorageHelper.NULL_OR_UNDEFINED_KEY + ', try to use the type name as key'); 10725 } 10726 if (typeof keyOrDefaultCreator === 'string') { 10727 return keyOrDefaultCreator; 10728 } 10729 return this.getTypeName(type); 10730 } 10731 getKeyOrTypeName(keyOrType) { 10732 if (typeof keyOrType === 'function') { 10733 keyOrType = this.getTypeName(keyOrType); 10734 } 10735 return keyOrType; 10736 } 10737 checkTypeByName(key, type, oldType) { 10738 if (this.getTypeName(type) !== oldType) { 10739 throw new Error(`The type mismatches when use the key '${key}' in storage`); 10740 } 10741 } 10742 checkTypeByInstanceOf(key, type, ins) { 10743 this.checkTypeIsFunc(type); 10744 if (!(ins instanceof type)) { 10745 throw new Error(`The type mismatches when use the key '${key}' in storage`); 10746 } 10747 } 10748 getTypeName(type) { 10749 this.checkTypeIsFunc(type); 10750 let name = type.name; 10751 while (name === undefined) { 10752 type = Object.getPrototypeOf(type); 10753 if (!type) { 10754 break; 10755 } 10756 name = type.name; 10757 } 10758 return name; 10759 } 10760 isKeyValid(key) { 10761 if (typeof key !== 'string') { 10762 throw new Error(StorageHelper.INVALID_TYPE); 10763 } 10764 // The key string is empty 10765 if (key === '') { 10766 stateMgmtConsole.applicationError(StorageHelper.EMPTY_STRING_KEY); 10767 return false; 10768 } 10769 const len = key.length; 10770 // The key string length should shorter than 1024 10771 if (len >= 1024) { 10772 stateMgmtConsole.applicationError(StorageHelper.INVALID_LENGTH_KEY); 10773 return false; 10774 } 10775 if (len < 2 || len > 255) { 10776 stateMgmtConsole.applicationWarn(StorageHelper.INVALID_LENGTH_KEY); 10777 } 10778 if (!/^[0-9a-zA-Z_]+$/.test(key)) { 10779 stateMgmtConsole.applicationWarn(StorageHelper.INVALID_CHARACTER_KEY); 10780 } 10781 return true; 10782 } 10783 checkTypeIsFunc(type) { 10784 if (typeof type !== 'function') { 10785 throw new Error(StorageHelper.INVALID_TYPE); 10786 } 10787 } 10788} 10789StorageHelper.INVALID_DEFAULT_VALUE = 'The default creator should be function when first connect'; 10790StorageHelper.DELETE_NOT_EXIST_KEY = 'The key to be deleted does not exist'; 10791StorageHelper.INVALID_TYPE = 'The type should have function constructor signature when use storage'; 10792StorageHelper.EMPTY_STRING_KEY = 'Cannot use empty string as the key'; 10793StorageHelper.INVALID_LENGTH_KEY = 'Cannot use the key! The length of key should be 2 to 255'; 10794StorageHelper.INVALID_CHARACTER_KEY = 'Cannot use the key! The value of key can only consist of letters, digits and underscores'; 10795StorageHelper.NULL_OR_UNDEFINED_KEY = `The parameter cannot be null or undefined`; 10796class AppStorageV2Impl extends StorageHelper { 10797 constructor() { 10798 super(); 10799 this.memorizedValues_ = new Map(); 10800 } 10801 static instance() { 10802 if (AppStorageV2Impl.instance_) { 10803 return AppStorageV2Impl.instance_; 10804 } 10805 AppStorageV2Impl.instance_ = new AppStorageV2Impl(); 10806 return AppStorageV2Impl.instance_; 10807 } 10808 connect(type, keyOrDefaultCreator, defaultCreator) { 10809 const key = this.getConnectedKey(type, keyOrDefaultCreator); 10810 if (!this.isKeyValid(key)) { 10811 return undefined; 10812 } 10813 if (typeof keyOrDefaultCreator === 'function') { 10814 defaultCreator = keyOrDefaultCreator; 10815 } 10816 if (!this.memorizedValues_.has(key)) { 10817 if (typeof defaultCreator !== 'function') { 10818 throw new Error(AppStorageV2Impl.INVALID_DEFAULT_VALUE); 10819 } 10820 const defaultValue = defaultCreator(); 10821 this.checkTypeByInstanceOf(key, type, defaultValue); 10822 this.memorizedValues_.set(key, defaultValue); 10823 this.oldTypeValues_.set(key, this.getTypeName(type)); 10824 return defaultValue; 10825 } 10826 this.checkTypeByName(key, type, this.oldTypeValues_.get(key)); 10827 const existedValue = this.memorizedValues_.get(key); 10828 return existedValue; 10829 } 10830 remove(keyOrType) { 10831 if (keyOrType === null || keyOrType === undefined) { 10832 stateMgmtConsole.applicationWarn(AppStorageV2Impl.NULL_OR_UNDEFINED_KEY); 10833 return; 10834 } 10835 const key = this.getKeyOrTypeName(keyOrType); 10836 if (!this.isKeyValid(key)) { 10837 return; 10838 } 10839 this.removeFromMemory(key); 10840 } 10841 getMemoryKeys() { 10842 return Array.from(this.memorizedValues_.keys()); 10843 } 10844 removeFromMemory(key) { 10845 const isDeleted = this.memorizedValues_.delete(key); 10846 if (!isDeleted) { 10847 stateMgmtConsole.applicationWarn(AppStorageV2Impl.DELETE_NOT_EXIST_KEY); 10848 } 10849 else { 10850 this.oldTypeValues_.delete(key); 10851 } 10852 } 10853} 10854AppStorageV2Impl.instance_ = undefined; 10855class PersistenceV2Impl extends StorageHelper { 10856 constructor() { 10857 super(); 10858 this.cb_ = undefined; 10859 this.map_ = new Proxy(new Map(), new SetMapProxyHandler()); 10860 this.keysArr_ = new Set(); 10861 this.idToKey_ = new Map(); 10862 } 10863 static instance() { 10864 if (PersistenceV2Impl.instance_) { 10865 return PersistenceV2Impl.instance_; 10866 } 10867 PersistenceV2Impl.instance_ = new PersistenceV2Impl(); 10868 return PersistenceV2Impl.instance_; 10869 } 10870 static configureBackend(storage) { 10871 PersistenceV2Impl.storage_ = storage; 10872 } 10873 connect(type, keyOrDefaultCreator, defaultCreator) { 10874 this.checkTypeIsClassObject(type); 10875 const key = this.getRightKey(type, keyOrDefaultCreator); 10876 if (!this.isKeyValid(key)) { 10877 return undefined; 10878 } 10879 if (typeof keyOrDefaultCreator === 'function') { 10880 defaultCreator = keyOrDefaultCreator; 10881 } 10882 // In memory 10883 if (this.map_.has(key)) { 10884 const existedValue = this.map_.get(key); 10885 this.checkTypeByName(key, type, this.oldTypeValues_.get(key)); 10886 return existedValue; 10887 } 10888 const observedValue = this.getConnectDefaultValue(key, type, defaultCreator); 10889 if (!observedValue) { 10890 return undefined; 10891 } 10892 const id = ++PersistenceV2Impl.nextPersistId_; 10893 this.idToKey_.set(id, key); 10894 // Not in memory, but in disk 10895 if (PersistenceV2Impl.storage_.has(key)) { 10896 return this.getValueFromDisk(key, id, observedValue, type); 10897 } 10898 // Neither in memory or in disk 10899 return this.setValueToDisk(key, id, observedValue, type); 10900 } 10901 keys() { 10902 try { 10903 if (!this.keysArr_.size) { 10904 this.keysArr_ = this.getKeysArrFromStorage(); 10905 } 10906 } 10907 catch (err) { 10908 if (this.cb_ && typeof this.cb_ === 'function') { 10909 this.cb_('', "unknown" /* Unknown */, `fail to get all persisted keys`); 10910 return; 10911 } 10912 throw new Error(err); 10913 } 10914 return Array.from(this.keysArr_); 10915 } 10916 remove(keyOrType) { 10917 if (keyOrType === null || keyOrType === undefined) { 10918 stateMgmtConsole.applicationWarn(PersistenceV2Impl.NULL_OR_UNDEFINED_KEY); 10919 return; 10920 } 10921 this.checkTypeIsClassObject(keyOrType); 10922 const key = this.getKeyOrTypeName(keyOrType); 10923 if (!this.isKeyValid(key)) { 10924 return; 10925 } 10926 this.disconnectValue(key); 10927 } 10928 save(keyOrType) { 10929 if (keyOrType === null || keyOrType === undefined) { 10930 stateMgmtConsole.applicationWarn(PersistenceV2Impl.NULL_OR_UNDEFINED_KEY); 10931 return; 10932 } 10933 this.checkTypeIsClassObject(keyOrType); 10934 const key = this.getKeyOrTypeName(keyOrType); 10935 if (!this.isKeyValid(key)) { 10936 return; 10937 } 10938 if (!this.map_.has(key)) { 10939 stateMgmtConsole.applicationWarn(`Cannot save the key '${key}'! The key is disconnected`); 10940 return; 10941 } 10942 try { 10943 PersistenceV2Impl.storage_.set(key, JSONCoder.stringify(this.map_.get(key))); 10944 } 10945 catch (err) { 10946 this.errorHelper(key, "serialization" /* Serialization */, err); 10947 } 10948 } 10949 notifyOnError(cb) { 10950 this.cb_ = cb; 10951 } 10952 onChangeObserved(persistKeys) { 10953 this.writeAllChangedToFile(persistKeys); 10954 } 10955 connectNewValue(key, newValue, typeName) { 10956 this.map_.set(key, newValue); 10957 this.oldTypeValues_.set(key, typeName); 10958 this.storeKeyToPersistenceV2(key); 10959 } 10960 disconnectValue(key) { 10961 this.map_.delete(key); 10962 this.oldTypeValues_.delete(key); 10963 this.removeFromPersistenceV2(key); 10964 } 10965 checkTypeIsClassObject(keyOrType) { 10966 if ((typeof keyOrType !== 'string' && typeof keyOrType !== 'function') || 10967 PersistenceV2Impl.NOT_SUPPORT_TYPES_.includes(keyOrType)) { 10968 throw new Error(PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_); 10969 } 10970 } 10971 getRightKey(type, keyOrDefaultCreator) { 10972 const key = this.getConnectedKey(type, keyOrDefaultCreator); 10973 if (key === undefined) { 10974 throw new Error(PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_); 10975 } 10976 if (key === PersistenceV2Impl.KEYS_ARR_) { 10977 this.errorHelper(key, "quota" /* Quota */, `The key '${key}' cannot be used`); 10978 return undefined; 10979 } 10980 return key; 10981 } 10982 getConnectDefaultValue(key, type, defaultCreator) { 10983 if (!PersistenceV2Impl.storage_) { 10984 this.errorHelper(key, "unknown" /* Unknown */, `The storage is null`); 10985 return undefined; 10986 } 10987 if (typeof defaultCreator !== 'function') { 10988 throw new Error(PersistenceV2Impl.INVALID_DEFAULT_VALUE); 10989 } 10990 const observedValue = defaultCreator(); 10991 this.checkTypeByInstanceOf(key, type, observedValue); 10992 if (this.isNotClassObject(observedValue)) { 10993 throw new Error(PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_); 10994 } 10995 return observedValue; 10996 } 10997 getValueFromDisk(key, id, observedValue, type) { 10998 let newObservedValue; 10999 try { 11000 const json = PersistenceV2Impl.storage_.get(key); 11001 // Adding ref for persistence 11002 ObserveV2.getObserve().startRecordDependencies(this, id); 11003 newObservedValue = JSONCoder.parseTo(observedValue, json); 11004 ObserveV2.getObserve().stopRecordDependencies(); 11005 } 11006 catch (err) { 11007 this.errorHelper(key, "serialization" /* Serialization */, err); 11008 } 11009 this.checkTypeByInstanceOf(key, type, newObservedValue); 11010 this.connectNewValue(key, newObservedValue, this.getTypeName(type)); 11011 return newObservedValue; 11012 } 11013 setValueToDisk(key, id, observedValue, type) { 11014 ObserveV2.getObserve().startRecordDependencies(this, id); 11015 // Map is a proxy object. When it is connected for the first time, map.has is used to add dependencies, 11016 // and map.set is used to trigger writing to disk. 11017 const hasKey = this.map_.has(key); 11018 ObserveV2.getObserve().stopRecordDependencies(); 11019 // Writing to persistence by ref 11020 if (!hasKey) { 11021 this.connectNewValue(key, observedValue, this.getTypeName(type)); 11022 } 11023 return observedValue; 11024 } 11025 writeAllChangedToFile(persistKeys) { 11026 for (let i = 0; i < persistKeys.length; ++i) { 11027 const id = persistKeys[i]; 11028 const key = this.idToKey_.get(id); 11029 try { 11030 const hasKey = this.map_.has(key); 11031 if (hasKey) { 11032 const value = this.map_.get(key); 11033 ObserveV2.getObserve().startRecordDependencies(this, id); 11034 const json = JSONCoder.stringify(value); 11035 ObserveV2.getObserve().stopRecordDependencies(); 11036 if (this.isOverSizeLimit(json)) { 11037 stateMgmtConsole.applicationError(`Cannot store the key '${key}'! The length of data must be less than ${PersistenceV2Impl.MAX_DATA_LENGTH_}`); 11038 } 11039 else { 11040 PersistenceV2Impl.storage_.set(key, json); 11041 } 11042 } 11043 } 11044 catch (err) { 11045 if (this.cb_ && typeof this.cb_ === 'function') { 11046 this.cb_(key, "serialization" /* Serialization */, err); 11047 continue; 11048 } 11049 stateMgmtConsole.applicationError(`For '${key}' key: ` + err); 11050 } 11051 } 11052 } 11053 isOverSizeLimit(json) { 11054 if (typeof json !== 'string') { 11055 return false; 11056 } 11057 return json.length >= PersistenceV2Impl.MAX_DATA_LENGTH_; 11058 } 11059 isNotClassObject(value) { 11060 return Array.isArray(value) || this.isNotSupportType(value); 11061 } 11062 isNotSupportType(value) { 11063 for (let i = 0; i < PersistenceV2Impl.NOT_SUPPORT_TYPES_.length; ++i) { 11064 if (value instanceof PersistenceV2Impl.NOT_SUPPORT_TYPES_[i]) { 11065 return true; 11066 } 11067 } 11068 return false; 11069 } 11070 storeKeyToPersistenceV2(key) { 11071 try { 11072 if (this.keysArr_.has(key)) { 11073 return; 11074 } 11075 // Initializing the keys arr in memory 11076 if (!this.keysArr_.size) { 11077 this.keysArr_ = this.getKeysArrFromStorage(); 11078 } 11079 this.keysArr_.add(key); 11080 // Updating the keys arr in disk 11081 this.storeKeysArrToStorage(this.keysArr_); 11082 } 11083 catch (err) { 11084 this.errorHelper(key, "unknown" /* Unknown */, `fail to store the key '${key}'`); 11085 } 11086 } 11087 removeFromPersistenceV2(key) { 11088 try { 11089 if (!PersistenceV2Impl.storage_.has(key)) { 11090 stateMgmtConsole.applicationWarn(PersistenceV2Impl.DELETE_NOT_EXIST_KEY); 11091 return; 11092 } 11093 PersistenceV2Impl.storage_.delete(key); 11094 // The first call to remove 11095 if (!this.keysArr_.has(key)) { 11096 this.keysArr_ = this.getKeysArrFromStorage(); 11097 } 11098 this.keysArr_.delete(key); 11099 this.storeKeysArrToStorage(this.keysArr_); 11100 } 11101 catch (err) { 11102 this.errorHelper(key, "unknown" /* Unknown */, `fail to remove the key '${key}'`); 11103 } 11104 } 11105 getKeysArrFromStorage() { 11106 if (!PersistenceV2Impl.storage_.has(PersistenceV2Impl.KEYS_ARR_)) { 11107 return this.keysArr_; 11108 } 11109 const jsonKeysArr = PersistenceV2Impl.storage_.get(PersistenceV2Impl.KEYS_ARR_); 11110 return new Set(JSON.parse(jsonKeysArr)); 11111 } 11112 storeKeysArrToStorage(keysArr) { 11113 PersistenceV2Impl.storage_.set(PersistenceV2Impl.KEYS_ARR_, JSON.stringify(Array.from(keysArr))); 11114 } 11115 errorHelper(key, reason, message) { 11116 if (this.cb_ && typeof this.cb_ === 'function') { 11117 this.cb_(key, reason, message); 11118 return; 11119 } 11120 if (!key) { 11121 key = 'unknown'; 11122 } 11123 throw new Error(`For '${key}' key: ` + message); 11124 } 11125} 11126PersistenceV2Impl.MIN_PERSISTENCE_ID = 0x1010000000000; 11127PersistenceV2Impl.nextPersistId_ = PersistenceV2Impl.MIN_PERSISTENCE_ID; 11128PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_ = 'Not support! Can only use the class object in Persistence'; 11129PersistenceV2Impl.KEYS_ARR_ = '___keys_arr'; 11130PersistenceV2Impl.MAX_DATA_LENGTH_ = 8000; 11131PersistenceV2Impl.NOT_SUPPORT_TYPES_ = [Array, Set, Map, WeakSet, WeakMap, Date, Boolean, Number, String, Symbol, BigInt, RegExp, Function, Promise, ArrayBuffer]; 11132PersistenceV2Impl.instance_ = undefined; 11133/* 11134 * Copyright (c) 2024 Huawei Device Co., Ltd. 11135 * Licensed under the Apache License, Version 2.0 (the "License"); 11136 * you may not use this file except in compliance with the License. 11137 * You may obtain a copy of the License at 11138 * 11139 * http://www.apache.org/licenses/LICENSE-2.0 11140 * 11141 * Unless required by applicable law or agreed to in writing, software 11142 * distributed under the License is distributed on an "AS IS" BASIS, 11143 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11144 * See the License for the specific language governing permissions and 11145 * limitations under the License. 11146 */ 11147class UIUtilsImpl { 11148 getTarget(source) { 11149 if (!source || typeof source !== 'object') { 11150 return source; 11151 } 11152 if (ObservedObject.IsObservedObject(source)) { 11153 // V1 Proxy object 11154 return ObservedObject.GetRawObject(source); 11155 } 11156 else if (source[ObserveV2.SYMBOL_PROXY_GET_TARGET]) { 11157 // V2 Proxy object 11158 return source[ObserveV2.SYMBOL_PROXY_GET_TARGET]; 11159 } 11160 else { 11161 // other situation, not handle 11162 return source; 11163 } 11164 } 11165 makeObserved(target) { 11166 // mark makeObserved using V2 feature 11167 ConfigureStateMgmt.instance.usingV2ObservedTrack('makeObserved', 'use'); 11168 return RefInfo.get(target).proxy; 11169 } 11170 static instance() { 11171 if (UIUtilsImpl.instance_) { 11172 return UIUtilsImpl.instance_; 11173 } 11174 UIUtilsImpl.instance_ = new UIUtilsImpl(); 11175 return UIUtilsImpl.instance_; 11176 } 11177} 11178UIUtilsImpl.instance_ = undefined; 11179/* 11180 * Copyright (c) 2024 Huawei Device Co., Ltd. 11181 * Licensed under the Apache License, Version 2.0 (the "License"); 11182 * you may not use this file except in compliance with the License. 11183 * You may obtain a copy of the License at 11184 * 11185 * http://www.apache.org/licenses/LICENSE-2.0 11186 * 11187 * Unless required by applicable law or agreed to in writing, software 11188 * distributed under the License is distributed on an "AS IS" BASIS, 11189 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11190 * See the License for the specific language governing permissions and 11191 * limitations under the License. 11192 */ 11193class GestureStyle extends NativeGestureStyle { 11194 constructor(arg) { 11195 super(arg); 11196 this.arg_ = arg; 11197 } 11198} 11199/* 11200 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 11201 * Licensed under the Apache License, Version 2.0 (the "License"); 11202 * you may not use this file except in compliance with the License. 11203 * You may obtain a copy of the License at 11204 * 11205 * http://www.apache.org/licenses/LICENSE-2.0 11206 * 11207 * Unless required by applicable law or agreed to in writing, software 11208 * distributed under the License is distributed on an "AS IS" BASIS, 11209 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11210 * See the License for the specific language governing permissions and 11211 * limitations under the License. 11212 */ 11213 11214PersistenceV2Impl.configureBackend(new Storage()); 11215PersistentStorage.configureBackend(new Storage()); 11216Environment.configureBackend(new EnvironmentSetting()); 11217 11218 11219