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 new value 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) ? 4131 optComp === null || optComp === void 0 ? void 0 : optComp.deref() : undefined; 4132 } 4133 purgeVariableDependenciesOnElmtIdOwnFunc(elmtId) { 4134 // ViewPU overrides to unregister ViewPU from variables, 4135 // not in use in ViewV2 4136 } 4137 // overwritten by sub classes 4138 debugInfo__() { 4139 return `@Component '${this.constructor.name}'[${this.id__()}]`; 4140 } 4141 debugInfoRegisteredElmtIds() { 4142 return this.updateFuncByElmtId.debugInfoRegisteredElmtIds(); 4143 } 4144 // for given elmtIds look up their component name/type and format a string out of this info 4145 // use function only for debug output and DFX. 4146 debugInfoElmtIds(elmtIds) { 4147 let result = ''; 4148 let sepa = ''; 4149 elmtIds.forEach((elmtId) => { 4150 result += `${sepa}${this.debugInfoElmtId(elmtId)}`; 4151 sepa = ', '; 4152 }); 4153 return result; 4154 } 4155 debugInfoElmtId(elmtId, isProfiler = false) { 4156 return isProfiler ? { 4157 elementId: elmtId, 4158 elementTag: this.updateFuncByElmtId.get(elmtId).getComponentName(), 4159 isCustomNode: this.childrenWeakrefMap_.has(elmtId) 4160 } : this.updateFuncByElmtId.debugInfoElmtId(elmtId); 4161 } 4162 dumpStateVars() { 4163 4164 } 4165 isViewActive() { 4166 return this.isActive_; 4167 } 4168 dumpReport() { 4169 stateMgmtConsole.warn(`Printing profiler information`); 4170 stateMgmtProfiler.report(); 4171 } 4172 updateStateVarsOfChildByElmtId(elmtId, params) { 4173 4174 4175 if (elmtId < 0) { 4176 stateMgmtConsole.warn(`${this.debugInfo__()}: updateChildViewById(${elmtId}) - invalid elmtId - internal error!`); 4177 4178 return; 4179 } 4180 let iChild = this.getChildById(elmtId); 4181 if (!iChild || !((iChild instanceof ViewPU) || (iChild instanceof ViewV2))) { 4182 stateMgmtConsole.warn(`${this.debugInfo__()}: updateChildViewById(${elmtId}) - no child with this elmtId - internal error!`); 4183 4184 return; 4185 } 4186 const child = iChild; 4187 if ('updateStateVars' in child) { 4188 child.updateStateVars(params); 4189 } 4190 4191 4192 } 4193 // request list of all (global) elmtIds of deleted UINodes and unregister from the all ViewPUs/ViewV2 4194 // this function equals purgeDeletedElmtIdsRecursively because it does un-registration for all ViewPU/V2's 4195 purgeDeletedElmtIds() { 4196 4197 // request list of all (global) elmtIds of deleted UINodes that need to be unregistered 4198 UINodeRegisterProxy.obtainDeletedElmtIds(); 4199 // unregister the removed elmtIds requested from the cpp side for all ViewPUs/ViewV2, it will make the first ViewPUs/ViewV2 slower 4200 // than before, but the rest ViewPUs/ViewV2 will be faster 4201 UINodeRegisterProxy.unregisterElmtIdsFromIViews(); 4202 4203 } 4204 /** 4205 * force a complete rerender / update by executing all update functions 4206 * exec a regular rerender first 4207 * 4208 * @param deep recurse all children as well 4209 * 4210 * framework internal functions, apps must not call 4211 */ 4212 forceCompleteRerender(deep = false) { 4213 4214 4215 // see which elmtIds are managed by this View 4216 // and clean up all book keeping for them 4217 this.purgeDeletedElmtIds(); 4218 Array.from(this.updateFuncByElmtId.keys()).sort(ViewPU.compareNumber).forEach(elmtId => this.UpdateElement(elmtId)); 4219 if (!deep) { 4220 4221 4222 return; 4223 } 4224 for (const child of this.childrenWeakrefMap_.values()) { 4225 const childView = child.deref(); 4226 if (!childView) { 4227 continue; 4228 } 4229 if (child instanceof ViewPU) { 4230 if (!child.isRecycled()) { 4231 child.forceCompleteRerender(true); 4232 } 4233 else { 4234 child.delayCompleteRerender(deep); 4235 } 4236 } 4237 else { 4238 childView.forceCompleteRerender(true); 4239 } 4240 } 4241 4242 4243 } 4244 /** 4245 * force a complete rerender / update on specific node by executing update function. 4246 * 4247 * @param elmtId which node needs to update. 4248 * 4249 * framework internal functions, apps must not call 4250 */ 4251 forceRerenderNode(elmtId) { 4252 4253 // see which elmtIds are managed by this View 4254 // and clean up all book keeping for them 4255 this.purgeDeletedElmtIds(); 4256 this.UpdateElement(elmtId); 4257 // remove elemtId from dirtDescendantElementIds. 4258 this.dirtDescendantElementIds_.delete(elmtId); 4259 4260 } 4261 /** 4262 * for C++ to judge whether a CustomNode has updateFunc with specified nodeId. 4263 * use same judgement with UpdateElement, to make sure it can rerender if return true. 4264 * 4265 * @param elmtId query ID 4266 * 4267 * framework internal function 4268 */ 4269 hasNodeUpdateFunc(elmtId) { 4270 const entry = this.updateFuncByElmtId.get(elmtId); 4271 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 4272 // if this component does not have updateFunc for elmtId, return false. 4273 return typeof updateFunc === 'function'; 4274 } 4275 static pauseRendering() { 4276 PUV2ViewBase.renderingPaused = true; 4277 } 4278 static restoreRendering() { 4279 PUV2ViewBase.renderingPaused = false; 4280 } 4281 // performs the update on a branch within if() { branch } else if (..) { branch } else { branch } 4282 ifElseBranchUpdateFunction(branchId, branchfunc) { 4283 var _a, _b; 4284 const oldBranchid = If.getBranchId(); 4285 if (branchId === oldBranchid) { 4286 4287 return; 4288 } 4289 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onIfElseBranchUpdateEnter(); 4290 // branchid identifies uniquely the if .. <1> .. else if .<2>. else .<3>.branch 4291 // ifElseNode stores the most recent branch, so we can compare 4292 // removedChildElmtIds will be filled with the elmtIds of all children and their children will be deleted in response to if .. else change 4293 let removedChildElmtIds = new Array(); 4294 If.branchId(branchId, removedChildElmtIds); 4295 //un-registers the removed child elementIDs using proxy 4296 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 4297 // purging these elmtIds from state mgmt will make sure no more update function on any deleted child wi;ll be executed 4298 4299 this.purgeDeletedElmtIds(); 4300 branchfunc(); 4301 (_b = PUV2ViewBase.arkThemeScopeManager) === null || _b === void 0 ? void 0 : _b.onIfElseBranchUpdateExit(removedChildElmtIds); 4302 } 4303 /** 4304 Partial updates for ForEach. 4305 * @param elmtId ID of element. 4306 * @param itemArray Array of items for use of itemGenFunc. 4307 * @param itemGenFunc Item generation function to generate new elements. If index parameter is 4308 * given set itemGenFuncUsesIndex to true. 4309 * @param idGenFunc ID generation function to generate unique ID for each element. If index parameter is 4310 * given set idGenFuncUsesIndex to true. 4311 * @param itemGenFuncUsesIndex itemGenFunc optional index parameter is given or not. 4312 * @param idGenFuncUsesIndex idGenFunc optional index parameter is given or not. 4313 */ 4314 forEachUpdateFunction(elmtId, itemArray, itemGenFunc, idGenFunc, itemGenFuncUsesIndex = false, idGenFuncUsesIndex = false) { 4315 4316 4317 if (itemArray === null || itemArray === undefined) { 4318 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): input array is null or undefined error. Application error!`); 4319 4320 return; 4321 } 4322 if (typeof itemGenFunc !== 'function') { 4323 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): Item generation function missing. Application error!`); 4324 4325 return; 4326 } 4327 if (idGenFunc !== undefined && typeof idGenFunc !== 'function') { 4328 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): id generator is not a function. Application error!`); 4329 4330 return; 4331 } 4332 if (idGenFunc === undefined) { 4333 4334 idGenFuncUsesIndex = true; 4335 // catch possible error caused by Stringify and re-throw an Error with a meaningful (!) error message 4336 idGenFunc = (item, index) => { 4337 try { 4338 return `${index}__${JSON.stringify(item)}`; 4339 } 4340 catch (e) { 4341 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!`); 4342 } 4343 }; 4344 } 4345 let diffIndexArray = []; // New indexes compared to old one. 4346 let newIdArray = []; 4347 let idDuplicates = []; 4348 const arr = itemArray; // just to trigger a 'get' onto the array 4349 // ID gen is with index. 4350 if (idGenFuncUsesIndex || idGenFunc.length > 1) { 4351 // Create array of new ids. 4352 arr.forEach((item, indx) => { 4353 newIdArray.push(idGenFunc(item, indx)); 4354 }); 4355 } 4356 else { 4357 // Create array of new ids. 4358 arr.forEach((item, index) => { 4359 newIdArray.push(`${itemGenFuncUsesIndex ? index + '_' : ''}` + idGenFunc(item)); 4360 }); 4361 } 4362 // removedChildElmtIds will be filled with the elmtIds of all children and their children will be deleted in response to foreach change 4363 let removedChildElmtIds = []; 4364 // Set new array on C++ side. 4365 // C++ returns array of indexes of newly added array items. 4366 // these are indexes in new child list. 4367 ForEach.setIdArray(elmtId, newIdArray, diffIndexArray, idDuplicates, removedChildElmtIds); 4368 // Its error if there are duplicate IDs. 4369 if (idDuplicates.length > 0) { 4370 idDuplicates.forEach((indx) => { 4371 stateMgmtConsole.error(`Error: ForEach id generated for ${indx}${indx < 4 ? indx === 2 ? 'nd' : 'rd' : 'th'} array item is duplicated.`); 4372 }); 4373 stateMgmtConsole.applicationError(`${this.debugInfo__()}: Ids generated by the ForEach id gen function must be unique. Application error!`); 4374 } 4375 4376 // Item gen is with index. 4377 4378 // Create new elements if any. 4379 4380 diffIndexArray.forEach((indx) => { 4381 ForEach.createNewChildStart(newIdArray[indx], this); 4382 if (itemGenFuncUsesIndex) { 4383 itemGenFunc(arr[indx], indx); 4384 } 4385 else { 4386 itemGenFunc(arr[indx]); 4387 } 4388 ForEach.createNewChildFinish(newIdArray[indx], this); 4389 }); 4390 // un-registers the removed child elementIDs using proxy 4391 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 4392 // purging these elmtIds from state mgmt will make sure no more update function on any deleted child will be executed 4393 4394 this.purgeDeletedElmtIds(); 4395 4396 4397 4398 } 4399 createOrGetNode(elmtId, builder) { 4400 const entry = this.updateFuncByElmtId.get(elmtId); 4401 if (entry === undefined) { 4402 throw new Error(`${this.debugInfo__()} fail to create node, elmtId is illegal`); 4403 } 4404 let nodeInfo = entry.getNode(); 4405 if (nodeInfo === undefined) { 4406 nodeInfo = builder(); 4407 entry.setNode(nodeInfo); 4408 } 4409 return nodeInfo; 4410 } 4411 /** 4412 * getNodeById is used to get ArkComponent stored updateFuncByElmtId 4413 * @param elmtId - the id of the component 4414 * @returns ArkComponent | undefined 4415 */ 4416 getNodeById(elmtId) { 4417 const entry = this.updateFuncByElmtId.get(elmtId); 4418 return entry ? entry.getNode() : undefined; 4419 } 4420 /** 4421 * return its elmtId if currently rendering or re-rendering an UINode 4422 * otherwise return UINodeRegisterProxy.notRecordingDependencies 4423 * set in observeComponentCreation(2) 4424 */ 4425 getCurrentlyRenderedElmtId() { 4426 return PUV2ViewBase.renderingPaused || this.currentlyRenderedElmtIdStack_.length === 0 4427 ? UINodeRegisterProxy.notRecordingDependencies 4428 : this.currentlyRenderedElmtIdStack_[this.currentlyRenderedElmtIdStack_.length - 1]; 4429 } 4430 debugInfoViewHierarchy(recursive = false) { 4431 return this.debugInfoViewHierarchyInternal(0, recursive); 4432 } 4433 debugInfoViewHierarchyInternal(depth = 0, recursive = false) { 4434 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]`; 4435 if (this.isCompFreezeAllowed()) { 4436 retVaL += ` {freezeWhenInactive : ${this.isCompFreezeAllowed()}}`; 4437 } 4438 if (depth < 1 || recursive) { 4439 this.childrenWeakrefMap_.forEach((weakChild) => { 4440 var _a; 4441 retVaL += (_a = weakChild.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoViewHierarchyInternal(depth + 1, recursive); 4442 }); 4443 } 4444 return retVaL; 4445 } 4446 debugInfoUpdateFuncByElmtId(recursive = false) { 4447 return this.debugInfoUpdateFuncByElmtIdInternal({ total: 0 }, 0, recursive); 4448 } 4449 debugInfoUpdateFuncByElmtIdInternal(counter, depth = 0, recursive = false) { 4450 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`; 4451 this.updateFuncByElmtId.forEach((value, key, map) => { 4452 retVaL += `\n${' '.repeat(depth + 2)}${value.getComponentName()}[${key}]`; 4453 }); 4454 counter.total += this.updateFuncByElmtId.size; 4455 retVaL += `\n${' '.repeat(depth + 1)}}[${this.updateFuncByElmtId.size}]`; 4456 if (recursive) { 4457 this.childrenWeakrefMap_.forEach((value, key, map) => { 4458 var _a; 4459 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoUpdateFuncByElmtIdInternal(counter, depth + 1, recursive); 4460 }); 4461 } 4462 if (recursive && depth === 0) { 4463 retVaL += `\nTotal: ${counter.total}`; 4464 } 4465 return retVaL; 4466 } 4467 debugInfoInactiveComponents() { 4468 return Array.from(PUV2ViewBase.inactiveComponents_) 4469 .map((component) => `- ${component}`).join('\n'); 4470 } 4471 onGlobalThemeChanged() { 4472 } 4473 static setArkThemeScopeManager(mgr) { 4474 PUV2ViewBase.arkThemeScopeManager = mgr; 4475 } 4476} // class PUV2ViewBase 4477// List of inactive components used for Dfx 4478PUV2ViewBase.inactiveComponents_ = new Set(); 4479// Array.sort() converts array items to string to compare them! 4480PUV2ViewBase.compareNumber = (a, b) => { 4481 return (a < b) ? -1 : (a > b) ? 1 : 0; 4482}; 4483// static flag for paused rendering 4484// when paused, getCurrentlyRenderedElmtId() will return UINodeRegisterProxy.notRecordingDependencies 4485PUV2ViewBase.renderingPaused = false; 4486PUV2ViewBase.arkThemeScopeManager = undefined; 4487/* 4488 * Copyright (c) 2024 Huawei Device Co., Ltd. 4489 * Licensed under the Apache License, Version 2.0 (the "License"); 4490 * you may not use this file except in compliance with the License. 4491 * You may obtain a copy of the License at 4492 * 4493 * http://www.apache.org/licenses/LICENSE-2.0 4494 * 4495 * Unless required by applicable law or agreed to in writing, software 4496 * distributed under the License is distributed on an "AS IS" BASIS, 4497 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4498 * See the License for the specific language governing permissions and 4499 * limitations under the License. 4500 */ 4501; 4502; 4503; 4504class SendableType { 4505 static isArray(o) { 4506 return o instanceof SendableArray; 4507 } 4508 static isSet(o) { 4509 return o instanceof SendableSet; 4510 } 4511 static isMap(o) { 4512 return o instanceof SendableMap; 4513 } 4514 static isContainer(o) { 4515 return o instanceof SendableMap || o instanceof SendableSet || o instanceof SendableArray; 4516 } 4517} 4518/* 4519 * Copyright (c) 2022 Huawei Device Co., Ltd. 4520 * Licensed under the Apache License, Version 2.0 (the "License"); 4521 * you may not use this file except in compliance with the License. 4522 * You may obtain a copy of the License at 4523 * 4524 * http://www.apache.org/licenses/LICENSE-2.0 4525 * 4526 * Unless required by applicable law or agreed to in writing, software 4527 * distributed under the License is distributed on an "AS IS" BASIS, 4528 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4529 * See the License for the specific language governing permissions and 4530 * limitations under the License. 4531 */ 4532/* 4533 * Copyright (c) 2023 Huawei Device Co., Ltd. 4534 * Licensed under the Apache License, Version 2.0 (the "License"); 4535 * you may not use this file except in compliance with the License. 4536 * You may obtain a copy of the License at 4537 * 4538 * http://www.apache.org/licenses/LICENSE-2.0 4539 * 4540 * Unless required by applicable law or agreed to in writing, software 4541 * distributed under the License is distributed on an "AS IS" BASIS, 4542 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4543 * See the License for the specific language governing permissions and 4544 * limitations under the License. 4545 */ 4546// @Track class property decorator 4547// indicates to framework to track individual object property value changes 4548function Track(target, property) { 4549 var _a; 4550 ConfigureStateMgmt.instance.usingPUObservedTrack(`@Track`, property); 4551 Reflect.set(target, `${TrackedObject.___TRACKED_PREFIX}${property}`, true); 4552 Reflect.set(target, TrackedObject.___IS_TRACKED_OPTIMISED, true); 4553 4554} 4555class TrackedObject { 4556 static isCompatibilityMode(obj) { 4557 return !obj || (typeof obj !== 'object') || !Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED); 4558 } 4559 static needsPropertyReadCb(obj) { 4560 return obj && (typeof obj === 'object') && Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED); 4561 } 4562 /** 4563 * @Track new object assignment optimization 4564 * can apply if old and new value are object, instance of same class, do not use compat mode. 4565 * in this case function returns true and calls supplied notifyTrackedPropertyChanged cb function 4566 * for each @Tracked'ed property whose value actually changed. 4567 * if optimisation can not be applied calls notifyPropertyChanged and returns false 4568 */ 4569 static notifyObjectValueAssignment(obj1, obj2, notifyPropertyChanged, // notify as assignment (none-optimised) 4570 notifyTrackedPropertyChange, obSelf) { 4571 if (!obj1 || !obj2 || (typeof obj1 !== 'object') || (typeof obj2 !== 'object') || 4572 (obj1.constructor !== obj2.constructor) || 4573 TrackedObject.isCompatibilityMode(obj1)) { 4574 4575 notifyPropertyChanged.call(obSelf); 4576 return false; 4577 } 4578 4579 const obj1Raw = ObservedObject.GetRawObject(obj1); 4580 const obj2Raw = ObservedObject.GetRawObject(obj2); 4581 let shouldFakePropPropertyBeNotified = false; 4582 Object.keys(obj2Raw) 4583 .forEach(propName => { 4584 // Collect only @Track'ed changed properties 4585 if (Reflect.has(obj1Raw, `${TrackedObject.___TRACKED_PREFIX}${propName}`) && 4586 (Reflect.get(obj1Raw, propName) !== Reflect.get(obj2Raw, propName))) { 4587 4588 notifyTrackedPropertyChange.call(obSelf, propName); 4589 shouldFakePropPropertyBeNotified = true; 4590 } 4591 else { 4592 4593 } 4594 }); 4595 // notify this non-existing object property has changed only if some of the tracked properties changed. 4596 // SynchedPropertyOneWay reset() report a 'read' on this property, thereby creating a dependency 4597 // reporting the property as changed causes @Prop sync from source 4598 if (shouldFakePropPropertyBeNotified) { 4599 4600 notifyTrackedPropertyChange.call(obSelf, TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY); 4601 } 4602 // always notify this non-existing object property has changed for SynchedPropertyNestedObject as 4603 // the object has changed in assigment. 4604 // SynchedPropertyNestedObject set() reports a 'read' on this property, thereby creating a dependency 4605 // reporting the property as changed causes @ObjectLink sync from source 4606 4607 notifyTrackedPropertyChange.call(obSelf, TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY); 4608 return true; 4609 } 4610} 4611TrackedObject.___IS_TRACKED_OPTIMISED = `___IS_TRACKED_OPTIMISED`; 4612TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_PROP_PROPERTY`; 4613TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_OBJLINK_PROPERTY`; 4614TrackedObject.___TRACKED_PREFIX = `___TRACKED_`; 4615TrackedObject.___TRACKED_PREFIX_LEN = TrackedObject.___TRACKED_PREFIX.length; 4616/* 4617 * Copyright (c) 2022 Huawei Device Co., Ltd. 4618 * Licensed under the Apache License, Version 2.0 (the "License"); 4619 * you may not use this file except in compliance with the License. 4620 * You may obtain a copy of the License at 4621 * 4622 * http://www.apache.org/licenses/LICENSE-2.0 4623 * 4624 * Unless required by applicable law or agreed to in writing, software 4625 * distributed under the License is distributed on an "AS IS" BASIS, 4626 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4627 * See the License for the specific language governing permissions and 4628 * limitations under the License. 4629 */ 4630var _a; 4631/** 4632 * ObservedPropertyAbstractPU aka ObservedPropertyAbstract for partial update 4633 * 4634 * all definitions in this file are framework internal 4635 */ 4636class ObservedPropertyAbstractPU extends ObservedPropertyAbstract { 4637 constructor(subscriber, viewName) { 4638 super(subscriber, viewName); 4639 this.changeNotificationIsOngoing_ = false; 4640 // when owning ViewPU is inActive, delay notifying changes 4641 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay; 4642 // install when current value is ObservedObject and the value type is not using compatibility mode 4643 // note value may change for union type variables when switching an object from one class to another. 4644 this.shouldInstallTrackedObjectReadCb = false; 4645 this.dependentElmtIdsByProperty_ = new PropertyDependencies(); 4646 Object.defineProperty(this, 'owningView_', { writable: true, enumerable: false, value: undefined }); 4647 Object.defineProperty(this, 'subscriberRefs_', { writable: true, enumerable: false, value: new Set() }); 4648 if (subscriber) { 4649 if (subscriber instanceof ViewPU) { 4650 this.owningView_ = subscriber; 4651 } 4652 else { 4653 this.subscriberRefs_.add(subscriber); 4654 } 4655 } 4656 } 4657 aboutToBeDeleted() { 4658 super.aboutToBeDeleted(); 4659 this.subscriberRefs_.clear(); 4660 this.owningView_ = undefined; 4661 } 4662 setDecoratorInfo(decorate) { 4663 this.decoratorInfo_ = decorate; 4664 } 4665 // dump info about variable decorator to string 4666 // e.g. @State, @Link, etc. 4667 debugInfoDecorator() { 4668 return this.decoratorInfo_; 4669 } 4670 // dump basic info about this variable to a string, non-recursive, no subscriber info 4671 debugInfo() { 4672 const propSource = this.isPropSourceObservedPropertyFakeName(); 4673 return (propSource) 4674 ? `internal source (ObservedPropertyPU) of @Prop ${propSource} [${this.id__()}]` 4675 : `${this.debugInfoDecorator()} '${this.info()}'[${this.id__()}] <${this.debugInfoOwningView()}>`; 4676 } 4677 debugInfoOwningView() { 4678 return `${this.owningView_ ? this.owningView_.debugInfo__() : 'owning @Component UNKNOWN'}`; 4679 } 4680 // dump info about owning view and subscribers (PU ones only) 4681 // use function only for debug output and DFX. 4682 debugInfoSubscribers() { 4683 return (this.owningView_) 4684 ? `|--Owned by ${this.debugInfoOwningView()} ` 4685 : `|--Owned by: owning view not known`; 4686 } 4687 debugInfoSyncPeers() { 4688 if (!this.subscriberRefs_.size) { 4689 return '|--Sync peers: none'; 4690 } 4691 let result = `|--Sync peers: {`; 4692 let sepa = ''; 4693 this.subscriberRefs_.forEach((subscriber) => { 4694 if ('debugInfo' in subscriber) { 4695 result += `\n ${sepa}${subscriber.debugInfo()}`; 4696 sepa = ', '; 4697 } 4698 }); 4699 result += '\n }'; 4700 return result; 4701 } 4702 debugInfoDependentElmtIds(dumpDependantElements = false) { 4703 return this.dependentElmtIdsByProperty_.dumpInfoDependencies(this.owningView_, dumpDependantElements); 4704 } 4705 dumpDependentElmtIdsObj(isTrackedMode, isProfiler) { 4706 return this.dependentElmtIdsByProperty_.dumpInfoDependenciesObj(this.owningView_, isTrackedMode, isProfiler); 4707 } 4708 debugInfoElmtId(elmtId) { 4709 if (this.owningView_) { 4710 return this.owningView_.debugInfoElmtId(elmtId); 4711 } 4712 return '<unknown element id ' + elmtId + ', missing owning view>'; 4713 } 4714 debugInfoDependentComponents() { 4715 let result = `|--Dependent elements: `; 4716 let sepa = '; '; 4717 let sepaDiff = ''; 4718 const dumpDependantElements = true; 4719 let queue = [this]; 4720 let seen = new Set(); 4721 while (queue.length) { 4722 let item = queue.shift(); 4723 seen.add(item); 4724 if (item !== this) { 4725 result += `${sepa}${item.debugInfoOwningView()}`; 4726 } 4727 result += `${sepaDiff}${item.debugInfoDependentElmtIds(dumpDependantElements)}`; // new dependent elements 4728 sepaDiff = ', '; 4729 item.subscriberRefs_.forEach((subscriber) => { 4730 if ((subscriber instanceof ObservedPropertyAbstractPU)) { 4731 if (!seen.has(subscriber)) { 4732 queue.push(subscriber); 4733 } 4734 } 4735 }); 4736 } 4737 return result; 4738 } 4739 /**/ 4740 hasDependencies() { 4741 return this.dependentElmtIdsByProperty_.hasDependencies(); 4742 } 4743 /* for @Prop value from source we need to generate a @State 4744 that observes when this value changes. This ObservedPropertyPU 4745 sits inside SynchedPropertyOneWayPU. 4746 below methods invent a fake variable name for it 4747 */ 4748 getPropSourceObservedPropertyFakeName() { 4749 return `${this.info()}_prop_fake_state_source___`; 4750 } 4751 isPropSourceObservedPropertyFakeName() { 4752 return this.info() && this.info().endsWith('_prop_fake_state_source___') 4753 ? this.info().substring(0, this.info().length - '_prop_fake_state_source___'.length) 4754 : false; 4755 } 4756 getOwningView() { 4757 var _a, _b; 4758 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__() }; 4759 } 4760 dumpSyncPeers(isProfiler, changedTrackPropertyName) { 4761 let res = []; 4762 this.subscriberRefs_.forEach((subscriber) => { 4763 if ('debugInfo' in subscriber) { 4764 const observedProp = subscriber; 4765 res.push(stateMgmtDFX.getObservedPropertyInfo(observedProp, isProfiler, changedTrackPropertyName)); 4766 } 4767 }); 4768 return res; 4769 } 4770 onDumpProfiler(changedTrackPropertyName) { 4771 var _a, _b; 4772 let res = new DumpInfo(); 4773 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__() }; 4774 res.observedPropertiesInfo.push(stateMgmtDFX.getObservedPropertyInfo(this, true, changedTrackPropertyName)); 4775 if (this.owningView_) { 4776 try { 4777 this.owningView_.sendStateInfo(JSON.stringify(res)); 4778 } 4779 catch (error) { 4780 stateMgmtConsole.applicationError(`${this.debugInfo()} has error in sendStateInfo: ${error.message}`); 4781 } 4782 } 4783 } 4784 /* 4785 Virtualized version of the subscription mechanism - add subscriber 4786 Overrides implementation in ObservedPropertyAbstract<T> 4787 */ 4788 addSubscriber(subscriber) { 4789 if (subscriber) { 4790 // ObservedPropertyAbstract will also add subscriber to 4791 // SubscriberManager map and to its own Set of subscribers as well 4792 // Something to improve in the future for PU path. 4793 // subscribeMe should accept IPropertySubscriber interface 4794 super.subscribeMe(subscriber); 4795 this.subscriberRefs_.add(subscriber); 4796 } 4797 } 4798 /* 4799 Virtualized version of the subscription mechanism - remove subscriber 4800 Overrides implementation in ObservedPropertyAbstract<T> 4801 */ 4802 removeSubscriber(subscriber, id) { 4803 if (subscriber) { 4804 this.subscriberRefs_.delete(subscriber); 4805 if (!id) { 4806 id = subscriber.id__(); 4807 } 4808 } 4809 super.unlinkSuscriber(id); 4810 } 4811 /** 4812 * put the property to delayed notification mode 4813 * feature is only used for @StorageLink/Prop, @LocalStorageLink/Prop 4814 */ 4815 enableDelayedNotification() { 4816 if (this.delayedNotification_ !== ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending) { 4817 4818 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_none_pending; 4819 } 4820 } 4821 /* 4822 when moving from inActive to active state the owning ViewPU calls this function 4823 This solution is faster than ViewPU polling each variable to send back a viewPropertyHasChanged event 4824 with the elmtIds 4825 4826 returns undefined if variable has _not_ changed 4827 returns dependentElementIds_ Set if changed. This Set is empty if variable is not used to construct the UI 4828 */ 4829 moveElmtIdsForDelayedUpdate(isReused = false) { 4830 const result = (this.delayedNotification_ === ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending) ? 4831 this.dependentElmtIdsByProperty_.getAllPropertyDependencies() : 4832 undefined; 4833 4834 if (isReused && !this.owningView_.isViewActive()) { 4835 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_none_pending; 4836 } 4837 else { 4838 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay; 4839 } 4840 return result; 4841 } 4842 notifyPropertyRead() { 4843 stateMgmtConsole.error(`${this.debugInfo()}: notifyPropertyRead, DO NOT USE with PU. Use notifyReadCb mechanism.`); 4844 } 4845 // notify owning ViewPU and peers of a variable assignment 4846 // also property/item changes to ObservedObjects of class object type, which use compat mode 4847 // Date and Array are notified as if there had been an assignment. 4848 notifyPropertyHasChangedPU() { 4849 4850 4851 if (this.owningView_) { 4852 if (this.delayedNotification_ === ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay) { 4853 // send viewPropertyHasChanged right away 4854 this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElmtIdsByProperty_.getAllPropertyDependencies()); 4855 // send changed observed property to profiler 4856 // only will be true when enable profiler 4857 if (stateMgmtDFX.enableProfiler) { 4858 4859 this.onDumpProfiler(); 4860 } 4861 } 4862 else { 4863 // mark this @StorageLink/Prop or @LocalStorageLink/Prop variable has having changed and notification of viewPropertyHasChanged delivery pending 4864 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending; 4865 } 4866 } 4867 this.subscriberRefs_.forEach((subscriber) => { 4868 if (subscriber && typeof subscriber === 'object' && 'syncPeerHasChanged' in subscriber) { 4869 subscriber.syncPeerHasChanged(this); 4870 } 4871 else { 4872 stateMgmtConsole.warn(`${this.debugInfo()}: notifyPropertyHasChangedPU: unknown subscriber ID 'subscribedId' error!`); 4873 } 4874 }); 4875 4876 } 4877 // notify owning ViewPU and peers of a ObservedObject @Track property's assignment 4878 notifyTrackedObjectPropertyHasChanged(changedPropertyName) { 4879 4880 4881 if (this.owningView_) { 4882 if (this.delayedNotification_ == ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay) { 4883 // send viewPropertyHasChanged right away 4884 this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElmtIdsByProperty_.getTrackedObjectPropertyDependencies(changedPropertyName, 'notifyTrackedObjectPropertyHasChanged')); 4885 // send changed observed property to profiler 4886 // only will be true when enable profiler 4887 if (stateMgmtDFX.enableProfiler) { 4888 4889 this.onDumpProfiler(changedPropertyName); 4890 } 4891 } 4892 else { 4893 // mark this @StorageLink/Prop or @LocalStorageLink/Prop variable has having changed and notification of viewPropertyHasChanged delivery pending 4894 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending; 4895 } 4896 } 4897 this.subscriberRefs_.forEach((subscriber) => { 4898 if (subscriber) { 4899 if ('syncPeerTrackedPropertyHasChanged' in subscriber) { 4900 subscriber.syncPeerTrackedPropertyHasChanged(this, changedPropertyName); 4901 } 4902 else { 4903 stateMgmtConsole.warn(`${this.debugInfo()}: notifyTrackedObjectPropertyHasChanged: unknown subscriber ID 'subscribedId' error!`); 4904 } 4905 } 4906 }); 4907 4908 } 4909 markDependentElementsDirty(view) { 4910 // TODO ace-ets2bundle, framework, complicated apps need to update together 4911 // this function will be removed after a short transition period. 4912 stateMgmtConsole.warn(`${this.debugInfo()}: markDependentElementsDirty no longer supported. App will work ok, but 4913 please update your ace-ets2bundle and recompile your application!`); 4914 } 4915 numberOfSubscrbers() { 4916 return this.subscriberRefs_.size + (this.owningView_ ? 1 : 0); 4917 } 4918 /* 4919 type checking for any supported type, as required for union type support 4920 see 1st parameter for explanation what is allowed 4921 4922 FIXME this expects the Map, Set patch to go in 4923 */ 4924 checkIsSupportedValue(value) { 4925 // FIXME enable the check when V1-V2 interoperability is forbidden 4926 // && !ObserveV2.IsProxiedObservedV2(value)) 4927 let res = ((typeof value === 'object' && typeof value !== 'function' && 4928 !ObserveV2.IsObservedObjectV2(value) && 4929 !ObserveV2.IsMakeObserved(value)) || 4930 typeof value === 'number' || 4931 typeof value === 'string' || 4932 typeof value === 'boolean' || 4933 value === undefined || 4934 value === null); 4935 if (!res) { 4936 errorReport.varValueCheckFailed({ 4937 customComponent: this.debugInfoOwningView(), 4938 variableDeco: this.debugInfoDecorator(), 4939 variableName: this.info(), 4940 expectedType: `undefined, null, number, boolean, string, or Object but not function, not V2 @ObservedV2 / @Trace class, and makeObserved return value either`, 4941 value: value 4942 }); 4943 } 4944 return res; 4945 } 4946 /* 4947 type checking for allowed Object type value 4948 see 1st parameter for explanation what is allowed 4949 4950 FIXME this expects the Map, Set patch to go in 4951 */ 4952 checkIsObject(value) { 4953 let res = ((typeof value === 'object' && typeof value !== 'function' && !ObserveV2.IsObservedObjectV2(value)) || 4954 value === undefined || value === null); 4955 if (!res) { 4956 errorReport.varValueCheckFailed({ 4957 customComponent: this.debugInfoOwningView(), 4958 variableDeco: this.debugInfoDecorator(), 4959 variableName: this.info(), 4960 expectedType: `undefined, null, Object including Array and instance of SubscribableAbstract and excluding function and V3 @observed/@track object`, 4961 value: value 4962 }); 4963 } 4964 return res; 4965 } 4966 /* 4967 type checking for allowed simple types value 4968 see 1st parameter for explanation what is allowed 4969 */ 4970 checkIsSimple(value) { 4971 let res = (value === undefined || typeof value === 'number' || typeof value === 'string' || typeof value === 'boolean'); 4972 if (!res) { 4973 errorReport.varValueCheckFailed({ 4974 customComponent: this.debugInfoOwningView(), 4975 variableDeco: this.debugInfoDecorator(), 4976 variableName: this.info(), 4977 expectedType: `undefined, number, boolean, string`, 4978 value: value 4979 }); 4980 } 4981 return res; 4982 } 4983 checkNewValue(isAllowedComment, newValue, validator) { 4984 if (validator(newValue)) { 4985 return true; 4986 } 4987 // report error 4988 // current implementation throws an Exception 4989 errorReport.varValueCheckFailed({ 4990 customComponent: this.debugInfoOwningView(), 4991 variableDeco: this.debugInfoDecorator(), 4992 variableName: this.info(), 4993 expectedType: isAllowedComment, 4994 value: newValue 4995 }); 4996 // never gets here if errorReport.varValueCheckFailed throws an exception 4997 // but should not depend on its implementation 4998 return false; 4999 } 5000 /** 5001 * factory function for concrete 'object' or 'simple' ObservedProperty object 5002 * depending if value is Class object 5003 * or simple type (boolean | number | string) 5004 * @param value 5005 * @param owningView 5006 * @param thisPropertyName 5007 * @returns either 5008 */ 5009 static CreateObservedObject(value, owningView, thisPropertyName) { 5010 return (typeof value === 'object') ? 5011 new ObservedPropertyObject(value, owningView, thisPropertyName) 5012 : new ObservedPropertySimple(value, owningView, thisPropertyName); 5013 } 5014 /** 5015 * If owning viewPU is currently rendering or re-rendering a UINode, return its elmtId 5016 * return notRecordingDependencies (-1) otherwise 5017 * ViewPU caches the info, it does not request the info from C++ side (by calling 5018 * ViewStackProcessor.GetElmtIdToAccountFor(); as done in earlier implementation 5019 */ 5020 getRenderingElmtId() { 5021 return (this.owningView_) ? this.owningView_.getCurrentlyRenderedElmtId() : UINodeRegisterProxy.notRecordingDependencies; 5022 } 5023 /** 5024 * during 'get' access recording take note of the created component and its elmtId 5025 * and add this component to the list of components who are dependent on this property 5026 */ 5027 recordPropertyDependentUpdate() { 5028 const elmtId = this.getRenderingElmtId(); 5029 if (elmtId === UINodeRegisterProxy.notRecordingDependencies) { 5030 // not access recording 5031 return; 5032 } 5033 if (elmtId === UINodeRegisterProxy.monitorIllegalV2V3StateAccess) { 5034 const error = `${this.debugInfo()}: recordPropertyDependentUpdate trying to use V2 state to init/update child V3 @Component. Application error`; 5035 stateMgmtConsole.applicationError(error); 5036 throw new TypeError(error); 5037 } 5038 5039 this.dependentElmtIdsByProperty_.addPropertyDependency(elmtId); 5040 } 5041 /** record dependency ObservedObject + propertyName -> elmtId 5042 * caller ensures renderingElmtId >= 0 5043 */ 5044 recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readTrackedPropertyName) { 5045 5046 this.dependentElmtIdsByProperty_.addTrackedObjectPropertyDependency(readTrackedPropertyName, renderingElmtId); 5047 } 5048 purgeDependencyOnElmtId(rmElmtId) { 5049 var _a; 5050 (_a = this.dependentElmtIdsByProperty_) === null || _a === void 0 ? void 0 : _a.purgeDependenciesForElmtId(rmElmtId); 5051 } 5052 SetPropertyUnchanged() { 5053 // function to be removed 5054 // keep it here until transpiler is updated. 5055 } 5056 // unified Appstorage, what classes to use, and the API 5057 createLink(subscribeOwner, linkPropName) { 5058 throw new Error(`${this.debugInfo()}: createLink: Can not create a AppStorage 'Link' from this property.`); 5059 } 5060 createProp(subscribeOwner, linkPropName) { 5061 throw new Error(`${this.debugInfo()}: createProp: Can not create a AppStorage 'Prop' from a @State property. `); 5062 } 5063 /* 5064 Below empty functions required to keep as long as this class derives from FU version 5065 ObservedPropertyAbstract. Need to overwrite these functions to do nothing for PU 5066 */ 5067 notifyHasChanged(_) { 5068 stateMgmtConsole.error(`${this.debugInfo()}: notifyHasChanged, DO NOT USE with PU. Use syncPeerHasChanged() \ 5069 or onTrackedObjectProperty(CompatMode)HasChangedPU()`); 5070 } 5071 /** 5072 * event emitted by wrapped ObservedObject, when one of its property values changes 5073 * for class objects when in compatibility mode 5074 * for Array, Date instances always 5075 * @param souceObject 5076 * @param changedPropertyName 5077 */ 5078 onTrackedObjectPropertyHasChangedPU(sourceObject, changedPropertyName) { 5079 5080 this.notifyTrackedObjectPropertyHasChanged(changedPropertyName); 5081 } 5082 /** 5083 * event emitted by wrapped ObservedObject, when one of its property values changes 5084 * for class objects when in compatibility mode 5085 * for Array, Date instances always 5086 * @param souceObject 5087 * @param changedPropertyName 5088 */ 5089 onTrackedObjectPropertyCompatModeHasChangedPU(sourceObject, changedPropertyName) { 5090 5091 this.notifyPropertyHasChangedPU(); 5092 } 5093 hasChanged(_) { 5094 // unused for PU 5095 // need to overwrite impl of base class with empty function. 5096 } 5097 propertyHasChanged(_) { 5098 // unused for PU 5099 // need to overwrite impl of base class with empty function. 5100 } 5101 propertyRead(_) { 5102 // unused for PU 5103 // need to overwrite impl of base class with empty function. 5104 } 5105} 5106ObservedPropertyAbstractPU.DelayedNotifyChangesEnum = (_a = class { 5107 }, 5108 _a.do_not_delay = 0, 5109 _a.delay_none_pending = 1, 5110 _a.delay_notification_pending = 2, 5111 _a); 5112class PropertyDependencies { 5113 constructor() { 5114 // dependencies for property -> elmtId 5115 // variable read during render adds elmtId 5116 // variable assignment causes elmtId to need re-render. 5117 // UINode with elmtId deletion needs elmtId to be removed from all records, see purgeDependenciesForElmtId 5118 this.propertyDependencies_ = new Set(); 5119 // dependencies on individual object properties 5120 this.trackedObjectPropertyDependencies_ = new Map(); 5121 } 5122 getAllPropertyDependencies() { 5123 5124 return this.propertyDependencies_; 5125 } 5126 addPropertyDependency(elmtId) { 5127 this.propertyDependencies_.add(elmtId); 5128 5129 } 5130 purgeDependenciesForElmtId(rmElmtId) { 5131 5132 this.propertyDependencies_.delete(rmElmtId); 5133 5134 this.trackedObjectPropertyDependencies_.forEach((propertyElmtId, propertyName) => { 5135 propertyElmtId.delete(rmElmtId); 5136 5137 }); 5138 } 5139 addTrackedObjectPropertyDependency(readProperty, elmtId) { 5140 let dependentElmtIds = this.trackedObjectPropertyDependencies_.get(readProperty); 5141 if (!dependentElmtIds) { 5142 dependentElmtIds = new Set(); 5143 this.trackedObjectPropertyDependencies_.set(readProperty, dependentElmtIds); 5144 } 5145 dependentElmtIds.add(elmtId); 5146 5147 } 5148 getTrackedObjectPropertyDependencies(changedObjectProperty, debugInfo) { 5149 const dependentElmtIds = this.trackedObjectPropertyDependencies_.get(changedObjectProperty) || new Set(); 5150 5151 return dependentElmtIds; 5152 } 5153 dumpInfoDependencies(owningView = undefined, dumpDependantElements) { 5154 const formatElmtId = owningView ? (elmtId => owningView.debugInfoElmtId(elmtId)) : (elmtId => elmtId); 5155 let result = ''; 5156 const arr = Array.from(this.propertyDependencies_).map(formatElmtId); 5157 if (dumpDependantElements) { 5158 return (arr.length > 1 ? arr.join(', ') : arr[0]); 5159 } 5160 if (!this.trackedObjectPropertyDependencies_.size) { 5161 result += `dependencies: variable assignment affects elmtIds: ${Array.from(this.propertyDependencies_).map(formatElmtId).join(', ')}`; 5162 return result; 5163 } 5164 this.trackedObjectPropertyDependencies_.forEach((propertyElmtId, propertyName) => { 5165 result += `dependencies: property '@Track ${propertyName}' change affects elmtIds: ${Array.from(propertyElmtId).map(formatElmtId).join(', ')}`; 5166 }); 5167 return result; 5168 } 5169 dumpInfoDependenciesObj(owningView = undefined, isTrackedMode, isProfiler) { 5170 const formatElmtId = owningView ? (elmtId => owningView.debugInfoElmtId(elmtId, isProfiler)) : (elmtId => elmtId); 5171 let trackedObjectPropertyDependenciesDumpInfo = new Map(); 5172 this.trackedObjectPropertyDependencies_.forEach((propertyElmtId, propertyName) => { 5173 trackedObjectPropertyDependenciesDumpInfo.set(propertyName, Array.from(propertyElmtId).map(formatElmtId)); 5174 }); 5175 let PropertyDependenciesInfo = { 5176 mode: isTrackedMode ? 'Track Mode' : 'Compatible Mode', 5177 trackPropertiesDependencies: MapInfo.toObject(trackedObjectPropertyDependenciesDumpInfo).keyToValue, 5178 propertyDependencies: Array.from(this.propertyDependencies_).map(formatElmtId), 5179 }; 5180 return PropertyDependenciesInfo; 5181 } 5182 hasDependencies() { 5183 return this.propertyDependencies_.size > 0 || this.trackedObjectPropertyDependencies_.size > 0; 5184 } 5185} 5186/* 5187 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 5188 * Licensed under the Apache License, Version 2.0 (the "License"); 5189 * you may not use this file except in compliance with the License. 5190 * You may obtain a copy of the License at 5191 * 5192 * http://www.apache.org/licenses/LICENSE-2.0 5193 * 5194 * Unless required by applicable law or agreed to in writing, software 5195 * distributed under the License is distributed on an "AS IS" BASIS, 5196 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5197 * See the License for the specific language governing permissions and 5198 * limitations under the License. 5199 */ 5200/** 5201 * ObservedPropertyObjectPU 5202 * implementation of @State and @Provide decorated variables of type class object 5203 * 5204 * all definitions in this file are framework internal 5205 * 5206 * class that holds an actual property value of type T 5207 * uses its base class to manage subscribers to this 5208 * property. 5209*/ 5210class ObservedPropertyPU extends ObservedPropertyAbstractPU { 5211 constructor(localInitValue, owningView, propertyName) { 5212 super(owningView, propertyName); 5213 this.setValueInternal(localInitValue); 5214 this.setDecoratorInfo('@State'); 5215 } 5216 aboutToBeDeleted(unsubscribeMe) { 5217 this.unsubscribeWrappedObject(); 5218 this.removeSubscriber(unsubscribeMe); 5219 super.aboutToBeDeleted(); 5220 } 5221 /** 5222 * Called by a SynchedPropertyObjectTwoWayPU (@Link, @Consume) that uses this as sync peer when it has changed 5223 * @param eventSource 5224 */ 5225 syncPeerHasChanged(eventSource) { 5226 5227 this.notifyPropertyHasChangedPU(); 5228 } 5229 syncPeerTrackedPropertyHasChanged(eventSource, changedTrackedObjectPropertyName) { 5230 5231 this.notifyTrackedObjectPropertyHasChanged(changedTrackedObjectPropertyName); 5232 } 5233 /** 5234 * Wrapped ObservedObjectPU has changed 5235 * @param souceObject 5236 * @param changedPropertyName 5237 */ 5238 objectPropertyHasChangedPU(souceObject, changedPropertyName) { 5239 5240 this.notifyPropertyHasChangedPU(); 5241 } 5242 unsubscribeWrappedObject() { 5243 if (this.wrappedValue_) { 5244 if (this.wrappedValue_ instanceof SubscribableAbstract) { 5245 this.wrappedValue_.removeOwningProperty(this); 5246 } 5247 else { 5248 ObservedObject.removeOwningProperty(this.wrappedValue_, this); 5249 // make sure the ObservedObject no longer has a read callback function 5250 // assigned to it 5251 ObservedObject.unregisterPropertyReadCb(this.wrappedValue_); 5252 } 5253 } 5254 } 5255 /* 5256 actually update this.wrappedValue_ 5257 called needs to do value change check 5258 and also notify with this.aboutToChange(); 5259 */ 5260 setValueInternal(newValue) { 5261 5262 if (newValue === this.wrappedValue_) { 5263 5264 5265 return false; 5266 } 5267 if (!this.checkIsSupportedValue(newValue)) { 5268 5269 return false; 5270 } 5271 this.unsubscribeWrappedObject(); 5272 if (!newValue || typeof newValue !== 'object') { 5273 // undefined, null, simple type: 5274 // nothing to subscribe to in case of new value undefined || null || simple type 5275 this.wrappedValue_ = newValue; 5276 } 5277 else if (newValue instanceof SubscribableAbstract) { 5278 5279 this.wrappedValue_ = newValue; 5280 this.wrappedValue_.addOwningProperty(this); 5281 } 5282 else if (ObservedObject.IsObservedObject(newValue)) { 5283 5284 ObservedObject.addOwningProperty(newValue, this); 5285 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(newValue); 5286 this.wrappedValue_ = newValue; 5287 } 5288 else { 5289 5290 this.wrappedValue_ = ObservedObject.createNew(newValue, this); 5291 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.wrappedValue_); 5292 } 5293 5294 return true; 5295 } 5296 get() { 5297 5298 5299 this.recordPropertyDependentUpdate(); 5300 if (this.shouldInstallTrackedObjectReadCb) { 5301 5302 ObservedObject.registerPropertyReadCb(this.wrappedValue_, this.onOptimisedObjectPropertyRead, this); 5303 } 5304 else { 5305 5306 } 5307 5308 return this.wrappedValue_; 5309 } 5310 getUnmonitored() { 5311 5312 // unmonitored get access , no call to notifyPropertyRead ! 5313 return this.wrappedValue_; 5314 } 5315 set(newValue) { 5316 if (this.wrappedValue_ === newValue) { 5317 5318 return; 5319 } 5320 5321 const oldValue = this.wrappedValue_; 5322 if (this.setValueInternal(newValue)) { 5323 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.wrappedValue_, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 5324 } 5325 } 5326 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 5327 5328 const renderingElmtId = this.getRenderingElmtId(); 5329 if (renderingElmtId >= 0) { 5330 if (!isTracked) { 5331 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 5332 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 5333 } 5334 else { 5335 5336 // only record dependency when 5337 // 1 - currently rendering or re-rendering 5338 // TODO room for further optimization: if not an expression in updateFunc, only first time render needs to record 5339 // because there can be change to depended variables unless one of the bindings is a JS expression 5340 // 2 - the changed ObservedObject is the wrapped object. The situation where it can be different is after a value assignment. 5341 if (this.getUnmonitored() === readObservedObject) { 5342 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 5343 } 5344 } 5345 } 5346 5347 } 5348} 5349// class definitions for backward compatibility 5350class ObservedPropertyObjectPU extends ObservedPropertyPU { 5351} 5352class ObservedPropertySimplePU extends ObservedPropertyPU { 5353} 5354/* 5355 * Copyright (c) 2022 Huawei Device Co., Ltd. 5356 * Licensed under the Apache License, Version 2.0 (the "License"); 5357 * you may not use this file except in compliance with the License. 5358 * You may obtain a copy of the License at 5359 * 5360 * http://www.apache.org/licenses/LICENSE-2.0 5361 * 5362 * Unless required by applicable law or agreed to in writing, software 5363 * distributed under the License is distributed on an "AS IS" BASIS, 5364 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5365 * See the License for the specific language governing permissions and 5366 * limitations under the License. 5367 */ 5368/** 5369 * SynchedPropertyObjectOneWayPU 5370 * implementation of @Prop decorated variables of type class object 5371 * 5372 * all definitions in this file are framework internal 5373 * 5374 */ 5375/** 5376 * Initialisation scenarios: 5377 * ------------------------- 5378 * 5379 * 1 - no local initialization, source provided (its ObservedObject value) 5380 * wrap the ObservedObject into an ObservedPropertyObjectPU 5381 * deep copy the ObservedObject into localCopyObservedObject_ 5382 * 5383 * 2 - local initialization, no source provided 5384 * app transpiled code calls set 5385 * leave source_ undefined 5386 * no deep copy needed, but provided local init might need wrapping inside an ObservedObject to set to 5387 * localCopyObservedObject_ 5388 * 5389 * 3 local initialization, source provided (its ObservedObject value) 5390 * current app transpiled code is not optional 5391 * sets source in constructor, as in case 1 5392 * calls set() to set the source value, but this will not deepcopy 5393 * 5394 * Update scenarios: 5395 * ----------------- 5396 * 5397 * 1- assignment of a new Object value: this.aProp = new ClassA() 5398 * rhs can be ObservedObject because of @Observed decoration or now 5399 * notifyPropertyHasChangedPU 5400 * 5401 * 2- local ObservedObject member property change 5402 * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored in localCopyObservedObject_ 5403 * no need to copy, notifyPropertyHasChangedPU 5404 * 5405 * 3- Rerender of the custom component triggered from the parent 5406 * reset() is called (code generated by the transpiler), set the value of source_ , if that causes a change will call syncPeerHasChanged 5407 * syncPeerHasChanged need to deep copy the ObservedObject from source to localCopyObservedObject_ 5408 * notifyPropertyHasChangedPU 5409 * 5410 * 4- source_ ObservedObject member property change 5411 * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored source_.getUnmonitored 5412 * notifyPropertyHasChangedPU 5413 */ 5414class SynchedPropertyOneWayPU extends ObservedPropertyAbstractPU { 5415 constructor(source, owningChildView, thisPropertyName) { 5416 super(owningChildView, thisPropertyName); 5417 this.setDecoratorInfo("@Prop"); 5418 if (source && (typeof (source) === 'object') && ('subscribeMe' in source)) { 5419 // code path for @(Local)StorageProp, the source is a ObservedPropertyObject<C> in a LocalStorage) 5420 this.source_ = source; 5421 this.sourceIsOwnObject = false; 5422 // subscribe to receive value change updates from LocalStorage source property 5423 this.source_.addSubscriber(this); 5424 } 5425 else { 5426 const sourceValue = source; 5427 if (this.checkIsSupportedValue(sourceValue)) { 5428 // code path for 5429 // 1- source is of same type C in parent, source is its value, not the backing store ObservedPropertyObject 5430 // 2- nested Object/Array inside observed another object/array in parent, source is its value 5431 5432 this.createSourceDependency(sourceValue); 5433 this.source_ = new ObservedPropertyObjectPU(sourceValue, this, this.getPropSourceObservedPropertyFakeName()); 5434 this.sourceIsOwnObject = true; 5435 } 5436 } 5437 if (this.source_ !== undefined) { 5438 this.resetLocalValue(this.source_.get(), /* needCopyObject */ true); 5439 } 5440 5441 } 5442 /* 5443 like a destructor, need to call this before deleting 5444 the property. 5445 */ 5446 aboutToBeDeleted() { 5447 if (this.source_) { 5448 this.source_.removeSubscriber(this); 5449 if (this.sourceIsOwnObject === true && this.source_.numberOfSubscrbers() === 0) { 5450 5451 this.source_.aboutToBeDeleted(); 5452 } 5453 this.source_ = undefined; 5454 } 5455 super.aboutToBeDeleted(); 5456 } 5457 // sync peer can be 5458 // 1. the embedded ObservedPropertyPU, followed by a reset when the owning ViewPU received a local update in parent 5459 // 2. a @Link or @Consume that uses this @Prop as a source. FIXME is this possible? - see the if (eventSource && this.source_ == eventSource) { 5460 syncPeerHasChanged(eventSource) { 5461 5462 if (this.source_ === undefined) { 5463 stateMgmtConsole.error(`${this.debugInfo()}: syncPeerHasChanged from peer ${eventSource && eventSource.debugInfo && eventSource.debugInfo()}. source_ undefined. Internal error.`); 5464 5465 return; 5466 } 5467 if (eventSource && this.source_ === eventSource) { 5468 // defensive programming: should always be the case! 5469 const newValue = this.source_.getUnmonitored(); 5470 if (this.checkIsSupportedValue(newValue)) { 5471 5472 if (this.resetLocalValue(newValue, /* needCopyObject */ true)) { 5473 this.notifyPropertyHasChangedPU(); 5474 } 5475 } 5476 } 5477 else { 5478 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.`); 5479 } 5480 5481 } 5482 syncPeerTrackedPropertyHasChanged(eventSource, changedPropertyName) { 5483 5484 if (this.source_ == undefined) { 5485 stateMgmtConsole.error(`${this.debugInfo()}: syncPeerTrackedPropertyHasChanged from peer ${eventSource && eventSource.debugInfo && eventSource.debugInfo()}. source_ undefined. Internal error.`); 5486 5487 return; 5488 } 5489 if (eventSource && this.source_ == eventSource) { 5490 // defensive programming: should always be the case! 5491 const newValue = this.source_.getUnmonitored(); 5492 if (this.checkIsSupportedValue(newValue)) { 5493 5494 if (this.resetLocalValue(newValue, /* needCopyObject */ true)) { 5495 this.notifyTrackedObjectPropertyHasChanged(changedPropertyName); 5496 } 5497 } 5498 } 5499 else { 5500 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.`); 5501 } 5502 5503 } 5504 getUnmonitored() { 5505 5506 // unmonitored get access , no call to notifyPropertyRead ! 5507 return this.localCopyObservedObject_; 5508 } 5509 get() { 5510 5511 5512 this.recordPropertyDependentUpdate(); 5513 if (this.shouldInstallTrackedObjectReadCb) { 5514 5515 ObservedObject.registerPropertyReadCb(this.localCopyObservedObject_, this.onOptimisedObjectPropertyRead, this); 5516 } 5517 else { 5518 5519 } 5520 5521 return this.localCopyObservedObject_; 5522 } 5523 // assignment to local variable in the form of this.aProp = <object value> 5524 set(newValue) { 5525 if (this.localCopyObservedObject_ === newValue) { 5526 5527 return; 5528 } 5529 5530 const oldValue = this.localCopyObservedObject_; 5531 if (this.resetLocalValue(newValue, /* needCopyObject */ false)) { 5532 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.localCopyObservedObject_, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 5533 } 5534 } 5535 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 5536 5537 const renderingElmtId = this.getRenderingElmtId(); 5538 if (renderingElmtId >= 0) { 5539 if (!isTracked) { 5540 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 5541 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 5542 } 5543 else { 5544 5545 if (this.getUnmonitored() === readObservedObject) { 5546 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 5547 } 5548 } 5549 } 5550 5551 } 5552 // called when updated from parent 5553 // during parent ViewPU rerender, calls update lambda of child ViewPU with @Prop variable 5554 // this lambda generated code calls ViewPU.updateStateVarsOfChildByElmtId, 5555 // calls inside app class updateStateVars() 5556 // calls reset() for each @Prop 5557 reset(sourceChangedValue) { 5558 5559 if (this.source_ !== undefined && this.checkIsSupportedValue(sourceChangedValue)) { 5560 // if this.source_.set causes an actual change, then, ObservedPropertyObject source_ will call syncPeerHasChanged method 5561 this.createSourceDependency(sourceChangedValue); 5562 this.source_.set(sourceChangedValue); 5563 } 5564 } 5565 createSourceDependency(sourceObject) { 5566 if (ObservedObject.IsObservedObject(sourceObject)) { 5567 5568 const fake = sourceObject[TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY]; 5569 } 5570 } 5571 /* 5572 unsubscribe from previous wrapped ObjectObject 5573 take a shallow or (TODO) deep copy 5574 copied Object might already be an ObservedObject (e.g. becurse of @Observed decorator) or might be raw 5575 Therefore, conditionally wrap the object, then subscribe 5576 return value true only if localCopyObservedObject_ has been changed 5577 */ 5578 resetLocalValue(newObservedObjectValue, needCopyObject) { 5579 // note: We can not test for newObservedObjectValue == this.localCopyObservedObject_ 5580 // here because the object might still be the same, but some property of it has changed 5581 // this is added for stability test: Target of target is not Object/is not callable/ 5582 // InstanceOf error when target is not Callable/Can not get Prototype on non ECMA Object 5583 try { 5584 if (!this.checkIsSupportedValue(newObservedObjectValue)) { 5585 return false; 5586 } 5587 // unsubscribe from old local copy 5588 if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { 5589 this.localCopyObservedObject_.removeOwningProperty(this); 5590 } 5591 else { 5592 ObservedObject.removeOwningProperty(this.localCopyObservedObject_, this); 5593 // make sure the ObservedObject no longer has a read callback function 5594 // assigned to it 5595 ObservedObject.unregisterPropertyReadCb(this.localCopyObservedObject_); 5596 } 5597 } 5598 catch (error) { 5599 stateMgmtConsole.error(`${this.debugInfo()}, an error occurred in resetLocalValue: ${error.message}`); 5600 ArkTools.print("resetLocalValue SubscribableAbstract", SubscribableAbstract); 5601 ArkTools.print("resetLocalValue ObservedObject", ObservedObject); 5602 ArkTools.print("resetLocalValue this", this); 5603 let a = Reflect.getPrototypeOf(this); 5604 ArkTools.print("resetLocalVale getPrototypeOf", a); 5605 throw error; 5606 } 5607 // shallow/deep copy value 5608 // needed whenever newObservedObjectValue comes from source 5609 // not needed on a local set (aka when called from set() method) 5610 if (needCopyObject) { 5611 ViewPU.pauseRendering(); 5612 this.localCopyObservedObject_ = this.copyObject(newObservedObjectValue, this.info_); 5613 ViewPU.restoreRendering(); 5614 } 5615 else { 5616 this.localCopyObservedObject_ = newObservedObjectValue; 5617 } 5618 if (typeof this.localCopyObservedObject_ === 'object') { 5619 if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { 5620 // deep copy will copy Set of subscribers as well. But local copy only has its own subscribers 5621 // not those of its parent value. 5622 this.localCopyObservedObject_.clearOwningProperties(); 5623 this.localCopyObservedObject_.addOwningProperty(this); 5624 } 5625 else if (ObservedObject.IsObservedObject(this.localCopyObservedObject_)) { 5626 // case: new ObservedObject 5627 ObservedObject.addOwningProperty(this.localCopyObservedObject_, this); 5628 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.localCopyObservedObject_); 5629 } 5630 else { 5631 // wrap newObservedObjectValue raw object as ObservedObject and subscribe to it 5632 5633 this.localCopyObservedObject_ = ObservedObject.createNew(this.localCopyObservedObject_, this); 5634 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.localCopyObservedObject_); 5635 } 5636 5637 } 5638 return true; 5639 } 5640 copyObject(value, propName) { 5641 // ViewStackProcessor.getApiVersion function is not present in API9 5642 // therefore shallowCopyObject will always be used in API version 9 and before 5643 // but the code in this file is the same regardless of API version 5644 5645 return ((typeof ViewStackProcessor['getApiVersion'] == 'function') && 5646 (ViewStackProcessor['getApiVersion']() >= 10)) 5647 ? this.deepCopyObject(value, propName) 5648 : this.shallowCopyObject(value, propName); 5649 } 5650 // API 9 code path 5651 shallowCopyObject(value, propName) { 5652 let rawValue = ObservedObject.GetRawObject(value); 5653 let copy; 5654 if (!rawValue || typeof rawValue !== 'object') { 5655 copy = rawValue; 5656 } 5657 else if (typeof rawValue != 'object') { 5658 // FIXME would it be better to throw Exception here? 5659 stateMgmtConsole.error(`${this.debugInfo()}: shallowCopyObject: request to copy non-object value, actual type is '${typeof rawValue}'. Internal error! Setting copy:=original value.`); 5660 copy = rawValue; 5661 } 5662 else if (rawValue instanceof Array) { 5663 // case Array inside ObservedObject 5664 copy = ObservedObject.createNew([...rawValue], this); 5665 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 5666 } 5667 else if (rawValue instanceof Date) { 5668 // case Date inside ObservedObject 5669 let d = new Date(); 5670 d.setTime(rawValue.getTime()); 5671 // subscribe, also Date gets wrapped / proxied by ObservedObject 5672 copy = ObservedObject.createNew(d, this); 5673 } 5674 else if (rawValue instanceof SubscribableAbstract) { 5675 // case SubscribableAbstract, no wrapping inside ObservedObject 5676 copy = Object.assign({}, rawValue); 5677 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 5678 if (copy instanceof SubscribableAbstract) { 5679 // subscribe 5680 copy.addOwningProperty(this); 5681 } 5682 } 5683 else if (typeof rawValue === 'object') { 5684 // case Object that is not Array, not Date, not SubscribableAbstract 5685 copy = ObservedObject.createNew(Object.assign({}, rawValue), this); 5686 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 5687 } 5688 else { 5689 // TODO in PR "F": change to exception throwing: 5690 stateMgmtConsole.error(`${this.debugInfo()}: shallow failed. Attempt to copy unsupported value of type '${typeof rawValue}' .`); 5691 copy = rawValue; 5692 } 5693 return copy; 5694 } 5695 // API 10 code path 5696 deepCopyObject(obj, variable) { 5697 let copy = SynchedPropertyObjectOneWayPU.deepCopyObjectInternal(obj, variable); 5698 // this subscribe to the top level object/array of the copy 5699 // same as shallowCopy does 5700 if ((obj instanceof SubscribableAbstract) && 5701 (copy instanceof SubscribableAbstract)) { 5702 copy.addOwningProperty(this); 5703 } 5704 else if (ObservedObject.IsObservedObject(obj) && ObservedObject.IsObservedObject(copy)) { 5705 ObservedObject.addOwningProperty(copy, this); 5706 } 5707 return copy; 5708 ; 5709 } 5710 // do not use this function from outside unless it is for testing purposes. 5711 static deepCopyObjectInternal(obj, variable) { 5712 if (!obj || typeof obj !== 'object') { 5713 return obj; 5714 } 5715 let copiedObjects = new Map(); 5716 return getDeepCopyOfObjectRecursive(obj); 5717 function getDeepCopyOfObjectRecursive(obj) { 5718 if (!obj || typeof obj !== 'object') { 5719 return obj; 5720 } 5721 const alreadyCopiedObject = copiedObjects.get(obj); 5722 if (alreadyCopiedObject) { 5723 5724 return alreadyCopiedObject; 5725 } 5726 let copy; 5727 if (obj instanceof Set) { 5728 copy = new Set(); 5729 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5730 copiedObjects.set(obj, copy); 5731 obj.forEach((setKey) => { 5732 copy.add(getDeepCopyOfObjectRecursive(setKey)); 5733 }); 5734 } 5735 else if (obj instanceof Map) { 5736 copy = new Map(); 5737 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5738 copiedObjects.set(obj, copy); 5739 obj.forEach((mapValue, mapKey) => { 5740 copy.set(mapKey, getDeepCopyOfObjectRecursive(mapValue)); 5741 }); 5742 } 5743 else if (obj instanceof Date) { 5744 copy = new Date(); 5745 copy.setTime(obj.getTime()); 5746 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5747 copiedObjects.set(obj, copy); 5748 } 5749 else if (obj instanceof Object) { 5750 copy = Array.isArray(obj) ? [] : {}; 5751 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 5752 copiedObjects.set(obj, copy); 5753 } 5754 else { 5755 /** 5756 * As we define a variable called 'copy' with no initial value before this if/else branch, 5757 * so it will crash at Reflect.set when obj is not instance of Set/Map/Date/Object/Array. 5758 * This branch is for those known special cases: 5759 * 1、obj is a NativePointer 5760 * 2、obj is a @Sendable decorated class 5761 * In case the application crash directly, use shallow copy instead. 5762 * Will use new API when ark engine team is ready which will be a more elegant way. 5763 * If we difine the copy like 'let copy = {};', 5764 * it will not crash but copy will be a normal JSObject, not a @Sendable object. 5765 * To keep the functionality of @Sendable, still not define copy with initial value. 5766 */ 5767 stateMgmtConsole.warn('DeepCopy target obj is not instance of Set/Date/Map/Object/Array, will use shallow copy instead.'); 5768 return obj; 5769 } 5770 Object.keys(obj).forEach((objKey) => { 5771 copy[objKey] = getDeepCopyOfObjectRecursive(obj[objKey]); 5772 }); 5773 return ObservedObject.IsObservedObject(obj) ? ObservedObject.createNew(copy, undefined) : copy; 5774 } 5775 } 5776} 5777// class definitions for backward compatibility 5778class SynchedPropertySimpleOneWayPU extends SynchedPropertyOneWayPU { 5779} 5780class SynchedPropertyObjectOneWayPU extends SynchedPropertyOneWayPU { 5781} 5782/* 5783 * Copyright (c) 2022 Huawei Device Co., Ltd. 5784 * Licensed under the Apache License, Version 2.0 (the "License"); 5785 * you may not use this file except in compliance with the License. 5786 * You may obtain a copy of the License at 5787 * 5788 * http://www.apache.org/licenses/LICENSE-2.0 5789 * 5790 * Unless required by applicable law or agreed to in writing, software 5791 * distributed under the License is distributed on an "AS IS" BASIS, 5792 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5793 * See the License for the specific language governing permissions and 5794 * limitations under the License. 5795 */ 5796/** 5797 * SynchedPropertyObjectTwoWayPU 5798 * implementation of @Link and @Consume decorated variables of type class object 5799 * 5800 * all definitions in this file are framework internal 5801*/ 5802class SynchedPropertyTwoWayPU extends ObservedPropertyAbstractPU { 5803 constructor(source, owningChildView, thisPropertyName) { 5804 super(owningChildView, thisPropertyName); 5805 this.source_ = source; 5806 if (this.source_) { 5807 // register to the parent property 5808 this.source_.addSubscriber(this); 5809 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.source_.getUnmonitored()); 5810 } 5811 else { 5812 throw new SyntaxError(`${this.debugInfo()}: constructor: source variable in parent/ancestor @Component must be defined. Application error!`); 5813 } 5814 this.setDecoratorInfo("@Link"); 5815 } 5816 /* 5817 like a destructor, need to call this before deleting 5818 the property. 5819 */ 5820 aboutToBeDeleted() { 5821 // unregister from parent of this link 5822 if (this.source_) { 5823 this.source_.removeSubscriber(this); 5824 // unregister from the ObservedObject 5825 ObservedObject.removeOwningProperty(this.source_.getUnmonitored(), this); 5826 } 5827 super.aboutToBeDeleted(); 5828 } 5829 isStorageLinkProp() { 5830 return (this.source_ && this.source_ instanceof ObservedPropertyAbstract && (!(this.source_ instanceof ObservedPropertyAbstractPU))); 5831 } 5832 setObject(newValue) { 5833 if (!this.source_) { 5834 throw new SyntaxError(`${this.debugInfo()}: setObject (assign a new value), no source variable in parent/ancestor \ 5835 @Component. Application error.`); 5836 } 5837 if (this.getUnmonitored() === newValue) { 5838 5839 return; 5840 } 5841 5842 if (this.checkIsSupportedValue(newValue)) { 5843 // the source_ ObservedProperty will call: this.syncPeerHasChanged(newValue); 5844 this.source_.set(newValue); 5845 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(newValue); 5846 } 5847 } 5848 /** 5849 * Called when sync peer ObservedPropertyObject or SynchedPropertyObjectTwoWay has changed value 5850 * that peer can be in either parent or child component if 'this' is used for a @Link 5851 * that peer can be in either ancestor or descendant component if 'this' is used for a @Consume 5852 * @param eventSource 5853 */ 5854 syncPeerHasChanged(eventSource) { 5855 5856 if (!this.changeNotificationIsOngoing_) { 5857 5858 this.notifyPropertyHasChangedPU(); 5859 } 5860 5861 } 5862 syncPeerTrackedPropertyHasChanged(eventSource, changedTrackedObjectPropertyName) { 5863 5864 if (!this.changeNotificationIsOngoing_) { 5865 5866 this.notifyTrackedObjectPropertyHasChanged(changedTrackedObjectPropertyName); 5867 } 5868 5869 } 5870 getUnmonitored() { 5871 5872 return (this.source_ ? this.source_.getUnmonitored() : undefined); 5873 } 5874 // get 'read through` from the ObservedProperty 5875 get() { 5876 5877 5878 this.recordPropertyDependentUpdate(); 5879 const result = this.getUnmonitored(); 5880 if (this.shouldInstallTrackedObjectReadCb) { 5881 5882 ObservedObject.registerPropertyReadCb(result, this.onOptimisedObjectPropertyRead, this); 5883 } 5884 else { 5885 5886 } 5887 5888 return result; 5889 } 5890 // set 'writes through` to the ObservedProperty 5891 set(newValue) { 5892 5893 if (this.getUnmonitored() === newValue) { 5894 5895 5896 return; 5897 } 5898 5899 // avoid circular notifications @Link -> source @State -> other but also back to same @Link 5900 this.changeNotificationIsOngoing_ = true; 5901 let oldValue = this.getUnmonitored(); 5902 this.setObject(newValue); 5903 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ newValue, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 5904 this.changeNotificationIsOngoing_ = false; 5905 5906 } 5907 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 5908 5909 const renderingElmtId = this.getRenderingElmtId(); 5910 if (renderingElmtId >= 0) { 5911 if (!isTracked) { 5912 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 5913 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 5914 } 5915 else { 5916 5917 if (this.getUnmonitored() === readObservedObject) { 5918 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 5919 } 5920 } 5921 } 5922 5923 } 5924} 5925// class definitions for backward compatibility 5926class SynchedPropertyObjectTwoWayPU extends SynchedPropertyTwoWayPU { 5927} 5928class SynchedPropertySimpleTwoWayPU extends SynchedPropertyTwoWayPU { 5929} 5930/* 5931 * Copyright (c) 2022 Huawei Device Co., Ltd. 5932 * Licensed under the Apache License, Version 2.0 (the "License"); 5933 * you may not use this file except in compliance with the License. 5934 * You may obtain a copy of the License at 5935 * 5936 * http://www.apache.org/licenses/LICENSE-2.0 5937 * 5938 * Unless required by applicable law or agreed to in writing, software 5939 * distributed under the License is distributed on an "AS IS" BASIS, 5940 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5941 * See the License for the specific language governing permissions and 5942 * limitations under the License. 5943 */ 5944/** 5945 * SynchedPropertyNestedObjectPU 5946 * implementation of @ObjectLink decorated variables 5947 * 5948 * all definitions in this file are framework internal 5949 * 5950 */ 5951class SynchedPropertyNestedObjectPU extends ObservedPropertyAbstractPU { 5952 /** 5953 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 5954 * example 5955 * this.b.$a with b of type PC and a of type C, or 5956 * this.$b[5] with this.b of type PC and array item b[5] of type C; 5957 * 5958 * @param subscribeMe 5959 * @param propName 5960 */ 5961 constructor(obsObject, owningChildView, propertyName) { 5962 super(owningChildView, propertyName); 5963 this.obsObject_ = undefined; 5964 this.createSourceDependency(obsObject); 5965 this.setValueInternal(obsObject); 5966 this.setDecoratorInfo("@ObjectLink"); 5967 } 5968 /* 5969 like a destructor, need to call this before deleting 5970 the property. 5971 */ 5972 aboutToBeDeleted() { 5973 // unregister from the ObservedObject 5974 ObservedObject.removeOwningProperty(this.obsObject_, this); 5975 super.aboutToBeDeleted(); 5976 } 5977 getUnmonitored() { 5978 5979 // unmonitored get access , no call to notifyPropertyRead ! 5980 return this.obsObject_; 5981 } 5982 // get 'read through` from the ObservedProperty 5983 get() { 5984 5985 5986 this.recordPropertyDependentUpdate(); 5987 if (this.shouldInstallTrackedObjectReadCb) { 5988 5989 ObservedObject.registerPropertyReadCb(this.obsObject_, this.onOptimisedObjectPropertyRead, this); 5990 } 5991 else { 5992 5993 } 5994 5995 return this.obsObject_; 5996 } 5997 // parent ViewPU rerender, runs update lambda with child ViewPU that contains a @ObjectLink 5998 // calls ViewPU.updateStateVarsByElmtId, calls updateStateVars in application class, calls this 'set' function 5999 set(newValue) { 6000 if (this.obsObject_ === newValue) { 6001 6002 return; 6003 } 6004 6005 const oldValue = this.obsObject_; 6006 if (this.setValueInternal(newValue)) { 6007 this.createSourceDependency(newValue); 6008 // notify value change to subscribing View 6009 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.obsObject_, this.notifyPropertyHasChangedPU, this.notifyTrackedObjectPropertyHasChanged, this); 6010 } 6011 } 6012 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 6013 6014 const renderingElmtId = this.getRenderingElmtId(); 6015 if (renderingElmtId >= 0) { 6016 if (!isTracked) { 6017 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 6018 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 6019 } 6020 else { 6021 6022 if (this.getUnmonitored() === readObservedObject) { 6023 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 6024 } 6025 } 6026 } 6027 6028 } 6029 createSourceDependency(sourceObject) { 6030 if (ObservedObject.IsObservedObject(sourceObject)) { 6031 6032 const fake = sourceObject[TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY]; 6033 } 6034 } 6035 setValueInternal(newValue) { 6036 if (!this.checkIsObject(newValue)) { 6037 return false; 6038 } 6039 if (this.obsObject_ != undefined) { 6040 if (this.obsObject_ instanceof SubscribableAbstract) { 6041 // unregister from SubscribableAbstract object 6042 this.obsObject_.removeOwningProperty(this); 6043 } 6044 else if (ObservedObject.IsObservedObject(this.obsObject_)) { 6045 // unregister from the ObservedObject 6046 ObservedObject.removeOwningProperty(this.obsObject_, this); 6047 // make sure the ObservedObject no longer has a read callback function 6048 // assigned to it 6049 ObservedObject.unregisterPropertyReadCb(this.obsObject_); 6050 } 6051 } 6052 this.obsObject_ = newValue; 6053 if (this.obsObject_ != undefined) { 6054 if (this.obsObject_ instanceof SubscribableAbstract) { 6055 // register to SubscribableAbstract object 6056 this.obsObject_.addOwningProperty(this); 6057 } 6058 else if (ObservedObject.IsObservedObject(this.obsObject_)) { 6059 // register to the ObservedObject 6060 ObservedObject.addOwningProperty(this.obsObject_, this); 6061 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.obsObject_); 6062 } 6063 else { 6064 stateMgmtConsole.applicationWarn(`${this.debugInfo()}: set/init (method setValueInternal): assigned value is not 6065 be decorated by @Observed. Value changes will not be observed and UI will not update.`); 6066 } 6067 } 6068 return true; 6069 } 6070} 6071/** backward compatibility after typo in classname fix */ 6072class SynchedPropertyNesedObjectPU extends SynchedPropertyNestedObjectPU { 6073} 6074/* 6075 * Copyright (c) 2023 Huawei Device Co., Ltd. 6076 * Licensed under the Apache License, Version 2.0 (the "License"); 6077 * you may not use this file except in compliance with the License. 6078 * You may obtain a copy of the License at 6079 * 6080 * http://www.apache.org/licenses/LICENSE-2.0 6081 * 6082 * Unless required by applicable law or agreed to in writing, software 6083 * distributed under the License is distributed on an "AS IS" BASIS, 6084 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6085 * See the License for the specific language governing permissions and 6086 * limitations under the License. 6087 */ 6088// defined a globle function to clean up the removeItems when idle 6089function uiNodeCleanUpIdleTask() { 6090 6091 UINodeRegisterProxy.obtainDeletedElmtIds(); 6092 UINodeRegisterProxy.unregisterElmtIdsFromIViews(); 6093} 6094class UINodeRegisterProxy { 6095 constructor() { 6096 this.removeElementsInfo_ = new Array(); 6097 } 6098 static obtainDeletedElmtIds() { 6099 6100 if ((!UINodeRegisterProxy.instance_.obtainDeletedElmtIds) || typeof UINodeRegisterProxy.instance_.obtainDeletedElmtIds !== 'function') { 6101 stateMgmtConsole.error(`UINodeRegisterProxy obtainDeletedElmtIds is not a function: ${UINodeRegisterProxy.instance_.obtainDeletedElmtIds}.`); 6102 } 6103 else { 6104 UINodeRegisterProxy.instance_.obtainDeletedElmtIds(); 6105 } 6106 } 6107 // FIXME unregisterElmtIdsFromIViews needs adaptation 6108 static unregisterElmtIdsFromIViews() { 6109 6110 UINodeRegisterProxy.instance_.unregisterElmtIdsFromIViews(); 6111 } 6112 // unregisters all the received removedElements in func parameter 6113 static unregisterRemovedElmtsFromViewPUs(removedElements) { 6114 6115 UINodeRegisterProxy.instance_.populateRemoveElementInfo(removedElements); 6116 UINodeRegisterProxy.instance_.unregisterElmtIdsFromIViews(); 6117 } 6118 static registerModifierElmtDeleteCallback(callback) { 6119 if (UINodeRegisterProxy.modifierElmtDeleteCallback_) { 6120 return; 6121 } 6122 UINodeRegisterProxy.modifierElmtDeleteCallback_ = callback; 6123 } 6124 populateRemoveElementInfo(removedElements) { 6125 for (const elmtId of removedElements) { 6126 this.removeElementsInfo_.push(elmtId); 6127 } 6128 } 6129 /* just get the remove items from the native side 6130 */ 6131 obtainDeletedElmtIds() { 6132 6133 let removedElementsInfo = new Array(); 6134 ViewStackProcessor.moveDeletedElmtIds(removedElementsInfo); 6135 6136 this.removeElementsInfo_ = removedElementsInfo; 6137 } 6138 unregisterElmtIdsFromIViews() { 6139 6140 if (this.removeElementsInfo_.length === 0) { 6141 6142 return; 6143 } 6144 let owningView; 6145 this.removeElementsInfo_.forEach((elmtId) => { 6146 const owningViewPUWeak = UINodeRegisterProxy.ElementIdToOwningViewPU_.get(elmtId); 6147 if (owningViewPUWeak !== undefined) { 6148 owningView = owningViewPUWeak.deref(); 6149 if (owningView) { 6150 owningView.purgeDeleteElmtId(elmtId); 6151 } 6152 else { 6153 6154 } 6155 if (UINodeRegisterProxy.modifierElmtDeleteCallback_) { 6156 UINodeRegisterProxy.modifierElmtDeleteCallback_(elmtId); 6157 } 6158 } 6159 else { 6160 6161 } 6162 // FIXME: only do this if app uses V3 6163 ObserveV2.getObserve().clearBinding(elmtId); 6164 }); 6165 this.removeElementsInfo_.length = 0; 6166 } 6167 static cleanUpDeadReferences() { 6168 6169 ObserveV2.getObserve().cleanUpDeadReferences(); 6170 } 6171} 6172UINodeRegisterProxy.notRecordingDependencies = -1; 6173UINodeRegisterProxy.monitorIllegalV2V3StateAccess = -2; 6174UINodeRegisterProxy.instance_ = new UINodeRegisterProxy(); 6175UINodeRegisterProxy.ElementIdToOwningViewPU_ = new Map(); 6176/* 6177 * Copyright (c) 2022-2024 Huawei Device Co., Ltd. 6178 * Licensed under the Apache License, Version 2.0 (the "License"); 6179 * you may not use this file except in compliance with the License. 6180 * You may obtain a copy of the License at 6181 * 6182 * http://www.apache.org/licenses/LICENSE-2.0 6183 * 6184 * Unless required by applicable law or agreed to in writing, software 6185 * distributed under the License is distributed on an "AS IS" BASIS, 6186 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6187 * See the License for the specific language governing permissions and 6188 * limitations under the License. 6189 * 6190 * * ViewPU - View for Partial Update 6191 * 6192* all definitions in this file are framework internal 6193*/ 6194class ViewPU extends PUV2ViewBase { 6195 /** 6196 * Create a View 6197 * 6198 * 1. option: top level View, specify 6199 * - compilerAssignedUniqueChildId must specify 6200 * - parent=undefined 6201 * - localStorage must provide if @LocalSTorageLink/Prop variables are used 6202 * in this View or descendant Views. 6203 * 6204 * 2. option: not a top level View 6205 * - compilerAssignedUniqueChildId must specify 6206 * - parent must specify 6207 * - localStorage do not specify, will inherit from parent View. 6208 * 6209 */ 6210 constructor(parent, localStorage, elmtId = UINodeRegisterProxy.notRecordingDependencies, extraInfo = undefined) { 6211 var _a; 6212 super(parent, elmtId, extraInfo); 6213 // flag for initial rendering or re-render on-going. 6214 this.isRenderInProgress = false; 6215 // flag for initial rendering being done 6216 this.isInitialRenderDone = false; 6217 this.runReuse_ = false; 6218 this.watchedProps = new Map(); 6219 this.recycleManager_ = undefined; 6220 // Internal variable to keep track is component recycled or not. 6221 this.hasBeenRecycled_ = false; 6222 this.preventRecursiveRecycle_ = false; 6223 this.delayRecycleNodeRerender = false; 6224 this.delayRecycleNodeRerenderDeep = false; 6225 // @Provide'd variables by this class and its ancestors 6226 this.providedVars_ = new Map(); 6227 // my LocalStorage instance, shared with ancestor Views. 6228 // create a default instance on demand if none is initialized 6229 this.localStoragebackStore_ = undefined; 6230 /** 6231 * on first render create a new Instance of Repeat 6232 * on re-render connect to existing instance 6233 * @param arr 6234 * @returns 6235 */ 6236 this.__mkRepeatAPI = (arr) => { 6237 // factory is for future extensions, currently always return the same 6238 const elmtId = this.getCurrentlyRenderedElmtId(); 6239 let repeat = this.elmtId2Repeat_.get(elmtId); 6240 if (!repeat) { 6241 repeat = new __Repeat(this, arr); 6242 this.elmtId2Repeat_.set(elmtId, repeat); 6243 } 6244 else { 6245 repeat.updateArr(arr); 6246 } 6247 return repeat; 6248 }; 6249 // if set use the elmtId also as the ViewPU object's subscribable id. 6250 // these matching is requirement for updateChildViewById(elmtId) being able to 6251 // find the child ViewPU object by given elmtId 6252 //this.id_ = elmtId == UINodeRegisterProxy.notRecordingDependencies ? SubscriberManager.MakeId() : elmtId; 6253 this.localStoragebackStore_ = undefined; 6254 6255 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onViewPUCreate(this); 6256 if (localStorage) { 6257 this.localStorage_ = localStorage; 6258 6259 } 6260 SubscriberManager.Add(this); 6261 6262 } 6263 get ownObservedPropertiesStore_() { 6264 if (!this.ownObservedPropertiesStore__) { 6265 // lazy init 6266 this.ownObservedPropertiesStore__ = new Set(); 6267 this.obtainOwnObservedProperties(); 6268 } 6269 return this.ownObservedPropertiesStore__; 6270 } 6271 obtainOwnObservedProperties() { 6272 let usesStateMgmtVersion = 0; 6273 Object.getOwnPropertyNames(this) 6274 .filter((propName) => { 6275 // do not include backing store, and ObserveV2/MonitorV2/ComputedV2 meta data objects 6276 return (propName.startsWith('__') && 6277 !propName.startsWith(ObserveV2.OB_PREFIX) && 6278 !propName.startsWith(MonitorV2.WATCH_PREFIX) && 6279 !propName.startsWith(ComputedV2.COMPUTED_PREFIX)); 6280 }) 6281 .forEach((propName) => { 6282 const stateVar = Reflect.get(this, propName); 6283 if (stateVar && typeof stateVar === 'object' && 'notifyPropertyHasChangedPU' in stateVar) { 6284 6285 this.ownObservedPropertiesStore_.add(stateVar); 6286 usesStateMgmtVersion = 2; 6287 } 6288 else { 6289 6290 } 6291 }); 6292 if (this.isViewV3 === true) { 6293 if (usesStateMgmtVersion === 2) { 6294 const error = `${this.debugInfo__()}: mixed use of stateMgmt V2 and V3 variable decorators. Application error!`; 6295 stateMgmtConsole.applicationError(error); 6296 throw new Error(error); 6297 } 6298 } 6299 6300 } 6301 get localStorage_() { 6302 if (!this.localStoragebackStore_ && this.getParent()) { 6303 6304 this.localStoragebackStore_ = this.getParent().localStorage_; 6305 } 6306 if (!this.localStoragebackStore_) { 6307 6308 this.localStoragebackStore_ = new LocalStorage({ /* empty */}); 6309 } 6310 return this.localStoragebackStore_; 6311 } 6312 set localStorage_(instance) { 6313 if (!instance) { 6314 // setting to undefined not allowed 6315 return; 6316 } 6317 if (this.localStoragebackStore_) { 6318 stateMgmtConsole.applicationError(`${this.debugInfo__()}: constructor: is setting LocalStorage instance twice. Application error.`); 6319 } 6320 this.localStoragebackStore_ = instance; 6321 } 6322 // FIXME 6323 // indicate if this is V2 or a V3 component 6324 // V2 by default, changed to V3 by the first V3 decorated variable 6325 // when splitting ViewPU and ViewV3 6326 // use instanceOf. Until then, this is a workaround. 6327 // @state, @track, etc V3 decorator functions modify isViewV3 to return true 6328 // (decorator can modify functions in prototype) 6329 // FIXME 6330 get isViewV3() { 6331 return false; 6332 } 6333 onGlobalThemeChanged() { 6334 this.onWillApplyThemeInternally(); 6335 this.forceCompleteRerender(false); 6336 this.childrenWeakrefMap_.forEach((weakRefChild) => { 6337 const child = weakRefChild.deref(); 6338 if (child) { 6339 child.onGlobalThemeChanged(); 6340 } 6341 }); 6342 } 6343 aboutToReuse(params) { } 6344 aboutToRecycle() { } 6345 onWillApplyThemeInternally() { 6346 var _a; 6347 const theme = (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.getFinalTheme(this.id__()); 6348 if (theme) { 6349 this.onWillApplyTheme(theme); 6350 } 6351 } 6352 onWillApplyTheme(theme) { } 6353 // super class will call this function from 6354 // its aboutToBeDeleted implementation 6355 aboutToBeDeletedInternal() { 6356 var _a; 6357 6358 // if this isDeleting_ is true already, it may be set delete status recursively by its parent, so it is not necessary 6359 // to set and recursively set its children any more 6360 if (!this.isDeleting_) { 6361 this.isDeleting_ = true; 6362 this.setDeleteStatusRecursively(); 6363 } 6364 // tell UINodeRegisterProxy that all elmtIds under 6365 // this ViewPU should be treated as already unregistered 6366 6367 // purge the elmtIds owned by this viewPU from the updateFuncByElmtId and also the state variable dependent elmtIds 6368 Array.from(this.updateFuncByElmtId.keys()).forEach((elmtId) => { 6369 this.purgeDeleteElmtId(elmtId); 6370 }); 6371 if (this.hasRecycleManager()) { 6372 this.getRecycleManager().purgeAllCachedRecycleNode(); 6373 } 6374 // un-registration of ElementIDs 6375 6376 // it will unregister removed elmtIds from all ViewPu, equals purgeDeletedElmtIdsRecursively 6377 this.purgeDeletedElmtIds(); 6378 // unregisters its own id once its children are unregistered above 6379 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs([this.id__()]); 6380 6381 // in case this ViewPU is currently frozen 6382 PUV2ViewBase.inactiveComponents_.delete(`${this.constructor.name}[${this.id__()}]`); 6383 this.updateFuncByElmtId.clear(); 6384 this.watchedProps.clear(); 6385 this.providedVars_.clear(); 6386 if (this.ownObservedPropertiesStore__) { 6387 this.ownObservedPropertiesStore__.clear(); 6388 } 6389 if (this.getParent()) { 6390 this.getParent().removeChild(this); 6391 } 6392 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onViewPUDelete(this); 6393 this.localStoragebackStore_ = undefined; 6394 } 6395 purgeDeleteElmtId(rmElmtId) { 6396 6397 const result = this.updateFuncByElmtId.delete(rmElmtId); 6398 if (result) { 6399 this.purgeVariableDependenciesOnElmtIdOwnFunc(rmElmtId); 6400 // it means rmElmtId has finished all the unregistration from the js side, ElementIdToOwningViewPU_ does not need to keep it 6401 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(rmElmtId); 6402 } 6403 return result; 6404 } 6405 purgeVariableDependenciesOnElmtIdOwnFunc(elmtId) { 6406 this.ownObservedPropertiesStore_.forEach((stateVar) => { 6407 stateVar.purgeDependencyOnElmtId(elmtId); 6408 }); 6409 } 6410 debugInfoStateVars() { 6411 let result = `|--${this.constructor.name}[${this.id__()}]`; 6412 Object.getOwnPropertyNames(this) 6413 .filter((varName) => varName.startsWith('__') && !varName.startsWith(ObserveV2.OB_PREFIX)) 6414 .forEach((varName) => { 6415 const prop = Reflect.get(this, varName); 6416 if ('debugInfoDecorator' in prop) { 6417 const observedProp = prop; 6418 result += `\n ${observedProp.debugInfoDecorator()} '${observedProp.info()}'[${observedProp.id__()}]`; 6419 result += `\n ${observedProp.debugInfoSubscribers()}`; 6420 result += `\n ${observedProp.debugInfoSyncPeers()}`; 6421 result += `\n ${observedProp.debugInfoDependentElmtIds()}`; 6422 result += `\n ${observedProp.debugInfoDependentComponents()}`; 6423 } 6424 }); 6425 return result; 6426 } 6427 /** 6428 * Indicate if this @Component is allowed to freeze by calling with freezeState=true 6429 * Called with value of the @Component decorator 'freezeWhenInactive' parameter 6430 * or depending how UI compiler works also with 'undefined' 6431 * @param freezeState only value 'true' will be used, otherwise inherits from parent 6432 * if not parent, set to false. 6433 */ 6434 initAllowComponentFreeze(freezeState) { 6435 // set to true if freeze parameter set for this @Component to true 6436 // otherwise inherit from parent @Component (if it exists). 6437 this.isCompFreezeAllowed_ = freezeState || this.isCompFreezeAllowed_; 6438 6439 } 6440 /** 6441 * ArkUI engine will call this function when the corresponding CustomNode's active status change. 6442 * ArkUI engine will not recurse children nodes to inform the stateMgmt for the performance reason. 6443 * So the stateMgmt needs to recurse the children although the isCompFreezeAllowed is false because the children nodes 6444 * may enable the freezeWhenInActive. 6445 * @param active true for active, false for inactive 6446 */ 6447 setActiveInternal(active) { 6448 6449 if (this.isCompFreezeAllowed()) { 6450 this.isActive_ = active; 6451 if (this.isActive_) { 6452 this.onActiveInternal(); 6453 } 6454 else { 6455 this.onInactiveInternal(); 6456 } 6457 } 6458 for (const child of this.childrenWeakrefMap_.values()) { 6459 const childView = child.deref(); 6460 if (childView) { 6461 childView.setActiveInternal(active); 6462 } 6463 } 6464 6465 } 6466 onActiveInternal() { 6467 if (!this.isActive_) { 6468 return; 6469 } 6470 6471 this.performDelayedUpdate(); 6472 // Remove the active component from the Map for Dfx 6473 ViewPU.inactiveComponents_.delete(`${this.constructor.name}[${this.id__()}]`); 6474 } 6475 onInactiveInternal() { 6476 if (this.isActive_) { 6477 return; 6478 } 6479 6480 for (const stateLinkProp of this.ownObservedPropertiesStore_) { 6481 stateLinkProp.enableDelayedNotification(); 6482 } 6483 // Add the inactive Components to Map for Dfx listing 6484 ViewPU.inactiveComponents_.add(`${this.constructor.name}[${this.id__()}]`); 6485 } 6486 initialRenderView() { 6487 6488 this.onWillApplyThemeInternally(); 6489 this.obtainOwnObservedProperties(); 6490 this.isRenderInProgress = true; 6491 this.initialRender(); 6492 this.isRenderInProgress = false; 6493 this.isInitialRenderDone = true; 6494 6495 } 6496 UpdateElement(elmtId) { 6497 6498 if (elmtId === this.id__()) { 6499 // do not attempt to update itself. 6500 // a @Prop can add a dependency of the ViewPU onto itself. Ignore it. 6501 6502 return; 6503 } 6504 // do not process an Element that has been marked to be deleted 6505 const entry = this.updateFuncByElmtId.get(elmtId); 6506 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 6507 if (typeof updateFunc !== 'function') { 6508 6509 } 6510 else { 6511 6512 this.isRenderInProgress = true; 6513 6514 updateFunc(elmtId, /* isFirstRender */ false); 6515 6516 6517 this.finishUpdateFunc(elmtId); 6518 6519 this.isRenderInProgress = false; 6520 6521 } 6522 6523 } 6524 delayCompleteRerender(deep = false) { 6525 this.delayRecycleNodeRerender = true; 6526 this.delayRecycleNodeRerenderDeep = deep; 6527 } 6528 flushDelayCompleteRerender() { 6529 this.forceCompleteRerender(this.delayRecycleNodeRerenderDeep); 6530 this.delayRecycleNodeRerender = false; 6531 } 6532 /** 6533 * force a complete rerender / update on specific node by executing update function. 6534 * 6535 * @param elmtId which node needs to update. 6536 * 6537 * framework internal functions, apps must not call 6538 */ 6539 forceRerenderNode(elmtId) { 6540 6541 // see which elmtIds are managed by this View 6542 // and clean up all book keeping for them 6543 this.purgeDeletedElmtIds(); 6544 this.UpdateElement(elmtId); 6545 // remove elemtId from dirtDescendantElementIds. 6546 this.dirtDescendantElementIds_.delete(elmtId); 6547 6548 } 6549 // implements IMultiPropertiesChangeSubscriber 6550 viewPropertyHasChanged(varName, dependentElmtIds) { 6551 6552 aceTrace.begin('ViewPU.viewPropertyHasChanged', this.constructor.name, varName, dependentElmtIds.size); 6553 if (this.isRenderInProgress) { 6554 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!`); 6555 } 6556 this.syncInstanceId(); 6557 if (dependentElmtIds.size && !this.isFirstRender()) { 6558 if (!this.dirtDescendantElementIds_.size && !this.runReuse_) { 6559 // mark ComposedElement dirty when first elmtIds are added 6560 // do not need to do this every time 6561 this.markNeedUpdate(); 6562 } 6563 6564 for (const elmtId of dependentElmtIds) { 6565 if (this.hasRecycleManager()) { 6566 this.dirtDescendantElementIds_.add(this.recycleManager_.proxyNodeId(elmtId)); 6567 } 6568 else { 6569 this.dirtDescendantElementIds_.add(elmtId); 6570 } 6571 } 6572 6573 } 6574 else { 6575 6576 6577 } 6578 let cb = this.watchedProps.get(varName); 6579 if (cb && typeof cb === 'function') { 6580 6581 cb.call(this, varName); 6582 } 6583 this.restoreInstanceId(); 6584 aceTrace.end(); 6585 6586 } 6587 /** 6588 * inform that UINode with given elmtId needs rerender 6589 * does NOT exec @Watch function. 6590 * only used on V3 code path from ObserveV2.fireChange. 6591 * 6592 * FIXME will still use in the future? 6593 */ 6594 uiNodeNeedUpdateV3(elmtId) { 6595 if (this.isFirstRender()) { 6596 return; 6597 } 6598 6599 if (!this.dirtDescendantElementIds_.size) { // && !this runReuse_) { 6600 // mark ComposedElement dirty when first elmtIds are added 6601 // do not need to do this every time 6602 this.syncInstanceId(); 6603 this.markNeedUpdate(); 6604 this.restoreInstanceId(); 6605 } 6606 if (this.hasRecycleManager()) { 6607 this.dirtDescendantElementIds_.add(this.recycleManager_.proxyNodeId(elmtId)); 6608 } 6609 else { 6610 this.dirtDescendantElementIds_.add(elmtId); 6611 } 6612 6613 6614 } 6615 performDelayedUpdate() { 6616 if (!this.ownObservedPropertiesStore_.size && !this.elmtIdsDelayedUpdate.size) { 6617 return; 6618 } 6619 6620 aceTrace.begin('ViewPU.performDelayedUpdate', this.constructor.name); 6621 6622 this.syncInstanceId(); 6623 for (const stateLinkPropVar of this.ownObservedPropertiesStore_) { 6624 const changedElmtIds = stateLinkPropVar.moveElmtIdsForDelayedUpdate(); 6625 if (changedElmtIds) { 6626 const varName = stateLinkPropVar.info(); 6627 if (changedElmtIds.size && !this.isFirstRender()) { 6628 for (const elmtId of changedElmtIds) { 6629 this.dirtDescendantElementIds_.add(elmtId); 6630 } 6631 } 6632 6633 const cb = this.watchedProps.get(varName); 6634 if (cb) { 6635 6636 cb.call(this, varName); 6637 } 6638 } 6639 } // for all ownStateLinkProps_ 6640 for (let elementId of this.elmtIdsDelayedUpdate) { 6641 this.dirtDescendantElementIds_.add(elementId); 6642 } 6643 this.elmtIdsDelayedUpdate.clear(); 6644 this.restoreInstanceId(); 6645 if (this.dirtDescendantElementIds_.size) { 6646 this.markNeedUpdate(); 6647 } 6648 aceTrace.end(); 6649 6650 } 6651 /** 6652 * Function to be called from the constructor of the sub component 6653 * to register a @Watch variable 6654 * @param propStr name of the variable. Note from @Provide and @Consume this is 6655 * the variable name and not the alias! 6656 * @param callback application defined member function of sub-class 6657 */ 6658 declareWatch(propStr, callback) { 6659 this.watchedProps.set(propStr, callback); 6660 } 6661 /** 6662 * This View @Provide's a variable under given name 6663 * Call this function from the constructor of the sub class 6664 * @param providedPropName either the variable name or the alias defined as 6665 * decorator param 6666 * @param store the backing store object for this variable (not the get/set variable!) 6667 */ 6668 addProvidedVar(providedPropName, store, allowOverride = false) { 6669 if (!allowOverride && this.findProvidePU(providedPropName)) { 6670 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.`); 6671 } 6672 store.setDecoratorInfo('@Provide'); 6673 this.providedVars_.set(providedPropName, store); 6674 } 6675 /* 6676 findProvidePU finds @Provided property recursively by traversing ViewPU's towards that of the UI tree root @Component: 6677 if 'this' ViewPU has a @Provide('providedPropName') return it, otherwise ask from its parent ViewPU. 6678 */ 6679 findProvidePU(providedPropName) { 6680 return this.providedVars_.get(providedPropName) || (this.parent_ && this.parent_.findProvidePU(providedPropName)); 6681 } 6682 /** 6683 * Method for the sub-class to call from its constructor for resolving 6684 * a @Consume variable and initializing its backing store 6685 * with the SyncedPropertyTwoWay<T> object created from the 6686 * @Provide variable's backing store. 6687 * @param providedPropName the name of the @Provide'd variable. 6688 * This is either the @Consume decorator parameter, or variable name. 6689 * @param consumeVarName the @Consume variable name (not the 6690 * @Consume decorator parameter) 6691 * @returns initializing value of the @Consume backing store 6692 */ 6693 initializeConsume(providedPropName, consumeVarName) { 6694 let providedVarStore = this.findProvidePU(providedPropName); 6695 if (providedVarStore === undefined) { 6696 throw new ReferenceError(`${this.debugInfo__()} missing @Provide property with name ${providedPropName}. 6697 Fail to resolve @Consume(${providedPropName}).`); 6698 } 6699 const factory = (source) => { 6700 const result = new SynchedPropertyTwoWayPU(source, this, consumeVarName); 6701 result.setDecoratorInfo('@Consume'); 6702 6703 return result; 6704 }; 6705 return providedVarStore.createSync(factory); 6706 } 6707 /** 6708 * given the elmtId of a child or child of child within this custom component 6709 * remember this component needs a partial update 6710 * @param elmtId 6711 */ 6712 markElemenDirtyById(elmtId) { 6713 // TODO ace-ets2bundle, framework, compiled apps need to update together 6714 // this function will be removed after a short transition period 6715 stateMgmtConsole.applicationError(`${this.debugInfo__()}: markElemenDirtyById no longer supported. 6716 Please update your ace-ets2bundle and recompile your application. Application error!`); 6717 } 6718 /** 6719 * For each recorded dirty Element in this custom component 6720 * run its update function 6721 * 6722 */ 6723 updateDirtyElements() { 6724 6725 do { 6726 6727 // see which elmtIds are managed by this View 6728 // and clean up all book keeping for them 6729 this.purgeDeletedElmtIds(); 6730 // process all elmtIds marked as needing update in ascending order. 6731 // ascending order ensures parent nodes will be updated before their children 6732 // prior cleanup ensure no already deleted Elements have their update func executed 6733 const dirtElmtIdsFromRootNode = Array.from(this.dirtDescendantElementIds_).sort(ViewPU.compareNumber); 6734 // if state changed during exec update lambda inside UpdateElement, then the dirty elmtIds will be added 6735 // to newly created this.dirtDescendantElementIds_ Set 6736 dirtElmtIdsFromRootNode.forEach(elmtId => { 6737 if (this.hasRecycleManager()) { 6738 this.UpdateElement(this.recycleManager_.proxyNodeId(elmtId)); 6739 } 6740 else { 6741 this.UpdateElement(elmtId); 6742 } 6743 this.dirtDescendantElementIds_.delete(elmtId); 6744 }); 6745 if (this.dirtDescendantElementIds_.size) { 6746 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!`); 6747 } 6748 } while (this.dirtDescendantElementIds_.size); 6749 6750 6751 } 6752 // executed on first render only 6753 // kept for backward compatibility with old ace-ets2bundle 6754 observeComponentCreation(compilerAssignedUpdateFunc) { 6755 if (this.isDeleting_) { 6756 stateMgmtConsole.error(`View ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation `); 6757 return; 6758 } 6759 const updateFunc = (elmtId, isFirstRender) => { 6760 6761 this.currentlyRenderedElmtIdStack_.push(elmtId); 6762 compilerAssignedUpdateFunc(elmtId, isFirstRender); 6763 this.currentlyRenderedElmtIdStack_.pop(); 6764 6765 }; 6766 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 6767 // in observeComponentCreation function we do not get info about the component name, in 6768 // observeComponentCreation2 we do. 6769 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc }); 6770 // add element id -> owning ViewPU 6771 UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this)); 6772 try { 6773 updateFunc(elmtId, /* is first render */ true); 6774 } 6775 catch (error) { 6776 // avoid the incompatible change that move set function before updateFunc. 6777 this.updateFuncByElmtId.delete(elmtId); 6778 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId); 6779 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${error.message}`); 6780 throw error; 6781 } 6782 } 6783 observeComponentCreation2(compilerAssignedUpdateFunc, classObject) { 6784 if (this.isDeleting_) { 6785 stateMgmtConsole.error(`View ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation2 `); 6786 return; 6787 } 6788 const _componentName = (classObject && ('name' in classObject)) ? Reflect.get(classObject, 'name') : 'unspecified UINode'; 6789 if (_componentName === '__Recycle__') { 6790 return; 6791 } 6792 const _popFunc = (classObject && 'pop' in classObject) ? classObject.pop : () => { }; 6793 const updateFunc = (elmtId, isFirstRender) => { 6794 var _a, _b; 6795 this.syncInstanceId(); 6796 6797 (_a = PUV2ViewBase.arkThemeScopeManager) === null || _a === void 0 ? void 0 : _a.onComponentCreateEnter(_componentName, elmtId, isFirstRender, this); 6798 ViewStackProcessor.StartGetAccessRecordingFor(elmtId); 6799 if (!this.isViewV3) { 6800 // Enable PU state tracking only in PU @Components 6801 this.currentlyRenderedElmtIdStack_.push(elmtId); 6802 stateMgmtDFX.inRenderingElementId.push(elmtId); 6803 } 6804 // if V2 @Observed/@Track used anywhere in the app (there is no more fine grained criteria), 6805 // enable V2 object deep observation 6806 // FIXME: A @Component should only use PU or V2 state, but ReactNative dynamic viewer uses both. 6807 if (this.isViewV3 || ConfigureStateMgmt.instance.needsV2Observe()) { 6808 // FIXME: like in V2 setting bindId_ in ObserveV2 does not work with 'stacked' 6809 // update + initial render calls, like in if and ForEach case, convert to stack as well 6810 ObserveV2.getObserve().startRecordDependencies(this, elmtId); 6811 } 6812 compilerAssignedUpdateFunc(elmtId, isFirstRender); 6813 if (!isFirstRender) { 6814 _popFunc(); 6815 } 6816 let node = this.getNodeById(elmtId); 6817 if (node !== undefined) { 6818 node.cleanStageValue(); 6819 } 6820 if (this.isViewV3 || ConfigureStateMgmt.instance.needsV2Observe()) { 6821 ObserveV2.getObserve().stopRecordDependencies(); 6822 } 6823 if (!this.isViewV3) { 6824 this.currentlyRenderedElmtIdStack_.pop(); 6825 stateMgmtDFX.inRenderingElementId.pop(); 6826 } 6827 ViewStackProcessor.StopGetAccessRecording(); 6828 (_b = PUV2ViewBase.arkThemeScopeManager) === null || _b === void 0 ? void 0 : _b.onComponentCreateExit(elmtId); 6829 6830 this.restoreInstanceId(); 6831 }; 6832 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 6833 // needs to move set before updateFunc. 6834 // make sure the key and object value exist since it will add node in attributeModifier during updateFunc. 6835 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc, classObject: classObject }); 6836 // add element id -> owning ViewPU 6837 UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this)); 6838 try { 6839 updateFunc(elmtId, /* is first render */ true); 6840 } 6841 catch (error) { 6842 // avoid the incompatible change that move set function before updateFunc. 6843 this.updateFuncByElmtId.delete(elmtId); 6844 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId); 6845 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${error.message}`); 6846 throw error; 6847 } 6848 6849 } 6850 getOrCreateRecycleManager() { 6851 if (!this.recycleManager_) { 6852 this.recycleManager_ = new RecycleManager; 6853 } 6854 return this.recycleManager_; 6855 } 6856 getRecycleManager() { 6857 return this.recycleManager_; 6858 } 6859 hasRecycleManager() { 6860 return !(this.recycleManager_ === undefined); 6861 } 6862 initRecycleManager() { 6863 if (this.recycleManager_) { 6864 stateMgmtConsole.error(`${this.debugInfo__()}: init recycleManager multiple times. Internal error.`); 6865 return; 6866 } 6867 this.recycleManager_ = new RecycleManager; 6868 } 6869 rebuildUpdateFunc(elmtId, compilerAssignedUpdateFunc) { 6870 const updateFunc = (elmtId, isFirstRender) => { 6871 this.currentlyRenderedElmtIdStack_.push(elmtId); 6872 compilerAssignedUpdateFunc(elmtId, isFirstRender); 6873 this.currentlyRenderedElmtIdStack_.pop(); 6874 }; 6875 if (this.updateFuncByElmtId.has(elmtId)) { 6876 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc }); 6877 } 6878 } 6879 /** 6880 * @function observeRecycleComponentCreation 6881 * @description custom node recycle creation 6882 * @param name custom node name 6883 * @param recycleUpdateFunc custom node recycle update which can be converted to a normal update function 6884 * @return void 6885 */ 6886 observeRecycleComponentCreation(name, recycleUpdateFunc) { 6887 // convert recycle update func to update func 6888 const compilerAssignedUpdateFunc = (element, isFirstRender) => { 6889 recycleUpdateFunc(element, isFirstRender, undefined); 6890 }; 6891 let node; 6892 // if there is no suitable recycle node, run a normal creation function. 6893 if (!this.hasRecycleManager() || !(node = this.getRecycleManager().popRecycleNode(name))) { 6894 6895 this.observeComponentCreation(compilerAssignedUpdateFunc); 6896 return; 6897 } 6898 // if there is a suitable recycle node, run a recycle update function. 6899 const newElmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 6900 const oldElmtId = node.id__(); 6901 this.recycleManager_.updateNodeId(oldElmtId, newElmtId); 6902 node.hasBeenRecycled_ = false; 6903 this.rebuildUpdateFunc(oldElmtId, compilerAssignedUpdateFunc); 6904 recycleUpdateFunc(oldElmtId, /* is first render */ true, node); 6905 } 6906 // param is used by BuilderNode 6907 aboutToReuseInternal(param) { 6908 this.runReuse_ = true; 6909 stateMgmtTrace.scopedTrace(() => { 6910 if (this.paramsGenerator_ && typeof this.paramsGenerator_ === 'function') { 6911 const params = param ? param : this.paramsGenerator_(); 6912 this.updateStateVars(params); 6913 this.aboutToReuse(params); 6914 } 6915 }, 'aboutToReuse', this.constructor.name); 6916 for (const stateLinkPropVar of this.ownObservedPropertiesStore_) { 6917 const changedElmtIds = stateLinkPropVar.moveElmtIdsForDelayedUpdate(true); 6918 if (changedElmtIds) { 6919 if (changedElmtIds.size && !this.isFirstRender()) { 6920 for (const elmtId of changedElmtIds) { 6921 this.dirtDescendantElementIds_.add(elmtId); 6922 } 6923 } 6924 } 6925 } 6926 if (!this.delayRecycleNodeRerender) { 6927 this.updateDirtyElements(); 6928 } 6929 else { 6930 this.flushDelayCompleteRerender(); 6931 } 6932 this.childrenWeakrefMap_.forEach((weakRefChild) => { 6933 const child = weakRefChild.deref(); 6934 if (child) { 6935 if (child instanceof ViewPU) { 6936 if (!child.hasBeenRecycled_) { 6937 child.aboutToReuseInternal(); 6938 } 6939 } 6940 else { 6941 // FIXME fix for mixed V2 - V3 Hierarchies 6942 throw new Error('aboutToReuseInternal: Recycle not implemented for ViewV2, yet'); 6943 } 6944 } // if child 6945 }); 6946 this.runReuse_ = false; 6947 } 6948 stopRecursiveRecycle() { 6949 this.preventRecursiveRecycle_ = true; 6950 } 6951 aboutToRecycleInternal() { 6952 this.runReuse_ = true; 6953 stateMgmtTrace.scopedTrace(() => { 6954 this.aboutToRecycle(); 6955 }, 'aboutToRecycle', this.constructor.name); 6956 if (this.preventRecursiveRecycle_) { 6957 this.preventRecursiveRecycle_ = false; 6958 return; 6959 } 6960 this.childrenWeakrefMap_.forEach((weakRefChild) => { 6961 const child = weakRefChild.deref(); 6962 if (child) { 6963 if (child instanceof ViewPU) { 6964 if (!child.hasBeenRecycled_) { 6965 child.aboutToRecycleInternal(); 6966 } 6967 } 6968 else { 6969 // FIXME fix for mixed V2 - V3 Hierarchies 6970 throw new Error('aboutToRecycleInternal: Recycle not yet implemented for ViewV2'); 6971 } 6972 } // if child 6973 }); 6974 this.runReuse_ = false; 6975 } 6976 // add current JS object to it's parent recycle manager 6977 recycleSelf(name) { 6978 if (this.getParent() && this.getParent() instanceof ViewPU && !this.getParent().isDeleting_) { 6979 const parentPU = this.getParent(); 6980 parentPU.getOrCreateRecycleManager().pushRecycleNode(name, this); 6981 this.hasBeenRecycled_ = true; 6982 } 6983 else { 6984 this.resetRecycleCustomNode(); 6985 } 6986 } 6987 isRecycled() { 6988 return this.hasBeenRecycled_; 6989 } 6990 UpdateLazyForEachElements(elmtIds) { 6991 if (!Array.isArray(elmtIds)) { 6992 return; 6993 } 6994 Array.from(elmtIds).sort(ViewPU.compareNumber).forEach((elmtId) => { 6995 const entry = this.updateFuncByElmtId.get(elmtId); 6996 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 6997 if (typeof updateFunc !== 'function') { 6998 6999 } 7000 else { 7001 this.isRenderInProgress = true; 7002 updateFunc(elmtId, false); 7003 this.finishUpdateFunc(elmtId); 7004 this.isRenderInProgress = false; 7005 } 7006 }); 7007 } 7008 /** 7009 * CreateStorageLink and CreateStorageLinkPU are used by the implementation of @StorageLink and 7010 * @LocalStotrageLink in full update and partial update solution respectively. 7011 * These are not part of the public AppStorage API , apps should not use. 7012 * @param storagePropName - key in LocalStorage 7013 * @param defaultValue - value to use when creating a new prop in the LocalStotage 7014 * @param owningView - the View/ViewPU owning the @StorageLink/@LocalStorageLink variable 7015 * @param viewVariableName - @StorageLink/@LocalStorageLink variable name 7016 * @returns SynchedPropertySimple/ObjectTwoWay/PU 7017 */ 7018 createStorageLink(storagePropName, defaultValue, viewVariableName) { 7019 const appStorageLink = AppStorage.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7020 ? undefined 7021 : new SynchedPropertyTwoWayPU(source, this, viewVariableName)); 7022 appStorageLink === null || appStorageLink === void 0 ? void 0 : appStorageLink.setDecoratorInfo('@StorageLink'); 7023 return appStorageLink; 7024 } 7025 createStorageProp(storagePropName, defaultValue, viewVariableName) { 7026 const appStorageProp = AppStorage.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7027 ? undefined 7028 : new SynchedPropertyOneWayPU(source, this, viewVariableName)); 7029 appStorageProp === null || appStorageProp === void 0 ? void 0 : appStorageProp.setDecoratorInfo('@StorageProp'); 7030 return appStorageProp; 7031 } 7032 createLocalStorageLink(storagePropName, defaultValue, viewVariableName) { 7033 const localStorageLink = this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7034 ? undefined 7035 : new SynchedPropertyTwoWayPU(source, this, viewVariableName)); 7036 localStorageLink === null || localStorageLink === void 0 ? void 0 : localStorageLink.setDecoratorInfo('@LocalStorageLink'); 7037 return localStorageLink; 7038 } 7039 createLocalStorageProp(storagePropName, defaultValue, viewVariableName) { 7040 const localStorageProp = this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 7041 ? undefined 7042 : new SynchedPropertyObjectOneWayPU(source, this, viewVariableName)); 7043 localStorageProp === null || localStorageProp === void 0 ? void 0 : localStorageProp.setDecoratorInfo('@LocalStorageProp'); 7044 return localStorageProp; 7045 } 7046 /** 7047 * onDumpInfo is used to process commands delivered by the hidumper process 7048 * @param commands - list of commands provided in the shell 7049 * @returns void 7050 */ 7051 onDumpInfo(commands) { 7052 let dfxCommands = this.processOnDumpCommands(commands); 7053 dfxCommands.forEach((command) => { 7054 let view = undefined; 7055 if (command.viewId) { 7056 view = this.findViewPUInHierarchy(command.viewId); 7057 if (!view) { 7058 DumpLog.print(0, `\nTarget view: ${command.viewId} not found for command: ${command.what}\n`); 7059 return; 7060 } 7061 } 7062 else { 7063 view = this; 7064 command.viewId = view.id__(); 7065 } 7066 switch (command.what) { 7067 case '-dumpAll': 7068 view.printDFXHeader('ViewPU Info', command); 7069 DumpLog.print(0, view.debugInfoView(command.isRecursive)); 7070 break; 7071 case '-viewHierarchy': 7072 view.printDFXHeader('ViewPU Hierarchy', command); 7073 DumpLog.print(0, view.debugInfoViewHierarchy(command.isRecursive)); 7074 break; 7075 case '-stateVariables': 7076 view.printDFXHeader('ViewPU State Variables', command); 7077 DumpLog.print(0, view.debugInfoStateVars()); 7078 break; 7079 case '-registeredElementIds': 7080 view.printDFXHeader('ViewPU Registered Element IDs', command); 7081 DumpLog.print(0, view.debugInfoUpdateFuncByElmtId(command.isRecursive)); 7082 break; 7083 case '-dirtyElementIds': 7084 view.printDFXHeader('ViewPU Dirty Registered Element IDs', command); 7085 DumpLog.print(0, view.debugInfoDirtDescendantElementIds(command.isRecursive)); 7086 break; 7087 case '-inactiveComponents': 7088 view.printDFXHeader('List of Inactive Components', command); 7089 DumpLog.print(0, view.debugInfoInactiveComponents()); 7090 break; 7091 case '-profiler': 7092 view.printDFXHeader('Profiler Info', command); 7093 view.dumpReport(); 7094 this.sendStateInfo('{}'); 7095 break; 7096 default: 7097 DumpLog.print(0, `\nUnsupported JS DFX dump command: [${command.what}, viewId=${command.viewId}, isRecursive=${command.isRecursive}]\n`); 7098 } 7099 }); 7100 } 7101 printDFXHeader(header, command) { 7102 let length = 50; 7103 let remainder = length - header.length < 0 ? 0 : length - header.length; 7104 DumpLog.print(0, `\n${'-'.repeat(remainder / 2)}${header}${'-'.repeat(remainder / 2)}`); 7105 DumpLog.print(0, `[${command.what}, viewId=${command.viewId}, isRecursive=${command.isRecursive}]\n`); 7106 } 7107 processOnDumpCommands(commands) { 7108 let isFlag = (param) => { 7109 return '-r'.match(param) != null || param.startsWith('-viewId='); 7110 }; 7111 let dfxCommands = []; 7112 for (var i = 0; i < commands.length; i++) { 7113 let command = commands[i]; 7114 if (isFlag(command)) { 7115 if (command.startsWith('-viewId=')) { 7116 let dfxCommand = dfxCommands[dfxCommands.length - 1]; 7117 if (dfxCommand) { 7118 let input = command.split('='); 7119 if (input[1]) { 7120 let viewId = Number.parseInt(input[1]); 7121 dfxCommand.viewId = Number.isNaN(viewId) ? UINodeRegisterProxy.notRecordingDependencies : viewId; 7122 } 7123 } 7124 } 7125 else if (command.match('-r')) { 7126 let dfxCommand = dfxCommands[dfxCommands.length - 1]; 7127 if (dfxCommand) { 7128 dfxCommand.isRecursive = true; 7129 } 7130 } 7131 } 7132 else { 7133 dfxCommands.push({ 7134 what: command, 7135 viewId: undefined, 7136 isRecursive: false, 7137 }); 7138 } 7139 } 7140 return dfxCommands; 7141 } 7142 findViewPUInHierarchy(id) { 7143 let weakChild = this.childrenWeakrefMap_.get(id); 7144 if (weakChild) { 7145 const child = weakChild.deref(); 7146 // found child with id, is it a ViewPU? 7147 return (child instanceof ViewPU) ? child : undefined; 7148 } 7149 // did not find, continue searching 7150 let retVal = undefined; 7151 for (const [key, value] of this.childrenWeakrefMap_.entries()) { 7152 retVal = value.deref().findViewPUInHierarchy(id); 7153 if (retVal) { 7154 break; 7155 } 7156 } 7157 return retVal; 7158 } 7159 debugInfoView(recursive = false) { 7160 return this.debugInfoViewInternal(recursive); 7161 } 7162 debugInfoViewInternal(recursive = false) { 7163 let retVal = `@Component\n${this.constructor.name}[${this.id__()}]`; 7164 retVal += `\n\nView Hierarchy:\n${this.debugInfoViewHierarchy(recursive)}`; 7165 retVal += `\n\nState variables:\n${this.debugInfoStateVars()}`; 7166 retVal += `\n\nRegistered Element IDs:\n${this.debugInfoUpdateFuncByElmtId(recursive)}`; 7167 retVal += `\n\nDirty Registered Element IDs:\n${this.debugInfoDirtDescendantElementIds(recursive)}`; 7168 return retVal; 7169 } 7170 debugInfoDirtDescendantElementIds(recursive = false) { 7171 return this.debugInfoDirtDescendantElementIdsInternal(0, recursive, { total: 0 }); 7172 } 7173 debugInfoDirtDescendantElementIdsInternal(depth = 0, recursive = false, counter) { 7174 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`; 7175 this.dirtDescendantElementIds_.forEach((value) => { 7176 retVaL += `${value}, `; 7177 }); 7178 counter.total += this.dirtDescendantElementIds_.size; 7179 retVaL += `\n${' '.repeat(depth + 1)}}[${this.dirtDescendantElementIds_.size}]`; 7180 if (recursive) { 7181 this.childrenWeakrefMap_.forEach((value, key, map) => { 7182 var _a; 7183 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoDirtDescendantElementIdsInternal(depth + 1, recursive, counter); 7184 }); 7185 } 7186 if (recursive && depth == 0) { 7187 retVaL += `\nTotal: ${counter.total}`; 7188 } 7189 return retVaL; 7190 } 7191 /** 7192 * onDumpInspector is invoked by native side to create Inspector tree including state variables 7193 * @returns dump info 7194 */ 7195 onDumpInspector() { 7196 let res = new DumpInfo(); 7197 res.viewInfo = { componentName: this.constructor.name, id: this.id__() }; 7198 Object.getOwnPropertyNames(this) 7199 .filter((varName) => varName.startsWith('__') && !varName.startsWith(ObserveV2.OB_PREFIX)) 7200 .forEach((varName) => { 7201 const prop = Reflect.get(this, varName); 7202 if (typeof prop === 'object' && 'debugInfoDecorator' in prop) { 7203 const observedProp = prop; 7204 res.observedPropertiesInfo.push(stateMgmtDFX.getObservedPropertyInfo(observedProp, false)); 7205 } 7206 }); 7207 let resInfo = ''; 7208 try { 7209 resInfo = JSON.stringify(res); 7210 } 7211 catch (error) { 7212 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in getInspector: ${error.message}`); 7213 } 7214 return resInfo; 7215 } 7216} // class ViewPU 7217/* 7218 * Copyright (c) 2023 Huawei Device Co., Ltd. 7219 * Licensed under the Apache License, Version 2.0 (the "License"); 7220 * you may not use this file except in compliance with the License. 7221 * You may obtain a copy of the License at 7222 * 7223 * http://www.apache.org/licenses/LICENSE-2.0 7224 * 7225 * Unless required by applicable law or agreed to in writing, software 7226 * distributed under the License is distributed on an "AS IS" BASIS, 7227 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7228 * See the License for the specific language governing permissions and 7229 * limitations under the License. 7230 * 7231 * * RecycleManager - Recycle cache manager 7232 * 7233* all definitions in this file are framework internal 7234*/ 7235/** 7236 * @class RecycleManager 7237 * @description manage the JS object cached of current node 7238 */ 7239class RecycleManager { 7240 constructor() { 7241 // key: recycle node name 7242 // value: recycle node JS object 7243 this.cachedRecycleNodes_ = undefined; 7244 this.biMap_ = undefined; 7245 this.cachedRecycleNodes_ = new Map(); 7246 this.biMap_ = new BidirectionalMap(); 7247 } 7248 updateNodeId(oldElmtId, newElmtId) { 7249 this.biMap_.delete(oldElmtId); 7250 this.biMap_.add([oldElmtId, newElmtId]); 7251 } 7252 proxyNodeId(oldElmtId) { 7253 const proxy = this.biMap_.get(oldElmtId); 7254 if (!proxy) { 7255 return oldElmtId; 7256 } 7257 return proxy; 7258 } 7259 pushRecycleNode(name, node) { 7260 var _a; 7261 if (!this.cachedRecycleNodes_.get(name)) { 7262 this.cachedRecycleNodes_.set(name, new Array()); 7263 } 7264 (_a = this.cachedRecycleNodes_.get(name)) === null || _a === void 0 ? void 0 : _a.push(node); 7265 } 7266 popRecycleNode(name) { 7267 var _a; 7268 return (_a = this.cachedRecycleNodes_.get(name)) === null || _a === void 0 ? void 0 : _a.pop(); 7269 } 7270 // When parent JS View is deleted, release all cached nodes 7271 purgeAllCachedRecycleNode() { 7272 this.cachedRecycleNodes_.forEach((nodes, _) => { 7273 nodes.forEach((node) => { 7274 node.resetRecycleCustomNode(); 7275 }); 7276 }); 7277 this.cachedRecycleNodes_.clear(); 7278 } 7279 // Set active status for all cached nodes 7280 setActive(active) { 7281 this.cachedRecycleNodes_.forEach((nodes, _) => { 7282 nodes.forEach((node) => { 7283 node.setActiveInternal(active); 7284 }); 7285 }); 7286 } 7287} 7288class BidirectionalMap { 7289 constructor() { 7290 this.fwdMap_ = undefined; 7291 this.revMap_ = undefined; 7292 this.fwdMap_ = new Map(); 7293 this.revMap_ = new Map(); 7294 } 7295 delete(key) { 7296 if (!this.fwdMap_[key]) { 7297 return; 7298 } 7299 const rev = this.fwdMap_[key]; 7300 this.fwdMap_.delete(key); 7301 this.revMap_.delete(rev); 7302 } 7303 get(key) { 7304 return this.fwdMap_[key] || this.revMap_[key]; 7305 } 7306 add(pair) { 7307 this.fwdMap_[pair[0]] = pair[1]; 7308 this.revMap_[pair[1]] = pair[0]; 7309 } 7310} 7311/* 7312 * Copyright (c) 2022 Huawei Device Co., Ltd. 7313 * Licensed under the Apache License, Version 2.0 (the "License"); 7314 * you may not use this file except in compliance with the License. 7315 * You may obtain a copy of the License at 7316 * 7317 * http://www.apache.org/licenses/LICENSE-2.0 7318 * 7319 * Unless required by applicable law or agreed to in writing, software 7320 * distributed under the License is distributed on an "AS IS" BASIS, 7321 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7322 * See the License for the specific language governing permissions and 7323 * limitations under the License. 7324 * 7325 * * ViewPU - View for Partial Update 7326 * 7327* all definitions in this file are framework internal 7328*/ 7329/** 7330 given parameters for calling a @Builder function 7331 this function wraps the Object of type T inside a ES6 Proxy. 7332 Each param, i.e. Object property is either a function or a value. 7333 If it is a function the function can either return a value of expected 7334 parameter type or an ObservedPropertyabstract<T> where T is the exected 7335 parameter type. The latter is the case when passing a state variable by 7336 reference. 7337 7338 Two purposes: 7339 1 - @Builder function boxy accesses params a '$$.paramA' 7340 However paramA can be a function, so to obtain the value the 7341 access would need to be '$$.param()' The proxy executes 7342 the function and return s the result 7343 2 - said function returns to ObservedPropertyAbstract backing store of 7344 a calling @Component state variable (whenever the state var is 7345 provided to the @Builder function). For this case the proxy can provide 7346 - the value by executing paramA() to return the ObservedPropertyAbstract 7347 and further (monitored!) get() to read its value 7348 - when requested to return '__param1' it returns the ObservedPropertyAbstract 7349 object. The scenario is to use to init a @Link source. 7350 */ 7351function makeBuilderParameterProxy(builderName, source) { 7352 return new Proxy(source, { 7353 set(target, prop, val) { 7354 throw Error(`@Builder '${builderName}': Invalid attempt to set(write to) parameter '${prop.toString()}' error!`); 7355 }, 7356 get(target, prop) { 7357 const prop1 = prop.toString().trim().startsWith('__') 7358 ? prop.toString().trim().substring(2) 7359 : prop.toString().trim(); 7360 7361 if (!(typeof target === 'object') && (prop1 in target)) { 7362 throw Error(`@Builder '${builderName}': '${prop1}' used but not a function parameter error!`); 7363 } 7364 const value = target[prop1]; 7365 if (typeof value !== 'function') { 7366 7367 return value; 7368 } 7369 const funcRet = value(); 7370 if ((typeof funcRet === 'object') && ('get' in funcRet)) { 7371 if (prop1 !== prop) { 7372 7373 return funcRet; 7374 } 7375 else { 7376 7377 const result = funcRet.get(); 7378 7379 return result; 7380 } 7381 } 7382 7383 return funcRet; 7384 } // get 7385 }); // new Proxy 7386} 7387/* 7388 * Copyright (c) 2024 Huawei Device Co., Ltd. 7389 * Licensed under the Apache License, Version 2.0 (the "License"); 7390 * you may not use this file except in compliance with the License. 7391 * You may obtain a copy of the License at 7392 * 7393 * http://www.apache.org/licenses/LICENSE-2.0 7394 * 7395 * Unless required by applicable law or agreed to in writing, software 7396 * distributed under the License is distributed on an "AS IS" BASIS, 7397 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7398 * See the License for the specific language governing permissions and 7399 * limitations under the License. 7400 */ 7401/** 7402 * Common Proxy handler for objects and dates for both decorators and makeObserved 7403 */ 7404class ObjectProxyHandler { 7405 constructor(isMakeObserved = false) { 7406 this.isMakeObserved_ = isMakeObserved; 7407 } 7408 // decorators work on object that holds the dependencies directly 7409 // makeObserved can't modify the object itself, so it creates a 7410 // wrapper object around it and that will hold the references 7411 // 7412 // this function is used to get the correct object that can be observed 7413 getTarget(obj) { 7414 return this.isMakeObserved_ ? RefInfo.get(obj) : obj; 7415 } 7416 get(target, key, receiver) { 7417 if (typeof key === 'symbol') { 7418 if (key === Symbol.iterator) { 7419 const conditionalTarget = this.getTarget(target); 7420 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7421 return (...args) => target[key](...args); 7422 } 7423 if (key === ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7424 return target; 7425 } 7426 if (this.isMakeObserved_ && key === ObserveV2.SYMBOL_MAKE_OBSERVED) { 7427 return true; 7428 } 7429 return target[key]; 7430 } 7431 7432 const conditionalTarget = this.getTarget(target); 7433 // makeObserved logic adds wrapper proxy later 7434 let ret = this.isMakeObserved_ ? target[key] : ObserveV2.autoProxyObject(target, key); 7435 // do not addref for function type, it will make such huge unnecessary dependency collection 7436 // for some common function attributes, e.g. toString etc. 7437 if (typeof (ret) !== 'function') { 7438 ObserveV2.getObserve().addRef(conditionalTarget, key); 7439 return (typeof (ret) === 'object' && this.isMakeObserved_) ? RefInfo.get(ret).proxy : ret; 7440 } 7441 if (target instanceof Date) { 7442 if (ObjectProxyHandler.dateSetFunctions.has(key)) { 7443 return function (...args) { 7444 // execute original function with given arguments 7445 let result = ret.call(this, ...args); 7446 ObserveV2.getObserve().fireChange(conditionalTarget, ObjectProxyHandler.OB_DATE); 7447 return result; 7448 // bind 'this' to target inside the function 7449 }.bind(target); 7450 } 7451 else { 7452 ObserveV2.getObserve().addRef(conditionalTarget, ObjectProxyHandler.OB_DATE); 7453 } 7454 return ret.bind(target); 7455 } 7456 // function 7457 return ret.bind(receiver); 7458 } 7459 set(target, key, value) { 7460 if (typeof key === 'symbol') { 7461 if (!this.isMakeObserved_ && key !== ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7462 target[key] = value; 7463 } 7464 return true; 7465 } 7466 if (target[key] === value) { 7467 return true; 7468 } 7469 target[key] = value; 7470 ObserveV2.getObserve().fireChange(this.getTarget(target), key.toString()); 7471 return true; 7472 } 7473} 7474ObjectProxyHandler.OB_DATE = '__date__'; 7475ObjectProxyHandler.dateSetFunctions = new Set(['setFullYear', 'setMonth', 'setDate', 'setHours', 'setMinutes', 7476 'setSeconds', 'setMilliseconds', 'setTime', 'setUTCFullYear', 'setUTCMonth', 'setUTCDate', 'setUTCHours', 7477 'setUTCMinutes', 'setUTCSeconds', 'setUTCMilliseconds']); 7478; 7479/** 7480 * Common Proxy handler for Arrays for both decorators and makeObserved 7481 */ 7482class ArrayProxyHandler { 7483 constructor(isMakeObserved = false) { 7484 this.isMakeObserved_ = isMakeObserved; 7485 } 7486 // decorators work on object that holds the dependencies directly 7487 // makeObserved can't modify the object itself, so it creates a 7488 // wrapper object around it and that will hold the references 7489 // 7490 // this function is used to get the correct object that can be observed 7491 getTarget(obj) { 7492 return this.isMakeObserved_ ? RefInfo.get(obj) : obj; 7493 } 7494 get(target, key, receiver) { 7495 if (typeof key === 'symbol') { 7496 if (key === Symbol.iterator) { 7497 const conditionalTarget = this.getTarget(target); 7498 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7499 return (...args) => target[key](...args); 7500 } 7501 if (key === ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7502 return target; 7503 } 7504 if (this.isMakeObserved_ && key === ObserveV2.SYMBOL_MAKE_OBSERVED) { 7505 return true; 7506 } 7507 return target[key]; 7508 } 7509 7510 const conditionalTarget = this.getTarget(target); 7511 if (key === 'length') { 7512 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7513 return target[key]; 7514 } 7515 // makeObserved logic adds wrapper proxy later 7516 let ret = this.isMakeObserved_ ? target[key] : ObserveV2.autoProxyObject(target, key); 7517 if (typeof (ret) !== 'function') { 7518 ObserveV2.getObserve().addRef(conditionalTarget, key); 7519 return (typeof (ret) === 'object' && this.isMakeObserved_) ? RefInfo.get(ret).proxy : ret; 7520 } 7521 if (ArrayProxyHandler.arrayMutatingFunctions.has(key)) { 7522 return function (...args) { 7523 ret.call(target, ...args); 7524 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7525 // returning the 'receiver(proxied object)' ensures that when chain calls also 2nd function call 7526 // operates on the proxied object. 7527 return receiver; 7528 }; 7529 } 7530 else if (ArrayProxyHandler.arrayLengthChangingFunctions.has(key)) { 7531 return function (...args) { 7532 const result = ret.call(target, ...args); 7533 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7534 return result; 7535 }; 7536 } 7537 else if (!SendableType.isArray(target)) { 7538 return ret.bind(receiver); 7539 } 7540 else if (key === 'forEach') { 7541 // to make ForEach Component and its Item can addref 7542 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7543 return function (callbackFn) { 7544 const result = ret.call(target, (value, index, array) => { 7545 // Collections.Array will report BusinessError: The foreach cannot be bound if call "receiver". 7546 // because the passed parameter is not the instance of the container class. 7547 // so we must call "target" here to deal with the collections situations. 7548 // But we also need to addref for each index. 7549 ObserveV2.getObserve().addRef(conditionalTarget, index.toString()); 7550 callbackFn(typeof value === 'object' ? RefInfo.get(value).proxy : value, index, receiver); 7551 }); 7552 return result; 7553 }; 7554 } 7555 else { 7556 return ret.bind(target); // SendableArray can't be bound -> functions not observed 7557 } 7558 } 7559 set(target, key, value) { 7560 if (typeof key === 'symbol') { 7561 if (!this.isMakeObserved_ && key !== ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7562 target[key] = value; 7563 } 7564 return true; 7565 } 7566 if (target[key] === value) { 7567 return true; 7568 } 7569 const originalLength = target.length; 7570 target[key] = value; 7571 const arrayLenChanged = target.length !== originalLength; 7572 ObserveV2.getObserve().fireChange(this.getTarget(target), arrayLenChanged ? ObserveV2.OB_LENGTH : key.toString()); 7573 return true; 7574 } 7575} 7576// shrinkTo and extendTo is collection.Array api. 7577ArrayProxyHandler.arrayLengthChangingFunctions = new Set(['push', 'pop', 'shift', 'splice', 'unshift', 'shrinkTo', 'extendTo']); 7578ArrayProxyHandler.arrayMutatingFunctions = new Set(['copyWithin', 'fill', 'reverse', 'sort']); 7579; 7580/** 7581 * Common Proxy handler for Maps and Sets for both decorators and makeObserved 7582 */ 7583class SetMapProxyHandler { 7584 constructor(isMakeObserved = false) { 7585 this.isMakeObserved_ = isMakeObserved; 7586 } 7587 // decorators work on object that holds the dependencies directly 7588 // makeObserved can't modify the object itself, so it creates a 7589 // wrapper object around it and that will hold the references 7590 // 7591 // this function is used to get the correct object that can be observed 7592 getTarget(obj) { 7593 return this.isMakeObserved_ ? RefInfo.get(obj) : obj; 7594 } 7595 get(target, key, receiver) { 7596 if (typeof key === 'symbol') { 7597 if (key === Symbol.iterator) { 7598 const conditionalTarget = this.getTarget(target); 7599 ObserveV2.getObserve().fireChange(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7600 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7601 return (...args) => target[key](...args); 7602 } 7603 if (key === ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7604 return target; 7605 } 7606 if (this.isMakeObserved_ && key === ObserveV2.SYMBOL_MAKE_OBSERVED) { 7607 return true; 7608 } 7609 return target[key]; 7610 } 7611 7612 const conditionalTarget = this.getTarget(target); 7613 if (key === 'size') { 7614 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7615 return target[key]; 7616 } 7617 // makeObserved logic adds wrapper proxy later 7618 let ret = this.isMakeObserved_ ? target[key] : ObserveV2.autoProxyObject(target, key); 7619 if (typeof (ret) !== 'function') { 7620 ObserveV2.getObserve().addRef(conditionalTarget, key); 7621 return (typeof (ret) === 'object' && this.isMakeObserved_) ? RefInfo.get(ret).proxy : ret; 7622 } 7623 if (key === 'has') { 7624 return (prop) => { 7625 const ret = target.has(prop); 7626 if (ret) { 7627 ObserveV2.getObserve().addRef(conditionalTarget, prop); 7628 } 7629 else { 7630 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7631 } 7632 return ret; 7633 }; 7634 } 7635 if (key === 'delete') { 7636 return (prop) => { 7637 if (target.has(prop)) { 7638 ObserveV2.getObserve().fireChange(conditionalTarget, prop); 7639 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7640 return target.delete(prop); 7641 } 7642 else { 7643 return false; 7644 } 7645 }; 7646 } 7647 if (key === 'clear') { 7648 return () => { 7649 if (target.size > 0) { 7650 target.forEach((_, prop) => { 7651 ObserveV2.getObserve().fireChange(conditionalTarget, prop.toString()); 7652 }); 7653 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7654 ObserveV2.getObserve().addRef(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7655 target.clear(); 7656 } 7657 }; 7658 } 7659 if (key === 'keys' || key === 'values' || key === 'entries') { 7660 return () => { 7661 ObserveV2.getObserve().addRef(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7662 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7663 return target[key](); 7664 }; 7665 } 7666 if (target instanceof Set || (this.isMakeObserved_ && SendableType.isSet(target))) { 7667 if (key === 'add') { 7668 return (val) => { 7669 ObserveV2.getObserve().fireChange(conditionalTarget, val.toString()); 7670 ObserveV2.getObserve().fireChange(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7671 if (!target.has(val)) { 7672 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7673 target.add(val); 7674 } 7675 return receiver; 7676 }; 7677 } 7678 if (key === 'forEach') { 7679 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7680 return function (callbackFn) { 7681 // need to execute it target because it is the internal function for build-in type, and proxy does not have such slot. 7682 // if necessary, addref for each item in Set and also wrap proxy for makeObserved if it is Object. 7683 // currently, just execute it in target because there is no Component need to iterate Set, only Array 7684 const result = ret.call(target, callbackFn); 7685 return result; 7686 }; 7687 } 7688 // Bind to receiver ==> functions are observed 7689 return (typeof ret === 'function') ? ret.bind(receiver) : ret; 7690 } 7691 if (target instanceof Map || (this.isMakeObserved_ && SendableType.isMap(target))) { 7692 if (key === 'get') { 7693 return (prop) => { 7694 if (target.has(prop)) { 7695 ObserveV2.getObserve().addRef(conditionalTarget, prop); 7696 } 7697 else { 7698 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7699 } 7700 let item = target.get(prop); 7701 return (typeof item === 'object' && this.isMakeObserved_) ? RefInfo.get(item).proxy : item; 7702 }; 7703 } 7704 if (key === 'set') { 7705 return (prop, val) => { 7706 if (!target.has(prop)) { 7707 ObserveV2.getObserve().fireChange(conditionalTarget, ObserveV2.OB_LENGTH); 7708 } 7709 else if (target.get(prop) !== val) { 7710 ObserveV2.getObserve().fireChange(conditionalTarget, prop); 7711 } 7712 ObserveV2.getObserve().fireChange(conditionalTarget, SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY); 7713 target.set(prop, val); 7714 return receiver; 7715 }; 7716 } 7717 if (key === 'forEach') { 7718 ObserveV2.getObserve().addRef(conditionalTarget, ObserveV2.OB_LENGTH); 7719 return function (callbackFn) { 7720 // need to execute it target because it is the internal function for build-in type, and proxy does not have such slot. 7721 // if necessary, addref for each item in Map and also wrap proxy for makeObserved if it is Object. 7722 // currently, just execute it in target because there is no Component need to iterate Map, only Array 7723 const result = ret.call(target, callbackFn); 7724 return result; 7725 }; 7726 } 7727 } 7728 // Bind to receiver ==> functions are observed 7729 return (typeof ret === 'function') ? ret.bind(receiver) : ret; 7730 } 7731 set(target, key, value) { 7732 if (typeof key === 'symbol') { 7733 if (!this.isMakeObserved_ && key !== ObserveV2.SYMBOL_PROXY_GET_TARGET) { 7734 target[key] = value; 7735 } 7736 return true; 7737 } 7738 if (target[key] === value) { 7739 return true; 7740 } 7741 target[key] = value; 7742 ObserveV2.getObserve().fireChange(this.getTarget(target), key.toString()); 7743 return true; 7744 } 7745} 7746SetMapProxyHandler.OB_MAP_SET_ANY_PROPERTY = '___ob_map_set'; 7747; 7748/* 7749 * Copyright (c) 2024 Huawei Device Co., Ltd. 7750 * Licensed under the Apache License, Version 2.0 (the "License"); 7751 * you may not use this file except in compliance with the License. 7752 * You may obtain a copy of the License at 7753 * 7754 * http://www.apache.org/licenses/LICENSE-2.0 7755 * 7756 * Unless required by applicable law or agreed to in writing, software 7757 * distributed under the License is distributed on an "AS IS" BASIS, 7758 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7759 * See the License for the specific language governing permissions and 7760 * limitations under the License. 7761 */ 7762/** 7763 * 7764 * This file includes only framework internal classes and functions 7765 * non are part of SDK. Do not access from app. 7766 * 7767 * 7768 * ObserveV2 is the singleton object for observing state variable access and 7769 * change 7770 */ 7771// in the case of ForEach, Repeat, AND If, two or more UINodes / elmtIds can render at the same time 7772// e.g. ForEach -> ForEach child Text, Repeat -> Nested Repeat, child Text 7773// Therefore, ObserveV2 needs to keep a strack of currently renderign ids / components 7774// in the same way as thsi is also done for PU stateMgmt with ViewPU.currentlyRenderedElmtIdStack_ 7775class StackOfRenderedComponents { 7776 constructor() { 7777 this.stack_ = new Array(); 7778 } 7779 push(id, cmp) { 7780 this.stack_.push(new StackOfRenderedComponentsItem(id, cmp)); 7781 } 7782 pop() { 7783 const item = this.stack_.pop(); 7784 return item ? [item.id_, item.cmp_] : undefined; 7785 } 7786 top() { 7787 if (this.stack_.length) { 7788 const item = this.stack_[this.stack_.length - 1]; 7789 return [item.id_, item.cmp_]; 7790 } 7791 else { 7792 return undefined; 7793 } 7794 } 7795} 7796class StackOfRenderedComponentsItem { 7797 constructor(id, cmp) { 7798 this.id_ = id; 7799 this.cmp_ = cmp; 7800 } 7801} 7802class ObserveV2 { 7803 constructor() { 7804 // see MonitorV2.observeObjectAccess: bindCmp is the MonitorV2 7805 // see modified ViewV2 and ViewPU observeComponentCreation, bindCmp is the ViewV2 or ViewPU 7806 // bindId: UINode elmtId or watchId, depending on what is being observed 7807 this.stackOfRenderedComponents_ = new StackOfRenderedComponents(); 7808 // Map bindId to WeakRef<ViewPU> | MonitorV2 7809 this.id2cmp_ = {}; 7810 // Map bindId -> Set of @observed class objects 7811 // reverse dependency map for quickly removing all dependencies of a bindId 7812 this.id2targets_ = {}; 7813 // queued up Set of bindId 7814 // elmtIds of UINodes need re-render 7815 // @monitor functions that need to execute 7816 this.elmtIdsChanged_ = new Set(); 7817 this.computedPropIdsChanged_ = new Set(); 7818 this.monitorIdsChanged_ = new Set(); 7819 this.persistenceChanged_ = new Set(); 7820 // avoid recursive execution of updateDirty 7821 // by state changes => fireChange while 7822 // UINode rerender or @monitor function execution 7823 this.startDirty_ = false; 7824 // flag to indicate change observation is disabled 7825 this.disabled_ = false; 7826 // flag to indicate ComputedV2 calculation is ongoing 7827 this.calculatingComputedProp_ = false; 7828 } 7829 static getObserve() { 7830 if (!this.obsInstance_) { 7831 this.obsInstance_ = new ObserveV2(); 7832 } 7833 return this.obsInstance_; 7834 } 7835 // return true given value is @observed object 7836 static IsObservedObjectV2(value) { 7837 return (value && typeof (value) === 'object' && value[ObserveV2.V2_DECO_META]); 7838 } 7839 // return true if given value is proxied observed object, either makeObserved or autoProxyObject 7840 static IsProxiedObservedV2(value) { 7841 return (value && typeof value === 'object' && value[ObserveV2.SYMBOL_PROXY_GET_TARGET]); 7842 } 7843 // return true given value is the return value of makeObserved 7844 static IsMakeObserved(value) { 7845 return (value && typeof (value) === 'object' && value[ObserveV2.SYMBOL_MAKE_OBSERVED]); 7846 } 7847 static getCurrentRecordedId() { 7848 const bound = ObserveV2.getObserve().stackOfRenderedComponents_.top(); 7849 return bound ? bound[0] : -1; 7850 } 7851 // At the start of observeComponentCreation or 7852 // MonitorV2 observeObjectAccess 7853 startRecordDependencies(cmp, id, doClearBinding = true) { 7854 if (cmp != null) { 7855 doClearBinding && this.clearBinding(id); 7856 this.stackOfRenderedComponents_.push(id, cmp); 7857 } 7858 } 7859 // At the start of observeComponentCreation or 7860 // MonitorV2 observeObjectAccess 7861 stopRecordDependencies() { 7862 const bound = this.stackOfRenderedComponents_.pop(); 7863 if (bound === undefined) { 7864 stateMgmtConsole.error('stopRecordDependencies finds empty stack. Internal error!'); 7865 return; 7866 } 7867 let targetsSet; 7868 if ((targetsSet = this.id2targets_[bound[0]]) !== undefined && targetsSet.size) { 7869 // only add IView | MonitorV2 | ComputedV2 if at least one dependency was 7870 // recorded when rendering this ViewPU/ViewV2/Monitor/ComputedV2 7871 // ViewPU is the likely case where no dependecy gets recorded 7872 // for others no dependencies are unlikely to happen 7873 this.id2cmp_[bound[0]] = new WeakRef(bound[1]); 7874 } 7875 } 7876 // clear any previously created dependency view model object to elmtId 7877 // find these view model objects with the reverse map id2targets_ 7878 clearBinding(id) { 7879 var _a; 7880 // multiple weakRefs might point to the same target - here we get Set of unique targets 7881 const targetSet = new Set(); 7882 (_a = this.id2targets_[id]) === null || _a === void 0 ? void 0 : _a.forEach((weak) => { 7883 if (weak.deref() instanceof Object) { 7884 targetSet.add(weak.deref()); 7885 } 7886 }); 7887 targetSet.forEach((target) => { 7888 var _a, _b; 7889 const idRefs = target[ObserveV2.ID_REFS]; 7890 const symRefs = target[ObserveV2.SYMBOL_REFS]; 7891 if (idRefs) { 7892 (_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); }); 7893 delete idRefs[id]; 7894 } 7895 else { 7896 for (let key in symRefs) { 7897 (_b = symRefs[key]) === null || _b === void 0 ? void 0 : _b.delete(id); 7898 } 7899 ; 7900 } 7901 }); 7902 delete this.id2targets_[id]; 7903 delete this.id2cmp_[id]; 7904 7905 7906 } 7907 /** 7908 * 7909 * this cleanUpId2CmpDeadReferences() 7910 * id2cmp is a 'map' object id => WeakRef<Object> where object is ViewV2, ViewPU, MonitorV2 or ComputedV2 7911 * This method iterates over the object entries and deleted all those entries whose value can no longer 7912 * be deref'ed. 7913 * 7914 * cleanUpId2TargetsDeadReferences() 7915 * is2targets is a 'map' object id => Set<WeakRef<Object>> 7916 * the method traverses over the object entries and for each value of type 7917 * Set<WeakRef<Object>> removes all those items from the set that can no longer be deref'ed. 7918 * 7919 * According to JS specifications, it is up to ArlTS runtime GC implementation when to collect unreferences objects. 7920 * Parameters such as available memory, ArkTS processing load, number and size of all JS objects for GC collection 7921 * can impact the time delay between an object loosing last reference and GC collecting this object. 7922 * 7923 * WeakRef deref() returns the object until GC has collected it. 7924 * The id2cmp and is2targets cleanup herein depends on WeakRef.deref() to return undefined, i.e. it depends on GC 7925 * collecting 'cmp' or 'target' objects. Only then the algorithm can remove the entry from id2cmp / from id2target. 7926 * It is therefore to be expected behavior that these map objects grow and they a contain a larger number of 7927 * MonitorV2, ComputedV2, and/or view model @Observed class objects that are no longer used / referenced by the application. 7928 * Only after ArkTS runtime GC has collected them, this function is able to clean up the id2cmp and is2targets. 7929 * 7930 * This cleanUpDeadReferences() function gets called from UINodeRegisterProxy.uiNodeCleanUpIdleTask() 7931 * 7932 */ 7933 cleanUpDeadReferences() { 7934 this.cleanUpId2CmpDeadReferences(); 7935 this.cleanUpId2TargetsDeadReferences(); 7936 } 7937 cleanUpId2CmpDeadReferences() { 7938 7939 for (const id in this.id2cmp_) { 7940 7941 let weakRef = this.id2cmp_[id]; 7942 if (weakRef && typeof weakRef === 'object' && 'deref' in weakRef && weakRef.deref() === undefined) { 7943 7944 delete this.id2cmp_[id]; 7945 } 7946 } 7947 } 7948 cleanUpId2TargetsDeadReferences() { 7949 for (const id in this.id2targets_) { 7950 const targetSet = this.id2targets_[id]; 7951 if (targetSet && targetSet instanceof Set) { 7952 for (let weakTarget of targetSet) { 7953 if (weakTarget.deref() === undefined) { 7954 7955 targetSet.delete(weakTarget); 7956 } 7957 } // for targetSet 7958 } 7959 } // for id2targets_ 7960 } 7961 /** 7962 * counts number of WeakRef<Object> entries in id2cmp_ 'map' object 7963 * @returns total count and count of WeakRefs that can be deref'ed 7964 * Methods only for testing 7965 */ 7966 get id2CompDeRefSize() { 7967 let totalCount = 0; 7968 let aliveCount = 0; 7969 let comp; 7970 for (const id in this.id2cmp_) { 7971 totalCount++; 7972 let weakRef = this.id2cmp_[id]; 7973 if (weakRef && 'deref' in weakRef && (comp = weakRef.deref()) && comp instanceof Object) { 7974 aliveCount++; 7975 } 7976 } 7977 return [totalCount, aliveCount]; 7978 } 7979 /** counts number of target WeakRef<object> entries in all the Sets inside id2targets 'map' object 7980 * @returns total count and those can be dereferenced 7981 * Methods only for testing 7982 */ 7983 get id2TargetsDerefSize() { 7984 let totalCount = 0; 7985 let aliveCount = 0; 7986 for (const id in this.id2targets_) { 7987 const targetSet = this.id2targets_[id]; 7988 if (targetSet && targetSet instanceof Set) { 7989 for (let weakTarget of targetSet) { 7990 totalCount++; 7991 if (weakTarget.deref()) { 7992 aliveCount++; 7993 } 7994 } // for targetSet 7995 } 7996 } // for id2targets_ 7997 return [totalCount, aliveCount]; 7998 } 7999 // add dependency view model object 'target' property 'attrName' 8000 // to current this.bindId 8001 addRef(target, attrName) { 8002 const bound = this.stackOfRenderedComponents_.top(); 8003 if (!bound) { 8004 return; 8005 } 8006 if (bound[0] === UINodeRegisterProxy.monitorIllegalV2V3StateAccess) { 8007 const error = `${attrName}: ObserveV2.addRef: trying to use V3 state '${attrName}' to init/update child V2 @Component. Application error`; 8008 stateMgmtConsole.applicationError(error); 8009 throw new TypeError(error); 8010 } 8011 8012 this.addRef4IdInternal(bound[0], target, attrName); 8013 } 8014 addRef4Id(id, target, attrName) { 8015 8016 this.addRef4IdInternal(id, target, attrName); 8017 } 8018 addRef4IdInternal(id, target, attrName) { 8019 var _a, _b, _c, _d; 8020 var _e, _f; 8021 // Map: attribute/symbol -> dependent id 8022 const symRefs = (_a = target[_e = ObserveV2.SYMBOL_REFS]) !== null && _a !== void 0 ? _a : (target[_e] = {}); 8023 (_b = symRefs[attrName]) !== null && _b !== void 0 ? _b : (symRefs[attrName] = new Set()); 8024 symRefs[attrName].add(id); 8025 // Map id -> attribute/symbol 8026 // optimization for faster clearBinding 8027 const idRefs = target[ObserveV2.ID_REFS]; 8028 if (idRefs) { 8029 (_c = idRefs[id]) !== null && _c !== void 0 ? _c : (idRefs[id] = new Set()); 8030 idRefs[id].add(attrName); 8031 } 8032 const targetSet = (_d = (_f = this.id2targets_)[id]) !== null && _d !== void 0 ? _d : (_f[id] = new Set()); 8033 targetSet.add(new WeakRef(target)); 8034 } 8035 /** 8036 * 8037 * @param target set tracked attribute to new value without notifying the change 8038 * !! use with caution !! 8039 * @param attrName 8040 * @param newValue 8041 */ 8042 setUnmonitored(target, attrName, newValue) { 8043 const storeProp = ObserveV2.OB_PREFIX + attrName; 8044 if (storeProp in target) { 8045 // @track attrName 8046 8047 target[storeProp] = newValue; 8048 } 8049 else { 8050 8051 // untracked attrName 8052 target[attrName] = newValue; 8053 } 8054 } 8055 /** 8056 * Execute given task while state change observation is disabled 8057 * A state mutation caused by the task will NOT trigger UI rerender 8058 * and @monitor function execution. 8059 * 8060 * !!! Use with Caution !!! 8061 * 8062 * @param task a function to execute without monitoring state changes 8063 * @returns task function return value 8064 */ 8065 executeUnobserved(task) { 8066 8067 this.disabled_ = true; 8068 let ret; 8069 try { 8070 ret = task(); 8071 } 8072 catch (e) { 8073 stateMgmtConsole.applicationError(`executeUnobserved - task execution caused error ${e} !`); 8074 } 8075 this.disabled_ = false; 8076 8077 return ret; 8078 } 8079 // mark view model object 'target' property 'attrName' as changed 8080 // notify affected watchIds and elmtIds 8081 fireChange(target, attrName) { 8082 // enable to get more fine grained traces 8083 // including 2 (!) .end calls. 8084 if (!target[ObserveV2.SYMBOL_REFS] || this.disabled_) { 8085 return; 8086 } 8087 const bound = this.stackOfRenderedComponents_.top(); 8088 if (this.calculatingComputedProp_) { 8089 const prop = bound ? bound[1].getProp() : 'unknown computed property'; 8090 const error = `Usage of ILLEGAL @Computed function detected for ${prop}! The @Computed function MUST NOT change the state of any observed state variable!`; 8091 stateMgmtConsole.applicationError(error); 8092 throw new Error(error); 8093 } 8094 // enable this trace marker for more fine grained tracing of the update pipeline 8095 // note: two (!) end markers need to be enabled 8096 let changedIdSet = target[ObserveV2.SYMBOL_REFS][attrName]; 8097 if (!changedIdSet || !(changedIdSet instanceof Set)) { 8098 return; 8099 } 8100 8101 for (const id of changedIdSet) { 8102 // Cannot fireChange the object that is being created. 8103 if (bound && id === bound[0]) { 8104 continue; 8105 } 8106 // if this is the first id to be added to any Set of changed ids, 8107 // schedule an 'updateDirty' task 8108 // that will run after the current call stack has unwound. 8109 // purpose of check for startDirty_ is to avoid going into recursion. This could happen if 8110 // exec a re-render or exec a monitor function changes some state -> calls fireChange -> ... 8111 if ((this.elmtIdsChanged_.size + this.monitorIdsChanged_.size + this.computedPropIdsChanged_.size === 0) && 8112 /* update not already in progress */ !this.startDirty_) { 8113 Promise.resolve() 8114 .then(this.updateDirty.bind(this)) 8115 .catch(error => { 8116 stateMgmtConsole.applicationError(`Exception occurred during the update process involving @Computed properties, @Monitor functions or UINode re-rendering`, error); 8117 _arkUIUncaughtPromiseError(error); 8118 }); 8119 } 8120 // add bindId to the correct Set of pending changes. 8121 if (id < ComputedV2.MIN_COMPUTED_ID) { 8122 this.elmtIdsChanged_.add(id); 8123 } 8124 else if (id < MonitorV2.MIN_WATCH_ID) { 8125 this.computedPropIdsChanged_.add(id); 8126 } 8127 else if (id < PersistenceV2Impl.MIN_PERSISTENCE_ID) { 8128 this.monitorIdsChanged_.add(id); 8129 } 8130 else { 8131 this.persistenceChanged_.add(id); 8132 } 8133 } // for 8134 } 8135 updateDirty() { 8136 this.startDirty_ = true; 8137 this.updateDirty2(false); 8138 this.startDirty_ = false; 8139 } 8140 /** 8141 * execute /update in this order 8142 * - @Computed variables 8143 * - @Monitor functions 8144 * - UINode re-render 8145 * three nested loops, means: 8146 * process @Computed until no more @Computed need update 8147 * process @Monitor until no more @Computed and @Monitor 8148 * process UINode update until no more @Computed and @Monitor and UINode rerender 8149 * 8150 * @param updateUISynchronously should be set to true if called during VSYNC only 8151 * 8152 */ 8153 updateDirty2(updateUISynchronously = false) { 8154 aceTrace.begin('updateDirty2'); 8155 8156 // obtain and unregister the removed elmtIds 8157 UINodeRegisterProxy.obtainDeletedElmtIds(); 8158 UINodeRegisterProxy.unregisterElmtIdsFromIViews(); 8159 // priority order of processing: 8160 // 1- update computed properties until no more need computed props update 8161 // 2- update monitors until no more monitors and no more computed props 8162 // 3- update UINodes until no more monitors, no more computed props, and no more UINodes 8163 // FIXME prevent infinite loops 8164 do { 8165 do { 8166 while (this.computedPropIdsChanged_.size) { 8167 // sort the ids and update in ascending order 8168 // If a @Computed property depends on other @Computed properties, their 8169 // ids will be smaller as they are defined first. 8170 const computedProps = Array.from(this.computedPropIdsChanged_).sort((id1, id2) => id1 - id2); 8171 this.computedPropIdsChanged_ = new Set(); 8172 this.updateDirtyComputedProps(computedProps); 8173 } 8174 if (this.persistenceChanged_.size) { 8175 const persistKeys = Array.from(this.persistenceChanged_); 8176 this.persistenceChanged_ = new Set(); 8177 PersistenceV2Impl.instance().onChangeObserved(persistKeys); 8178 } 8179 if (this.monitorIdsChanged_.size) { 8180 const monitors = this.monitorIdsChanged_; 8181 this.monitorIdsChanged_ = new Set(); 8182 this.updateDirtyMonitors(monitors); 8183 } 8184 } while (this.monitorIdsChanged_.size + this.persistenceChanged_.size + this.computedPropIdsChanged_.size > 0); 8185 if (this.elmtIdsChanged_.size) { 8186 const elmtIds = Array.from(this.elmtIdsChanged_).sort((elmtId1, elmtId2) => elmtId1 - elmtId2); 8187 this.elmtIdsChanged_ = new Set(); 8188 updateUISynchronously ? this.updateUINodesSynchronously(elmtIds) : this.updateUINodes(elmtIds); 8189 } 8190 } while (this.elmtIdsChanged_.size + this.monitorIdsChanged_.size + this.computedPropIdsChanged_.size > 0); 8191 8192 aceTrace.end(); 8193 } 8194 updateDirtyComputedProps(computed) { 8195 8196 aceTrace.begin(`ObservedV2.updateDirtyComputedProps ${computed.length} @Computed`); 8197 computed.forEach((id) => { 8198 let comp; 8199 let weakComp = this.id2cmp_[id]; 8200 if (weakComp && 'deref' in weakComp && (comp = weakComp.deref()) && comp instanceof ComputedV2) { 8201 const target = comp.getTarget(); 8202 if (target instanceof ViewV2 && !target.isViewActive()) { 8203 // add delayed ComputedIds id 8204 target.addDelayedComputedIds(id); 8205 } 8206 else { 8207 comp.fireChange(); 8208 } 8209 } 8210 }); 8211 aceTrace.end(); 8212 } 8213 updateDirtyMonitors(monitors) { 8214 8215 aceTrace.begin(`ObservedV3.updateDirtyMonitors: ${Array.from(monitors).length} @monitor`); 8216 let weakMonitor; 8217 let monitor; 8218 let monitorTarget; 8219 monitors.forEach((watchId) => { 8220 weakMonitor = this.id2cmp_[watchId]; 8221 if (weakMonitor && 'deref' in weakMonitor && (monitor = weakMonitor.deref()) && monitor instanceof MonitorV2) { 8222 if (((monitorTarget = monitor.getTarget()) instanceof ViewV2) && !monitorTarget.isViewActive()) { 8223 // monitor notifyChange delayed if target is a View that is not active 8224 monitorTarget.addDelayedMonitorIds(watchId); 8225 } 8226 else { 8227 monitor.notifyChange(); 8228 } 8229 } 8230 }); 8231 aceTrace.end(); 8232 } 8233 /** 8234 * This version of UpdateUINodes does not wait for VSYNC, violates rules 8235 * calls UpdateElement, thereby avoids the long and frequent code path from 8236 * FlushDirtyNodesUpdate to CustomNode to ViewV2.updateDirtyElements to UpdateElement 8237 * Code left here to reproduce benchmark measurements, compare with future optimisation 8238 * @param elmtIds 8239 * 8240 */ 8241 updateUINodesSynchronously(elmtIds) { 8242 8243 aceTrace.begin(`ObserveV2.updateUINodesSynchronously: ${elmtIds.length} elmtId`); 8244 let view; 8245 let weak; 8246 elmtIds.forEach((elmtId) => { 8247 if ((weak = this.id2cmp_[elmtId]) && (typeof weak === 'object') && ('deref' in weak) && 8248 (view = weak.deref()) && ((view instanceof ViewV2) || (view instanceof ViewPU))) { 8249 if (view.isViewActive()) { 8250 // FIXME need to call syncInstanceId before update? 8251 view.UpdateElement(elmtId); 8252 } 8253 else if (view instanceof ViewV2) { 8254 // schedule delayed update once the view gets active 8255 view.scheduleDelayedUpdate(elmtId); 8256 } 8257 } // if ViewV2 or ViewPU 8258 }); 8259 aceTrace.end(); 8260 } 8261 // This is the code path similar to V2, follows the rule that UI updates on VSYNC. 8262 // ViewPU/ViewV2 queues the elmtId that need update, marks the CustomNode dirty in RenderContext 8263 // On next VSYNC runs FlushDirtyNodesUpdate to call rerender to call UpdateElement. Much longer code path 8264 // much slower 8265 updateUINodes(elmtIds) { 8266 8267 aceTrace.begin(`ObserveV2.updateUINodes: ${elmtIds.length} elmtId`); 8268 let viewWeak; 8269 let view; 8270 elmtIds.forEach((elmtId) => { 8271 viewWeak = this.id2cmp_[elmtId]; 8272 if (viewWeak && 'deref' in viewWeak && (view = viewWeak.deref()) && 8273 ((view instanceof ViewV2) || (view instanceof ViewPU))) { 8274 if (view.isViewActive()) { 8275 view.uiNodeNeedUpdateV3(elmtId); 8276 } 8277 else { 8278 // schedule delayed update once the view gets active 8279 view.scheduleDelayedUpdate(elmtId); 8280 } 8281 } 8282 }); 8283 aceTrace.end(); 8284 } 8285 constructMonitor(owningObject, owningObjectName) { 8286 let watchProp = Symbol.for(MonitorV2.WATCH_PREFIX + owningObjectName); 8287 if (owningObject && (typeof owningObject === 'object') && owningObject[watchProp]) { 8288 Object.entries(owningObject[watchProp]).forEach(([pathString, monitorFunc]) => { 8289 var _a; 8290 var _b; 8291 if (monitorFunc && pathString && typeof monitorFunc === 'function') { 8292 const monitor = new MonitorV2(owningObject, pathString, monitorFunc); 8293 monitor.InitRun(); 8294 const refs = (_a = owningObject[_b = ObserveV2.MONITOR_REFS]) !== null && _a !== void 0 ? _a : (owningObject[_b] = {}); 8295 // store a reference inside owningObject 8296 // thereby MonitorV2 will share lifespan as owning @ComponentV2 or @ObservedV2 8297 // remember: id2cmp only has a WeakRef to MonitorV2 obj 8298 refs[monitorFunc.name] = monitor; 8299 } 8300 // FIXME Else handle error 8301 }); 8302 } // if target[watchProp] 8303 } 8304 constructComputed(owningObject, owningObjectName) { 8305 const computedProp = Symbol.for(ComputedV2.COMPUTED_PREFIX + owningObjectName); 8306 if (owningObject && (typeof owningObject === 'object') && owningObject[computedProp]) { 8307 Object.entries(owningObject[computedProp]).forEach(([computedPropertyName, computeFunc]) => { 8308 var _a, _b; 8309 var _c; 8310 8311 const computed = new ComputedV2(owningObject, computedPropertyName, computeFunc); 8312 computed.InitRun(); 8313 const refs = (_b = owningObject[_c = ObserveV2.COMPUTED_REFS]) !== null && _b !== void 0 ? _b : (owningObject[_c] = {}); 8314 // store a reference inside owningObject 8315 // thereby ComputedV2 will share lifespan as owning @ComponentV2 or @ObservedV2 8316 // remember: id2cmp only has a WeakRef to ComputedV2 obj 8317 refs[computedPropertyName] = computed; 8318 }); 8319 } 8320 } 8321 clearWatch(id) { 8322 this.clearBinding(id); 8323 } 8324 static autoProxyObject(target, key) { 8325 let val = target[key]; 8326 // Not an object, not a collection, no proxy required 8327 if (!val || typeof (val) !== 'object' || 8328 !(Array.isArray(val) || val instanceof Set || val instanceof Map || val instanceof Date)) { 8329 return val; 8330 } 8331 // Only collections require proxy observation, and if it has been observed, it does not need to be observed again. 8332 if (!val[ObserveV2.SYMBOL_PROXY_GET_TARGET]) { 8333 if (Array.isArray(val)) { 8334 target[key] = new Proxy(val, ObserveV2.arrayProxy); 8335 } 8336 else if (val instanceof Set || val instanceof Map) { 8337 target[key] = new Proxy(val, ObserveV2.setMapProxy); 8338 } 8339 else { 8340 target[key] = new Proxy(val, ObserveV2.objectProxy); 8341 } 8342 val = target[key]; 8343 } 8344 // If the return value is an Array, Set, Map 8345 if (!(val instanceof Date)) { 8346 ObserveV2.getObserve().addRef(ObserveV2.IsMakeObserved(val) ? RefInfo.get(UIUtilsImpl.instance().getTarget(val)) : 8347 val, ObserveV2.OB_LENGTH); 8348 } 8349 return val; 8350 } 8351 /** 8352 * Helper function to add meta data about decorator to ViewPU or ViewV2 8353 * @param proto prototype object of application class derived from ViewPU or ViewV2 8354 * @param varName decorated variable 8355 * @param deco '@state', '@event', etc (note '@model' gets transpiled in '@param' and '@event') 8356 */ 8357 static addVariableDecoMeta(proto, varName, deco) { 8358 var _a; 8359 var _b; 8360 // add decorator meta data 8361 const meta = (_a = proto[_b = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (proto[_b] = {}); 8362 meta[varName] = {}; 8363 meta[varName].deco = deco; 8364 // FIXME 8365 // when splitting ViewPU and ViewV3 8366 // use instanceOf. Until then, this is a workaround. 8367 // any @state, @track, etc V3 event handles this function to return false 8368 Reflect.defineProperty(proto, 'isViewV3', { 8369 get() { return true; }, 8370 enumerable: false 8371 }); 8372 } 8373 static addParamVariableDecoMeta(proto, varName, deco, deco2) { 8374 var _a, _b; 8375 var _c; 8376 // add decorator meta data 8377 const meta = (_a = proto[_c = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (proto[_c] = {}); 8378 (_b = meta[varName]) !== null && _b !== void 0 ? _b : (meta[varName] = {}); 8379 if (deco) { 8380 meta[varName].deco = deco; 8381 } 8382 if (deco2) { 8383 meta[varName].deco2 = deco2; 8384 } 8385 // FIXME 8386 // when splitting ViewPU and ViewV3 8387 // use instanceOf. Until then, this is a workaround. 8388 // any @state, @track, etc V3 event handles this function to return false 8389 Reflect.defineProperty(proto, 'isViewV3', { 8390 get() { return true; }, 8391 enumerable: false 8392 }); 8393 } 8394 static usesV3Variables(proto) { 8395 return (proto && typeof proto === 'object' && proto[ObserveV2.V2_DECO_META]); 8396 } 8397} // class ObserveV2 8398// meta data about decorated variable inside prototype 8399ObserveV2.V2_DECO_META = Symbol('__v2_deco_meta__'); 8400ObserveV2.SYMBOL_REFS = Symbol('__use_refs__'); 8401ObserveV2.ID_REFS = Symbol('__id_refs__'); 8402ObserveV2.MONITOR_REFS = Symbol('___monitor_refs_'); 8403ObserveV2.COMPUTED_REFS = Symbol('___computed_refs_'); 8404ObserveV2.SYMBOL_PROXY_GET_TARGET = Symbol('__proxy_get_target'); 8405ObserveV2.SYMBOL_MAKE_OBSERVED = Symbol('___make_observed__'); 8406ObserveV2.OB_PREFIX = '__ob_'; // OB_PREFIX + attrName => backing store attribute name 8407ObserveV2.OB_PREFIX_LEN = 5; 8408// used by array Handler to create dependency on artificial 'length' 8409// property of array, mark it as changed when array has changed. 8410ObserveV2.OB_LENGTH = '___obj_length'; 8411ObserveV2.setMapProxy = new SetMapProxyHandler(); 8412ObserveV2.arrayProxy = new ArrayProxyHandler(); 8413ObserveV2.objectProxy = new ObjectProxyHandler(); 8414const trackInternal = (target, propertyKey) => { 8415 var _a; 8416 var _b; 8417 if (typeof target === 'function' && !Reflect.has(target, propertyKey)) { 8418 // dynamic track,and it not a static attribute 8419 target = target.prototype; 8420 } 8421 const storeProp = ObserveV2.OB_PREFIX + propertyKey; 8422 target[storeProp] = target[propertyKey]; 8423 Reflect.defineProperty(target, propertyKey, { 8424 get() { 8425 ObserveV2.getObserve().addRef(this, propertyKey); 8426 return ObserveV2.autoProxyObject(this, ObserveV2.OB_PREFIX + propertyKey); 8427 }, 8428 set(val) { 8429 // If the object has not been observed, you can directly assign a value to it. This improves performance. 8430 if (val !== this[storeProp]) { 8431 this[storeProp] = val; 8432 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 8433 ObserveV2.getObserve().fireChange(this, propertyKey); 8434 } 8435 } 8436 }, 8437 enumerable: true 8438 }); 8439 // this marks the proto as having at least one @track property inside 8440 // used by IsObservedObjectV2 8441 (_a = target[_b = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (target[_b] = {}); 8442}; // trackInternal 8443/* 8444 * Copyright (c) 2024 Huawei Device Co., Ltd. 8445 * Licensed under the Apache License, Version 2.0 (the "License"); 8446 * you may not use this file except in compliance with the License. 8447 * You may obtain a copy of the License at 8448 * 8449 * http://www.apache.org/licenses/LICENSE-2.0 8450 * 8451 * Unless required by applicable law or agreed to in writing, software 8452 * distributed under the License is distributed on an "AS IS" BASIS, 8453 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8454 * See the License for the specific language governing permissions and 8455 * limitations under the License. 8456 */ 8457/** 8458 * 8459 * This file includes only framework internal classes and functions 8460 * non are part of SDK. Do not access from app. 8461 * 8462 * 8463 * Helper class for handling V2 decorated variables 8464 */ 8465class VariableUtilV3 { 8466 /** 8467 * setReadOnlyAttr - helper function used to update @param 8468 * from parent @Component. Not allowed for @param @once . 8469 * @param target - the object, usually the ViewV2 8470 * @param attrName - @param variable name 8471 * @param newValue - update to new value 8472 */ 8473 static initParam(target, attrName, newValue) { 8474 var _a; 8475 const meta = (_a = target[ObserveV2.V2_DECO_META]) === null || _a === void 0 ? void 0 : _a[attrName]; 8476 if (!meta || meta.deco !== '@param') { 8477 const error = `Use initParam(${attrName}) only to init @param. Internal error!`; 8478 stateMgmtConsole.error(error); 8479 throw new Error(error); 8480 } 8481 // prevent update for @param @once 8482 const storeProp = ObserveV2.OB_PREFIX + attrName; 8483 8484 target[storeProp] = newValue; 8485 ObserveV2.getObserve().addRef(target, attrName); 8486 } 8487 /** 8488 * setReadOnlyAttr - helper function used to update @param 8489 * from parent @Component. Not allowed for @param @once . 8490 * @param target - the object, usually the ViewV2 8491 * @param attrName - @param variable name 8492 * @param newValue - update to new value 8493 */ 8494 static updateParam(target, attrName, newValue) { 8495 var _a; 8496 // prevent update for @param @once 8497 const meta = (_a = target[ObserveV2.V2_DECO_META]) === null || _a === void 0 ? void 0 : _a[attrName]; 8498 if (!meta || meta.deco !== '@param') { 8499 const error = `Use updateParm(${attrName}) only to update @param. Internal error!`; 8500 stateMgmtConsole.error(error); 8501 throw new Error(error); 8502 } 8503 const storeProp = ObserveV2.OB_PREFIX + attrName; 8504 // @observed class and @track attrName 8505 if (newValue === target[storeProp]) { 8506 8507 return; 8508 } 8509 if (meta.deco2 === '@once') { 8510 // @param @once - init but no update 8511 8512 } 8513 else { 8514 8515 target[storeProp] = newValue; 8516 ObserveV2.getObserve().fireChange(target, attrName); 8517 } 8518 } 8519} 8520class ProviderConsumerUtilV2 { 8521 /** 8522 * meta added to the ViewV2 8523 * varName: { deco: '@Provider' | '@Consumer', aliasName: ..... } 8524 * prefix_@Provider_aliasName: {'varName': ..., 'aliasName': ...., 'deco': '@Provider' | '@Consumer' 8525 */ 8526 static metaAliasKey(aliasName, deco) { 8527 return `${ProviderConsumerUtilV2.ALIAS_PREFIX}_${deco}_${aliasName}`; 8528 } 8529 /** 8530 * Helper function to add meta data about @Provider and @Consumer decorators to ViewV2 8531 * similar to @see addVariableDecoMeta, but adds the alias to allow search from @Consumer for @Provider counterpart 8532 * @param proto prototype object of application class derived from ViewV2 8533 * @param varName decorated variable 8534 * @param deco '@state', '@event', etc (note '@model' gets transpiled in '@param' and '@event') 8535 */ 8536 static addProvideConsumeVariableDecoMeta(proto, varName, aliasName, deco) { 8537 var _a; 8538 var _b; 8539 // add decorator meta data to prototype 8540 const meta = (_a = proto[_b = ObserveV2.V2_DECO_META]) !== null && _a !== void 0 ? _a : (proto[_b] = {}); 8541 // note: aliasName is the actual alias not the prefixed version 8542 meta[varName] = { 'deco': deco, 'aliasName': aliasName }; 8543 // prefix to avoid name collisions with variable of same name as the alias! 8544 const aliasProp = ProviderConsumerUtilV2.metaAliasKey(aliasName, deco); 8545 meta[aliasProp] = { 'varName': varName, 'aliasName': aliasName, 'deco': deco }; 8546 } 8547 /** 8548 * find a @Provider'ed variable from its nearest ancestor ViewV2. 8549 * @param searchingAliasName The key name to search for. 8550 * @returns A tuple containing the ViewPU instance where the provider is found 8551 * and the provider name 8552 * If root @Component reached without finding, returns undefined. 8553 */ 8554 static findProvider(view, aliasName) { 8555 var _a; 8556 let checkView = view === null || view === void 0 ? void 0 : view.getParent(); 8557 const searchingPrefixedAliasName = ProviderConsumerUtilV2.metaAliasKey(aliasName, '@Provider'); 8558 8559 while (checkView) { 8560 const meta = (_a = checkView.constructor) === null || _a === void 0 ? void 0 : _a.prototype[ObserveV2.V2_DECO_META]; 8561 if (checkView instanceof ViewV2 && meta && meta[searchingPrefixedAliasName]) { 8562 const aliasMeta = meta[searchingPrefixedAliasName]; 8563 const providedVarName = (aliasMeta && (aliasMeta.deco === '@Provider') ? aliasMeta.varName : undefined); 8564 if (providedVarName) { 8565 8566 return [checkView, providedVarName]; 8567 } 8568 } 8569 checkView = checkView.getParent(); 8570 } 8571 ; // while 8572 stateMgmtConsole.warn(`findProvider: ${view.debugInfo__()} @Consumer('${aliasName}'), no matching @Provider found amongst ancestor @ComponentV2's!`); 8573 return undefined; 8574 } 8575 /** 8576 * Connects a consumer property of a view (`consumeView`) to a provider property of another view (`provideView`). 8577 * This function establishes a link between the consumer and provider, allowing the consumer to access and update 8578 * the provider's value directly. If the provider view is garbage collected, attempts to access the provider 8579 * property will throw an error. 8580 * 8581 * @param consumeView - The view object that consumes data from the provider. 8582 * @param consumeVarName - The name of the property in the consumer view that will be linked to the provider. 8583 * @param provideView - The view object that provides the data to the consumer. 8584 * @param provideVarName - The name of the property in the provider view that the consumer will access. 8585 * 8586 */ 8587 static connectConsumer2Provider(consumeView, consumeVarName, provideView, provideVarName) { 8588 var _a; 8589 const weakView = new WeakRef(provideView); 8590 const provideViewName = (_a = provideView.constructor) === null || _a === void 0 ? void 0 : _a.name; 8591 Reflect.defineProperty(consumeView, consumeVarName, { 8592 get() { 8593 let view = weakView.deref(); 8594 8595 ObserveV2.getObserve().addRef(this, consumeVarName); 8596 if (!view) { 8597 const error = `${this.debugInfo__()}: get() on @Consumer ${consumeVarName}: providing @ComponentV2 with @Provider ${provideViewName} no longer exists. Application error.`; 8598 stateMgmtConsole.error(error); 8599 throw new Error(error); 8600 } 8601 return view[provideVarName]; 8602 }, 8603 set(val) { 8604 let view = weakView.deref(); 8605 // If the object has not been observed, you can directly assign a value to it. This improves performance. 8606 8607 if (!view) { 8608 const error = `${this.debugInfo__()}: set() on @Consumer ${consumeVarName}: providing @ComponentV2 with @Provider ${provideViewName} no longer exists. Application error.`; 8609 stateMgmtConsole.error(error); 8610 throw new Error(error); 8611 } 8612 if (val !== view[provideVarName]) { 8613 8614 view[provideVarName] = val; 8615 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 8616 ObserveV2.getObserve().fireChange(this, consumeVarName); 8617 } 8618 } 8619 }, 8620 enumerable: true 8621 }); 8622 return provideView[provideVarName]; 8623 } 8624 static defineConsumerWithoutProvider(consumeView, consumeVarName, consumerLocalVal) { 8625 8626 const storeProp = ObserveV2.OB_PREFIX + consumeVarName; 8627 consumeView[storeProp] = consumerLocalVal; // use local init value, also as backing store 8628 Reflect.defineProperty(consumeView, consumeVarName, { 8629 get() { 8630 ObserveV2.getObserve().addRef(this, consumeVarName); 8631 return ObserveV2.autoProxyObject(this, ObserveV2.OB_PREFIX + consumeVarName); 8632 }, 8633 set(val) { 8634 if (val !== this[storeProp]) { 8635 this[storeProp] = val; 8636 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 8637 ObserveV2.getObserve().fireChange(this, consumeVarName); 8638 } 8639 } 8640 }, 8641 enumerable: true 8642 }); 8643 return consumeView[storeProp]; 8644 } 8645} 8646ProviderConsumerUtilV2.ALIAS_PREFIX = '___pc_alias_'; 8647/* 8648 Internal decorator for @Trace without usingV2ObservedTrack call. 8649 Real @Trace decorator function is in v2_decorators.ts 8650*/ 8651const Trace_Internal = (target, propertyKey) => { 8652 return trackInternal(target, propertyKey); 8653}; 8654/* 8655 Internal decorator for @ObservedV2 without usingV2ObservedTrack call. 8656 Real @ObservedV2 decorator function is in v2_decorators.ts 8657*/ 8658function ObservedV2_Internal(BaseClass) { 8659 return observedV2Internal(BaseClass); 8660} 8661/* 8662 @ObservedV2 decorator function uses this in v2_decorators.ts 8663*/ 8664function observedV2Internal(BaseClass) { 8665 // prevent @Track inside @observed class 8666 if (BaseClass.prototype && Reflect.has(BaseClass.prototype, TrackedObject.___IS_TRACKED_OPTIMISED)) { 8667 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.`; 8668 stateMgmtConsole.applicationError(error); 8669 throw new Error(error); 8670 } 8671 if (BaseClass.prototype && !Reflect.has(BaseClass.prototype, ObserveV2.V2_DECO_META)) { 8672 // not an error, suspicious of developer oversight 8673 stateMgmtConsole.warn(`'@observed class ${BaseClass === null || BaseClass === void 0 ? void 0 : BaseClass.name}': no @track property inside. Is this intended? Check our application.`); 8674 } 8675 // Use ID_REFS only if number of observed attrs is significant 8676 const attrList = Object.getOwnPropertyNames(BaseClass.prototype); 8677 const count = attrList.filter(attr => attr.startsWith(ObserveV2.OB_PREFIX)).length; 8678 if (count > 5) { 8679 8680 BaseClass.prototype[ObserveV2.ID_REFS] = {}; 8681 } 8682 const observedClass = class extends BaseClass { 8683 constructor(...args) { 8684 super(...args); 8685 AsyncAddComputedV2.addComputed(this, BaseClass.name); 8686 AsyncAddMonitorV2.addMonitor(this, BaseClass.name); 8687 } 8688 }; 8689 Object.defineProperty(observedClass, 'name', { value: BaseClass.name }); 8690 return observedClass; 8691} 8692/* 8693 * Copyright (c) 2024 Huawei Device Co., Ltd. 8694 * Licensed under the Apache License, Version 2.0 (the "License"); 8695 * you may not use this file except in compliance with the License. 8696 * You may obtain a copy of the License at 8697 * 8698 * http://www.apache.org/licenses/LICENSE-2.0 8699 * 8700 * Unless required by applicable law or agreed to in writing, software 8701 * distributed under the License is distributed on an "AS IS" BASIS, 8702 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8703 * See the License for the specific language governing permissions and 8704 * limitations under the License. 8705 */ 8706/** 8707 * 8708 * This file includes only framework internal classes and functions 8709 * non are part of SDK. Do not access from app. 8710 * 8711 * It includes @Monitor function decorator supporting classes MonitorV2 and AsyncMonitorV2 8712 * 8713 */ 8714class MonitorValueV2 { 8715 constructor(path) { 8716 this.path = path; 8717 this.dirty = false; 8718 this.props = path.split('.'); 8719 } 8720 setValue(isInit, newValue) { 8721 this.now = newValue; 8722 if (isInit) { 8723 this.before = this.now; 8724 } 8725 this.dirty = this.before !== this.now; 8726 return this.dirty; 8727 } 8728 // mv newValue to oldValue, set dirty to false 8729 reset() { 8730 this.before = this.now; 8731 this.dirty = false; 8732 } 8733 isDirty() { 8734 return this.dirty; 8735 } 8736} 8737/** 8738 * MonitorV2 8739 * one MonitorV2 object per @monitor function 8740 * watchId - similar to elmtId, identify one MonitorV2 in Observe.idToCmp Map 8741 * observeObjectAccess = get each object on the 'path' to create dependency and add them with Observe.addRef 8742 * fireChange - exec @Monitor function and re-new dependencies with observeObjectAccess 8743 */ 8744class MonitorV2 { 8745 constructor(target, pathsString, func) { 8746 var _a; 8747 var _b; 8748 this.values_ = new Array(); 8749 this.target_ = target; 8750 this.monitorFunction = func; 8751 this.watchId_ = ++MonitorV2.nextWatchId_; 8752 // split space separated array of paths 8753 let paths = pathsString.split(/\s+/g); 8754 paths.forEach(path => this.values_.push(new MonitorValueV2(path))); 8755 // add watchId to owning ViewV2 or view model data object 8756 // ViewV2 uses to call clearBinding(id) 8757 // FIXME data object leave data inside ObservedV3, because they can not 8758 // call clearBinding(id) before they get deleted. 8759 const meta = (_a = target[_b = MonitorV2.WATCH_INSTANCE_PREFIX]) !== null && _a !== void 0 ? _a : (target[_b] = {}); 8760 meta[pathsString] = this.watchId_; 8761 } 8762 getTarget() { 8763 return this.target_; 8764 } 8765 /** 8766 Return array of those monitored paths 8767 that changed since previous invocation 8768 */ 8769 get dirty() { 8770 let ret = new Array(); 8771 this.values_.forEach(monitorValue => { 8772 if (monitorValue.isDirty()) { 8773 ret.push(monitorValue.path); 8774 } 8775 }); 8776 return ret; 8777 } 8778 /** 8779 * return IMonitorValue for given path 8780 * or if no path is specified any dirty (changed) monitor value 8781 */ 8782 value(path) { 8783 for (let monitorValue of this.values_) { 8784 if ((path === undefined && monitorValue.isDirty()) || monitorValue.path === path) { 8785 return monitorValue; 8786 } 8787 } 8788 return undefined; 8789 } 8790 InitRun() { 8791 this.bindRun(true); 8792 return this; 8793 } 8794 notifyChange() { 8795 if (this.bindRun(/* is init / first run */ false)) { 8796 8797 try { 8798 // exec @Monitor function 8799 this.monitorFunction.call(this.target_, this); 8800 } 8801 catch (e) { 8802 stateMgmtConsole.applicationError(`@Monitor exception caught for ${this.monitorFunction.name}`, e.toString()); 8803 throw e; 8804 } 8805 finally { 8806 this.reset(); 8807 } 8808 } 8809 } 8810 // called after @Monitor function call 8811 reset() { 8812 this.values_.forEach(item => item.reset()); 8813 } 8814 // analysisProp for each monitored path 8815 bindRun(isInit = false) { 8816 ObserveV2.getObserve().startRecordDependencies(this, this.watchId_); 8817 let ret = false; 8818 this.values_.forEach((item) => { 8819 const [success, value] = this.analysisProp(isInit, item); 8820 if (!success) { 8821 8822 return; 8823 } 8824 let dirty = item.setValue(isInit, value); 8825 ret = ret || dirty; 8826 }); 8827 ObserveV2.getObserve().stopRecordDependencies(); 8828 return ret; 8829 } 8830 // record / update object dependencies by reading each object along the path 8831 // return the value, i.e. the value of the last path item 8832 analysisProp(isInit, monitoredValue) { 8833 let obj = this.target_; 8834 for (let prop of monitoredValue.props) { 8835 if (typeof obj === 'object' && Reflect.has(obj, prop)) { 8836 obj = obj[prop]; 8837 } 8838 else { 8839 isInit && stateMgmtConsole.warn(`watch prop ${monitoredValue.path} initialize not found, make sure it exists!`); 8840 return [false, undefined]; 8841 } 8842 } 8843 return [true, obj]; 8844 } 8845 static clearWatchesFromTarget(target) { 8846 var _a; 8847 let meta; 8848 if (!target || typeof target !== 'object' || 8849 !(meta = target[MonitorV2.WATCH_INSTANCE_PREFIX]) || typeof meta !== 'object') { 8850 return; 8851 } 8852 8853 Array.from(Object.values(meta)).forEach((watchId) => ObserveV2.getObserve().clearWatch(watchId)); 8854 } 8855} 8856MonitorV2.WATCH_PREFIX = '___watch_'; 8857MonitorV2.WATCH_INSTANCE_PREFIX = '___watch__obj_'; 8858// start with high number to avoid same id as elmtId for components. 8859MonitorV2.MIN_WATCH_ID = 0x1000000000000; 8860MonitorV2.nextWatchId_ = MonitorV2.MIN_WATCH_ID; 8861// Performance Improvement 8862class AsyncAddMonitorV2 { 8863 static addMonitor(target, name) { 8864 if (AsyncAddMonitorV2.watches.length === 0) { 8865 Promise.resolve(true) 8866 .then(AsyncAddMonitorV2.run) 8867 .catch(error => { 8868 stateMgmtConsole.applicationError(`Exception caught in @Monitor function ${name}`, error); 8869 _arkUIUncaughtPromiseError(error); 8870 }); 8871 } 8872 AsyncAddMonitorV2.watches.push([target, name]); 8873 } 8874 static run() { 8875 for (let item of AsyncAddMonitorV2.watches) { 8876 ObserveV2.getObserve().constructMonitor(item[0], item[1]); 8877 } 8878 AsyncAddMonitorV2.watches = []; 8879 } 8880} 8881AsyncAddMonitorV2.watches = []; 8882/* 8883 * Copyright (c) 2024 Huawei Device Co., Ltd. 8884 * Licensed under the Apache License, Version 2.0 (the "License"); 8885 * you may not use this file except in compliance with the License. 8886 * You may obtain a copy of the License at 8887 * 8888 * http://www.apache.org/licenses/LICENSE-2.0 8889 * 8890 * Unless required by applicable law or agreed to in writing, software 8891 * distributed under the License is distributed on an "AS IS" BASIS, 8892 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8893 * See the License for the specific language governing permissions and 8894 * limitations under the License. 8895 */ 8896/** 8897 * 8898 * This file includes only framework internal classes and functions 8899 * non are part of SDK. Do not access from app. 8900 * 8901 */ 8902/** 8903 * ComputedV2 8904 * one ComputedV2 object per @Computed variable 8905 * computedId_ - similar to elmtId, identify one ComputedV2 in Observe.idToCmp Map 8906 * observeObjectAccess = calculate the compute function and create dependencies to 8907 * source variables 8908 * fireChange - execute compute function and re-new dependencies with observeObjectAccess 8909 */ 8910class ComputedV2 { 8911 constructor(target, prop, func) { 8912 this.target_ = target; 8913 this.propertyComputeFunc_ = func; 8914 this.computedId_ = ++ComputedV2.nextCompId_; 8915 this.prop_ = prop; 8916 } 8917 InitRun() { 8918 let cachedProp = ComputedV2.COMPUTED_CACHED_PREFIX + this.prop_; 8919 let propertyKey = this.prop_; 8920 Reflect.defineProperty(this.target_, propertyKey, { 8921 get() { 8922 ObserveV2.getObserve().addRef(this, propertyKey); 8923 return ObserveV2.autoProxyObject(this, cachedProp); 8924 }, 8925 set(_) { 8926 const error = `@Computed ${propertyKey} is readonly, cannot set value for it`; 8927 stateMgmtConsole.applicationError(error); 8928 throw new Error(error); 8929 }, 8930 enumerable: true 8931 }); 8932 this.target_[cachedProp] = this.observeObjectAccess(); 8933 return this.computedId_; 8934 } 8935 fireChange() { 8936 let newVal = this.observeObjectAccess(); 8937 let cachedProp = ComputedV2.COMPUTED_CACHED_PREFIX + this.prop_; 8938 if (this.target_[cachedProp] !== newVal) { 8939 this.target_[cachedProp] = newVal; 8940 ObserveV2.getObserve().fireChange(this.target_, this.prop_); 8941 } 8942 } 8943 getTarget() { 8944 return this.target_; 8945 } 8946 getProp() { 8947 return this.prop_; 8948 } 8949 // register current watchId while executing compute function 8950 observeObjectAccess() { 8951 ObserveV2.getObserve().startRecordDependencies(this, this.computedId_); 8952 let ret; 8953 try { 8954 ret = this.propertyComputeFunc_.call(this.target_); 8955 } 8956 catch (e) { 8957 stateMgmtConsole.applicationError(`@Computed Exception caught for ${this.propertyComputeFunc_.name}`, e.toString()); 8958 ret = undefined; 8959 throw e; 8960 } 8961 finally { 8962 ObserveV2.getObserve().stopRecordDependencies(); 8963 } 8964 return ret; 8965 } 8966 static clearComputedFromTarget(target) { 8967 var _a; 8968 let meta; 8969 if (!target || typeof target !== 'object' || 8970 !(meta = target[ObserveV2.COMPUTED_REFS]) || typeof meta !== 'object') { 8971 return; 8972 } 8973 8974 Array.from(Object.values(meta)).forEach((computed) => ObserveV2.getObserve().clearWatch(computed.computedId_)); 8975 } 8976} 8977// start with high number to avoid same id as elmtId for components. 8978ComputedV2.MIN_COMPUTED_ID = 0x1000000000; 8979ComputedV2.nextCompId_ = ComputedV2.MIN_COMPUTED_ID; 8980ComputedV2.COMPUTED_PREFIX = '___comp_'; 8981ComputedV2.COMPUTED_CACHED_PREFIX = '___comp_cached_'; 8982class AsyncAddComputedV2 { 8983 static addComputed(target, name) { 8984 if (AsyncAddComputedV2.computedVars.length === 0) { 8985 Promise.resolve(true) 8986 .then(AsyncAddComputedV2.run) 8987 .catch(error => { 8988 stateMgmtConsole.applicationError(`Exception caught in @Computed ${name}`, error); 8989 _arkUIUncaughtPromiseError(error); 8990 }); 8991 } 8992 AsyncAddComputedV2.computedVars.push({ target: target, name: name }); 8993 } 8994 static run() { 8995 AsyncAddComputedV2.computedVars.forEach((computedVar) => ObserveV2.getObserve().constructComputed(computedVar.target, computedVar.name)); 8996 // according to stackoverflow this is the fastest way to clear an Array 8997 // ref https://stackoverflow.com/questions/1232040/how-do-i-empty-an-array-in-javascript 8998 AsyncAddComputedV2.computedVars.length = 0; 8999 } 9000} 9001AsyncAddComputedV2.computedVars = new Array(); 9002/* 9003 * Copyright (c) 2024 Huawei Device Co., Ltd. 9004 * Licensed under the Apache License, Version 2.0 (the "License"); 9005 * you may not use this file except in compliance with the License. 9006 * You may obtain a copy of the License at 9007 * 9008 * http://www.apache.org/licenses/LICENSE-2.0 9009 * 9010 * Unless required by applicable law or agreed to in writing, software 9011 * distributed under the License is distributed on an "AS IS" BASIS, 9012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9013 * See the License for the specific language governing permissions and 9014 * limitations under the License. 9015 */ 9016/** 9017 * 9018 * This file includes only framework internal classes and functions 9019 * non are part of SDK. Do not access from app. 9020 * 9021 * Implementation of @ComponentV2 is ViewV2 9022 * When transpiling @ComponentV2, the transpiler generates a class that extends from ViewV2. 9023 * 9024 */ 9025class ViewV2 extends PUV2ViewBase { 9026 constructor(parent, elmtId = UINodeRegisterProxy.notRecordingDependencies, extraInfo = undefined) { 9027 super(parent, elmtId, extraInfo); 9028 // Set of elmtIds that need re-render 9029 this.dirtDescendantElementIds_ = new Set(); 9030 this.monitorIdsDelayedUpdate = new Set(); 9031 this.computedIdsDelayedUpdate = new Set(); 9032 /** 9033 * on first render create a new Instance of Repeat 9034 * on re-render connect to existing instance 9035 * @param arr 9036 * @returns 9037 */ 9038 this.__mkRepeatAPI = (arr) => { 9039 // factory is for future extensions, currently always return the same 9040 const elmtId = ObserveV2.getCurrentRecordedId(); 9041 let repeat = this.elmtId2Repeat_.get(elmtId); 9042 if (!repeat) { 9043 repeat = new __Repeat(this, arr); 9044 this.elmtId2Repeat_.set(elmtId, repeat); 9045 } 9046 else { 9047 repeat.updateArr(arr); 9048 } 9049 return repeat; 9050 }; 9051 this.setIsV2(true); 9052 9053 } 9054 /** 9055 * The `freezeState` parameter determines whether this @ComponentV2 is allowed to freeze, when inactive 9056 * Its called with value of the `freezeWhenInactive` parameter from the @ComponentV2 decorator, 9057 * or it may be called with `undefined` depending on how the UI compiler works. 9058 * 9059 * @param freezeState Only the value `true` will be used to set the freeze state, 9060 * otherwise it inherits from its parent instance if its freezeState is true 9061 */ 9062 finalizeConstruction(freezeState) { 9063 ObserveV2.getObserve().constructComputed(this, this.constructor.name); 9064 ObserveV2.getObserve().constructMonitor(this, this.constructor.name); 9065 // Always use ID_REFS in ViewV2 9066 this[ObserveV2.ID_REFS] = {}; 9067 // set to true if freeze parameter set for this @ComponentV2 to true 9068 // otherwise inherit from its parentComponent (if it exists). 9069 this.isCompFreezeAllowed_ = freezeState || this.isCompFreezeAllowed_; 9070 9071 } 9072 debugInfo__() { 9073 return `@ComponentV2 '${this.constructor.name}'[${this.id__()}]`; 9074 } 9075 get isViewV3() { 9076 return true; 9077 } 9078 /** 9079 * Virtual function implemented in ViewPU and ViewV2 9080 * Unregisters and purges all child elements associated with the specified Element ID in ViewV2. 9081 * 9082 * @param rmElmtId - The Element ID to be purged and deleted 9083 * @returns {boolean} - Returns `true` if the Element ID was successfully deleted, `false` otherwise. 9084 */ 9085 purgeDeleteElmtId(rmElmtId) { 9086 9087 const result = this.updateFuncByElmtId.delete(rmElmtId); 9088 if (result) { 9089 const childOpt = this.getChildViewV2ForElmtId(rmElmtId); 9090 if (childOpt) { 9091 childOpt.setDeleting(); 9092 childOpt.setDeleteStatusRecursively(); 9093 } 9094 // it means rmElmtId has finished all the unregistration from the js side, ElementIdToOwningViewPU_ does not need to keep it 9095 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(rmElmtId); 9096 } 9097 // Needed only for V2 9098 ObserveV2.getObserve().clearBinding(rmElmtId); 9099 return result; 9100 } 9101 // super class will call this function from 9102 // its aboutToBeDeleted implementation 9103 aboutToBeDeletedInternal() { 9104 9105 // if this isDeleting_ is true already, it may be set delete status recursively by its parent, so it is not necessary 9106 // to set and resursively set its children any more 9107 if (!this.isDeleting_) { 9108 this.isDeleting_ = true; 9109 this.setDeleteStatusRecursively(); 9110 } 9111 // tell UINodeRegisterProxy that all elmtIds under 9112 // this ViewV2 should be treated as already unregistered 9113 9114 // purge the elmtIds owned by this ViewV2 from the updateFuncByElmtId and also the state variable dependent elmtIds 9115 Array.from(this.updateFuncByElmtId.keys()).forEach((elmtId) => { 9116 // FIXME split View: enable delete this purgeDeleteElmtId(elmtId); 9117 }); 9118 // unregistration of ElementIDs 9119 9120 // it will unregister removed elementids from all the ViewV2, equals purgeDeletedElmtIdsRecursively 9121 this.purgeDeletedElmtIds(); 9122 // unregisters its own id once its children are unregistered above 9123 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs([this.id__()]); 9124 9125 /* in case ViewPU is currently frozen 9126 ViewPU inactiveComponents_ delete(`${this.constructor.name}[${this.id__()}]`); 9127 */ 9128 MonitorV2.clearWatchesFromTarget(this); 9129 ComputedV2.clearComputedFromTarget(this); 9130 this.updateFuncByElmtId.clear(); 9131 if (this.parent_) { 9132 this.parent_.removeChild(this); 9133 } 9134 } 9135 initialRenderView() { 9136 9137 this.initialRender(); 9138 9139 } 9140 observeComponentCreation2(compilerAssignedUpdateFunc, classObject) { 9141 if (this.isDeleting_) { 9142 stateMgmtConsole.error(`@ComponentV2 ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation2 `); 9143 return; 9144 } 9145 const _componentName = (classObject && ('name' in classObject)) ? Reflect.get(classObject, 'name') : 'unspecified UINode'; 9146 const _popFunc = (classObject && 'pop' in classObject) ? classObject.pop : () => { }; 9147 const updateFunc = (elmtId, isFirstRender) => { 9148 this.syncInstanceId(); 9149 9150 ViewStackProcessor.StartGetAccessRecordingFor(elmtId); 9151 ObserveV2.getObserve().startRecordDependencies(this, elmtId); 9152 compilerAssignedUpdateFunc(elmtId, isFirstRender); 9153 if (!isFirstRender) { 9154 _popFunc(); 9155 } 9156 let node = this.getNodeById(elmtId); 9157 if (node !== undefined) { 9158 node.cleanStageValue(); 9159 } 9160 ObserveV2.getObserve().stopRecordDependencies(); 9161 ViewStackProcessor.StopGetAccessRecording(); 9162 9163 this.restoreInstanceId(); 9164 }; 9165 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 9166 // needs to move set before updateFunc. 9167 // make sure the key and object value exist since it will add node in attributeModifier during updateFunc. 9168 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc, classObject: classObject }); 9169 // add element id -> owning ViewV2 9170 UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this)); 9171 try { 9172 updateFunc(elmtId, /* is first render */ true); 9173 } 9174 catch (error) { 9175 // avoid the incompatible change that move set function before updateFunc. 9176 this.updateFuncByElmtId.delete(elmtId); 9177 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId); 9178 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${error.message}`); 9179 throw error; 9180 } 9181 9182 } 9183 /** 9184 * 9185 * @param paramVariableName 9186 * @param @once paramVariableName 9187 * @param is read only, therefore, init from parent needs to be done without 9188 * causing property setter() to be called 9189 * @param newValue 9190 */ 9191 initParam(paramVariableName, newValue) { 9192 this.checkIsV1Proxy(paramVariableName, newValue); 9193 VariableUtilV3.initParam(this, paramVariableName, newValue); 9194 } 9195 /** 9196 * 9197 * @param paramVariableName 9198 * @param @once paramVariableName 9199 * @param is read only, therefore, update from parent needs to be done without 9200 * causing property setter() to be called 9201 * @param @once reject any update 9202 * @param newValue 9203 */ 9204 updateParam(paramVariableName, newValue) { 9205 this.checkIsV1Proxy(paramVariableName, newValue); 9206 VariableUtilV3.updateParam(this, paramVariableName, newValue); 9207 } 9208 checkIsV1Proxy(paramVariableName, value) { 9209 if (ObservedObject.IsObservedObject(value)) { 9210 throw new Error(`Cannot assign the ComponentV1 value to the ComponentV2 for the property '${paramVariableName}'`); 9211 } 9212 } 9213 /** 9214 * inform that UINode with given elmtId needs rerender 9215 * does NOT exec @Watch function. 9216 * only used on V3 code path from ObserveV2.fireChange. 9217 * 9218 * FIXME will still use in the future? 9219 */ 9220 uiNodeNeedUpdateV3(elmtId) { 9221 if (this.isFirstRender()) { 9222 return; 9223 } 9224 9225 if (!this.isActive_) { 9226 this.scheduleDelayedUpdate(elmtId); 9227 return; 9228 } 9229 if (!this.dirtDescendantElementIds_.size) { // && !this runReuse_) { 9230 // mark ComposedElement dirty when first elmtIds are added 9231 // do not need to do this every time 9232 this.syncInstanceId(); 9233 this.markNeedUpdate(); 9234 this.restoreInstanceId(); 9235 } 9236 this.dirtDescendantElementIds_.add(elmtId); 9237 9238 9239 } 9240 /** 9241 * For each recorded dirty Element in this custom component 9242 * run its update function 9243 * 9244 */ 9245 updateDirtyElements() { 9246 9247 do { 9248 9249 // see which elmtIds are managed by this View 9250 // and clean up all book keeping for them 9251 this.purgeDeletedElmtIds(); 9252 // process all elmtIds marked as needing update in ascending order. 9253 // ascending order ensures parent nodes will be updated before their children 9254 // prior cleanup ensure no already deleted Elements have their update func executed 9255 const dirtElmtIdsFromRootNode = Array.from(this.dirtDescendantElementIds_).sort(ViewV2.compareNumber); 9256 // if state changed during exec update lambda inside UpdateElement, then the dirty elmtIds will be added 9257 // to newly created this.dirtDescendantElementIds_ Set 9258 dirtElmtIdsFromRootNode.forEach(elmtId => { 9259 this.UpdateElement(elmtId); 9260 this.dirtDescendantElementIds_.delete(elmtId); 9261 }); 9262 if (this.dirtDescendantElementIds_.size) { 9263 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!`); 9264 } 9265 } while (this.dirtDescendantElementIds_.size); 9266 9267 9268 } 9269 UpdateElement(elmtId) { 9270 if (this.isDeleting_) { 9271 9272 return; 9273 } 9274 9275 if (elmtId === this.id__()) { 9276 // do not attempt to update itself 9277 9278 return; 9279 } 9280 // do not process an Element that has been marked to be deleted 9281 const entry = this.updateFuncByElmtId.get(elmtId); 9282 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 9283 if (typeof updateFunc !== 'function') { 9284 9285 } 9286 else { 9287 const componentName = entry.getComponentName(); 9288 9289 9290 try { 9291 updateFunc(elmtId, /* isFirstRender */ false); 9292 } 9293 catch (e) { 9294 stateMgmtConsole.applicationError(`Exception caught in update function of ${componentName} for elmtId ${elmtId}`, e.toString()); 9295 throw e; 9296 } 9297 finally { 9298 9299 } 9300 9301 this.finishUpdateFunc(elmtId); 9302 9303 9304 } 9305 9306 } 9307 /** 9308 * Retrieve child by given id 9309 * @param id 9310 * @returns child if child with this id exists and it is instance of ViewV2 9311 */ 9312 getViewV2ChildById(id) { 9313 const childWeakRef = this.childrenWeakrefMap_.get(id); 9314 const child = childWeakRef ? childWeakRef.deref() : undefined; 9315 return (child && child instanceof ViewV2) ? child : undefined; 9316 } 9317 /** 9318 * findViewPUInHierarchy function needed for @Component and @ComponentV2 mixed 9319 * parent - child hierarchies. Not used by ViewV2 9320 */ 9321 findViewPUInHierarchy(id) { 9322 // this ViewV2 is not a ViewPU, continue searching amongst children 9323 let retVal = undefined; 9324 for (const [key, value] of this.childrenWeakrefMap_.entries()) { 9325 retVal = value.deref().findViewPUInHierarchy(id); 9326 if (retVal) { 9327 break; 9328 } 9329 } 9330 return retVal; 9331 } 9332 // WatchIds that needs to be fired later gets added to monitorIdsDelayedUpdate 9333 // monitor fireChange will be triggered for all these watchIds once this view gets active 9334 addDelayedMonitorIds(watchId) { 9335 9336 this.monitorIdsDelayedUpdate.add(watchId); 9337 } 9338 addDelayedComputedIds(watchId) { 9339 9340 this.computedIdsDelayedUpdate.add(watchId); 9341 } 9342 setActiveInternal(newState) { 9343 9344 if (!this.isCompFreezeAllowed()) { 9345 9346 9347 return; 9348 } 9349 9350 this.isActive_ = newState; 9351 if (this.isActive_) { 9352 this.onActiveInternal(); 9353 } 9354 else { 9355 this.onInactiveInternal(); 9356 } 9357 9358 } 9359 onActiveInternal() { 9360 if (!this.isActive_) { 9361 return; 9362 } 9363 9364 this.performDelayedUpdate(); 9365 // Set 'isActive_' state for all descendant child Views 9366 for (const child of this.childrenWeakrefMap_.values()) { 9367 const childView = child.deref(); 9368 if (childView) { 9369 childView.setActiveInternal(this.isActive_); 9370 } 9371 } 9372 } 9373 onInactiveInternal() { 9374 if (this.isActive_) { 9375 return; 9376 } 9377 9378 // Set 'isActive_' state for all descendant child Views 9379 for (const child of this.childrenWeakrefMap_.values()) { 9380 const childView = child.deref(); 9381 if (childView) { 9382 childView.setActiveInternal(this.isActive_); 9383 } 9384 } 9385 } 9386 performDelayedUpdate() { 9387 9388 if (this.computedIdsDelayedUpdate.size) { 9389 // exec computed functions 9390 ObserveV2.getObserve().updateDirtyComputedProps([...this.computedIdsDelayedUpdate]); 9391 } 9392 if (this.monitorIdsDelayedUpdate.size) { 9393 // exec monitor functions 9394 ObserveV2.getObserve().updateDirtyMonitors(this.monitorIdsDelayedUpdate); 9395 } 9396 if (this.elmtIdsDelayedUpdate.size) { 9397 // update re-render of updated element ids once the view gets active 9398 if (this.dirtDescendantElementIds_.size === 0) { 9399 this.dirtDescendantElementIds_ = new Set(this.elmtIdsDelayedUpdate); 9400 } 9401 else { 9402 this.elmtIdsDelayedUpdate.forEach((element) => { 9403 this.dirtDescendantElementIds_.add(element); 9404 }); 9405 } 9406 } 9407 this.markNeedUpdate(); 9408 this.elmtIdsDelayedUpdate.clear(); 9409 this.monitorIdsDelayedUpdate.clear(); 9410 this.computedIdsDelayedUpdate.clear(); 9411 9412 } 9413 /* 9414 findProvidePU finds @Provided property recursively by traversing ViewPU's towards that of the UI tree root @Component: 9415 if 'this' ViewPU has a @Provide('providedPropName') return it, otherwise ask from its parent ViewPU. 9416 function needed for mixed @Component and @ComponentV2 parent child hierarchies. 9417 */ 9418 findProvidePU(providedPropName) { 9419 var _a; 9420 return (_a = this.getParent()) === null || _a === void 0 ? void 0 : _a.findProvidePU(providedPropName); 9421 } 9422 get localStorage_() { 9423 // FIXME check this also works for root @ComponentV2 9424 return (this.getParent()) ? this.getParent().localStorage_ : new LocalStorage({ /* empty */}); 9425 } 9426 /** 9427 * @function observeRecycleComponentCreation 9428 * @description custom node recycle creation not supported for V2. So a dummy function is implemented to report 9429 * an error message 9430 * @param name custom node name 9431 * @param recycleUpdateFunc custom node recycle update which can be converted to a normal update function 9432 * @return void 9433 */ 9434 observeRecycleComponentCreation(name, recycleUpdateFunc) { 9435 stateMgmtConsole.error(`${this.debugInfo__()}: Recycle not supported for ComponentV2 instances`); 9436 } 9437 debugInfoDirtDescendantElementIdsInternal(depth = 0, recursive = false, counter) { 9438 let retVaL = `\n${' '.repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`; 9439 retVaL += `ViewV2 keeps no info about dirty elmtIds`; 9440 if (recursive) { 9441 this.childrenWeakrefMap_.forEach((value, key, map) => { 9442 var _a; 9443 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoDirtDescendantElementIdsInternal(depth + 1, recursive, counter); 9444 }); 9445 } 9446 if (recursive && depth === 0) { 9447 retVaL += `\nTotal: ${counter.total}`; 9448 } 9449 return retVaL; 9450 } 9451 debugInfoStateVars() { 9452 return ''; // TODO DFX, read out META 9453 } 9454} 9455/* 9456 * Copyright (c) 2024 Huawei Device Co., Ltd. 9457 * Licensed under the Apache License, Version 2.0 (the "License"); 9458 * you may not use this file except in compliance with the License. 9459 * You may obtain a copy of the License at 9460 * 9461 * http://www.apache.org/licenses/LICENSE-2.0 9462 * 9463 * Unless required by applicable law or agreed to in writing, software 9464 * distributed under the License is distributed on an "AS IS" BASIS, 9465 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9466 * See the License for the specific language governing permissions and 9467 * limitations under the License. 9468 */ 9469function ObservedV2(BaseClass) { 9470 ConfigureStateMgmt.instance.usingV2ObservedTrack(`@observed`, BaseClass === null || BaseClass === void 0 ? void 0 : BaseClass.name); 9471 return observedV2Internal(BaseClass); 9472} 9473/** 9474 * @Trace class property decorator, property inside @ObservedV2 class 9475 * 9476 * turns given property into getter and setter functions 9477 * adds property target[storeProp] as the backing store 9478 * 9479 * part of SDK 9480 * @from 12 9481 */ 9482const Trace = (target, propertyKey) => { 9483 ConfigureStateMgmt.instance.usingV2ObservedTrack(`@track`, propertyKey); 9484 return trackInternal(target, propertyKey); 9485}; 9486/** 9487 * @Local @ComponentV2/ViewV2 variable 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 required 9492 * no init or update from parent @ComponentV2 9493 * new value assignment allowed = has setter 9494 * 9495 * part of SDK 9496 * @from 12 9497 * 9498 */ 9499const Local = (target, propertyKey) => { 9500 ObserveV2.addVariableDecoMeta(target, propertyKey, '@state'); 9501 return trackInternal(target, propertyKey); 9502}; 9503/** 9504 * @Param class property decorator 9505 * 9506 * allowed value: simple or object type value allowed. Objects must be instances of 9507 * ObservedV2, Array, Map, Set, or Date for changes to be observed. No functions allowed 9508 * local init optional 9509 * init from parent @ComponentV2 is mandatory when no local init, otherwise optional 9510 * updates from parent @ComponentV2 if initialized from parent @ComponentV2, 9511 * no update when @Once is used. 9512 * new value assignment not allowed = has no setter. 9513 * 9514 * part of SDK 9515 * @from 12 9516 * 9517 */ 9518const Param = (proto, propertyKey) => { 9519 9520 ObserveV2.addParamVariableDecoMeta(proto, propertyKey, '@param', undefined); 9521 let storeProp = ObserveV2.OB_PREFIX + propertyKey; 9522 proto[storeProp] = proto[propertyKey]; 9523 Reflect.defineProperty(proto, propertyKey, { 9524 get() { 9525 ObserveV2.getObserve().addRef(this, propertyKey); 9526 return ObserveV2.autoProxyObject(this, ObserveV2.OB_PREFIX + propertyKey); 9527 }, 9528 set(val) { 9529 var _a; 9530 const meta = (_a = proto[ObserveV2.V2_DECO_META]) === null || _a === void 0 ? void 0 : _a[propertyKey]; 9531 if (meta && meta.deco2 !== '@once') { 9532 stateMgmtConsole.applicationError(`@param ${propertyKey.toString()}: can not assign a new value, application error.`); 9533 return; 9534 } 9535 if (val !== this[storeProp]) { 9536 this[storeProp] = val; 9537 if (this[ObserveV2.SYMBOL_REFS]) { // This condition can improve performance. 9538 ObserveV2.getObserve().fireChange(this, propertyKey); 9539 } 9540 } 9541 }, 9542 // @param can not be assigned, no setter 9543 enumerable: true 9544 }); 9545}; // Param 9546/** 9547 * @Once supplementary @ComponentV2 variable decorator to @Param decorator 9548 * must use like this @Param @Once varName. Can not be used without @param. 9549 * prevents @Param variable updates from parent component 9550 * 9551 * @param proto 9552 * @param propertyKey 9553 * 9554 * part of SDK 9555 * @from 12 9556 * 9557 */ 9558const Once = (proto, propertyKey) => { 9559 9560 ObserveV2.addParamVariableDecoMeta(proto, propertyKey, undefined, '@once'); 9561}; 9562/** 9563 * @Event class variable decorator, class must be @ComponentV2 9564 * 9565 * Allowed value: Function, can have parameters and return a value. 9566 * local init: optional for functions without return value, default is () => void 9567 * Local init is mandatory for functions with return value. 9568 * init from parent @Component: optional. 9569 * update from parent @Component: never 9570 * new value assignment not allowed 9571 * 9572 * part of SDK 9573 * @from 12 9574 * 9575 */ 9576const Event = (target, propertyKey) => { 9577 var _a; 9578 ObserveV2.addVariableDecoMeta(target, propertyKey, '@event'); 9579 (_a = target[propertyKey]) !== null && _a !== void 0 ? _a : (target[propertyKey] = () => { }); 9580}; 9581/** 9582 * @Provider variable decorator of @ComponentV2 variable 9583 * 9584 * @Provider(alias? : string) varName : typeName = initValue 9585 * 9586 * @param alias defaults to varName 9587 * 9588 * allowed value: simple or object type value allowed. Objects must be instances of 9589 * ObservedV2, Array, Map, Set, or Date for changes to be observed. No functions allowed 9590 * local init required 9591 * no init or update from parent @ComponentV2 9592 * provides its value to any @Consumer counter part 9593 * new value assignment allowed = has setter 9594 * 9595 * part of SDK 9596 * @since 12 9597 */ 9598const Provider = (aliasName) => { 9599 return (proto, varName) => { 9600 const providedUnderName = aliasName || varName; 9601 ProviderConsumerUtilV2.addProvideConsumeVariableDecoMeta(proto, varName, providedUnderName, '@Provider'); 9602 trackInternal(proto, varName); 9603 }; 9604}; // @Provider 9605/** 9606 * @Consumer variable decorator of @ComponentV2 variable 9607 * 9608 * @Consumer(alias? : string) varName : typeName = defaultValue 9609* 9610 * @param alias defaults to varName 9611 * 9612 * allowed value: simple or object type value allowed. Objects must be instances of 9613 * ObservedV2, Array, Map, Set, or Date for changes to be observed. No functions allowed 9614 * syncs two-way with the @Provider variable with same `alias` name in nearest ancestor @ComponentV2 9615 * local init required, used only if no @Provider counter part is found. 9616 * no init or update from parent @ComponentV2 via constructor allowed 9617 * new value assignment allowed, changes sys back to @Provider of one exists, otherwise update local value. 9618 * 9619 * part of SDK 9620 * @since 12 9621 */ 9622const Consumer = (aliasName) => { 9623 return (proto, varName) => { 9624 const searchForProvideWithName = aliasName || varName; 9625 // redefining the property happens when owning ViewV2 gets constructed 9626 // and @Consumer gets connected to @provide counterpart 9627 ProviderConsumerUtilV2.addProvideConsumeVariableDecoMeta(proto, varName, searchForProvideWithName, '@Consumer'); 9628 const providerName = (aliasName === undefined || aliasName === null || 9629 (typeof aliasName === 'string' && aliasName.trim() === '')) ? varName : aliasName; 9630 Reflect.defineProperty(proto, varName, { 9631 get() { 9632 // this get function should never be called, 9633 // because transpiler will always assign it a value first. 9634 stateMgmtConsole.warn('@Consumer outer "get" should never be called, internal error!'); 9635 return undefined; 9636 }, 9637 set(val) { 9638 let providerInfo = ProviderConsumerUtilV2.findProvider(this, providerName); 9639 if (providerInfo && providerInfo[0] && providerInfo[1]) { 9640 ProviderConsumerUtilV2.connectConsumer2Provider(this, varName, providerInfo[0], providerInfo[1]); 9641 } 9642 else { 9643 ProviderConsumerUtilV2.defineConsumerWithoutProvider(this, varName, val); 9644 } 9645 }, 9646 enumerable: true 9647 }); 9648 }; 9649}; // @Consumer 9650/** 9651 * @Monitor class function decorator, inside either @ComponentV2 or @ObservedV2 class 9652 * 9653 * @Monitor(path: string, paths: string[]) functionName (m : IMonitor) : void 9654 * 9655 * @param path : string , path of monitored object properties (strictly objects, no arrays, maps etc) 9656 * property names separated by '.'. 9657 * @param paths : string[] , further, optional paths to monitor 9658 * 9659 * 9660 * The decorated function must have one parameter of type IMonitor and no return value. 9661 * 9662 * Example: @Monitor('varName.obj', 'varName.obj.proA', 'varName2') onChange(m : IMonitor) : void { ... } 9663 * monitors assignments to this.varName.obj, this.varName.obj.propA, and this.varName2 . 9664 * 9665 * part of SDK 9666 * @since 12 9667 */ 9668const Monitor = function (key, ...keys) { 9669 const pathsUniqueString = keys ? [key, ...keys].join(' ') : key; 9670 return function (target, _, descriptor) { 9671 9672 let watchProp = Symbol.for(MonitorV2.WATCH_PREFIX + target.constructor.name); 9673 const monitorFunc = descriptor.value; 9674 target[watchProp] ? target[watchProp][pathsUniqueString] = monitorFunc : target[watchProp] = { [pathsUniqueString]: monitorFunc }; 9675 }; 9676}; 9677/** 9678 * @Computed TS computed class member variable decorator, inside either @ComponentV2 or @ObservedV2 class 9679 * 9680 * must be a computed class property following TS syntax, e.g. @Computed get varName() { return this.state1 + this.state2 } 9681 * value assignment / set not allowed = has no setter. 9682 * The framework updates the value of the @Computed variable whenever its input changes 9683 * Therefore, the getter function must only use variables whose changes can be observed. 9684 * The getter function implementation must not mutate any state. 9685 * Changes of the return value of the getter function must be observable to use for constructing UI. 9686 * This means if the return value is an object, it must be @ObservedV2 class instance with @Trace 'ed properties, 9687 * or of Array, Map, Set, or Date type. 9688 * The app should not modify the return value because re-execution of the getter function would overwrite these changes. 9689 * 9690 * part of SDK 9691 * @from 12 9692 * 9693 */ 9694const Computed = (target, propertyKey, descriptor) => { 9695 9696 let watchProp = Symbol.for(ComputedV2.COMPUTED_PREFIX + target.constructor.name); 9697 const computeFunction = descriptor.get; 9698 target[watchProp] ? target[watchProp][propertyKey] = computeFunction 9699 : target[watchProp] = { [propertyKey]: computeFunction }; 9700}; 9701/* 9702 * Copyright (c) 2024 Huawei Device Co., Ltd. 9703 * Licensed under the Apache License, Version 2.0 (the "License"); 9704 * you may not use this file except in compliance with the License. 9705 * You may obtain a copy of the License at 9706 * 9707 * http://www.apache.org/licenses/LICENSE-2.0 9708 * 9709 * Unless required by applicable law or agreed to in writing, software 9710 * distributed under the License is distributed on an "AS IS" BASIS, 9711 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9712 * See the License for the specific language governing permissions and 9713 * limitations under the License. 9714 */ 9715const V2_STATE_PREFIX = '__ob_'; 9716const V2_PREFIX_LENGTH = V2_STATE_PREFIX.length; 9717; 9718class Meta { 9719 static define(proto, prop, value) { 9720 const meta = Meta.proto2props.get(proto); 9721 if (!meta) { 9722 Meta.proto2props.set(proto, { [prop]: value }); 9723 } 9724 else { 9725 meta[prop] = value; 9726 } 9727 } 9728 static get(obj, prop) { 9729 let proto = obj.__proto__; 9730 while (proto) { 9731 let meta = Meta.proto2props.get(proto); 9732 if (meta && meta[prop]) { 9733 return meta[prop]; 9734 } 9735 proto = proto.__proto__; 9736 } 9737 return undefined; 9738 } 9739 static gets(obj) { 9740 const ret = {}; 9741 let proto = obj.__proto__; 9742 while (proto) { 9743 let meta = Meta.proto2props.get(proto); 9744 Object.assign(ret, meta); 9745 proto = proto.__proto__; 9746 } 9747 return ret; 9748 } 9749 static getOwn(obj, prop) { 9750 const meta = Meta.proto2props.get(obj.__proto__); 9751 return meta && meta[prop]; 9752 } 9753} 9754Meta.proto2props = new WeakMap(); 9755function __Type__(type, alias) { 9756 const options = JSONCoder.getOptions(type); 9757 if (alias) { 9758 options.alias = alias; 9759 } 9760 return (target, prop) => { 9761 const tar = typeof target === 'function' ? target.prototype : target; 9762 Meta.define(tar, prop, options); 9763 }; 9764} 9765function ObservedReplacer(replacer) { 9766 const defaultReplacer = function (key, value) { 9767 return value; 9768 }; 9769 const realReplacer = replacer || defaultReplacer; 9770 return function (key, value) { 9771 if (typeof value !== 'object' || Array.isArray(value)) { 9772 return realReplacer.call(this, key, value); 9773 } 9774 if (value instanceof Set) { 9775 return realReplacer.call(this, key, Array.from(value)); 9776 } 9777 if (value instanceof Map) { 9778 return realReplacer.call(this, key, Array.from(value.entries())); 9779 } 9780 const ret = {}; 9781 const meta = Meta.gets(value); 9782 Object.keys(value).forEach(key => { 9783 let saveKey = key.startsWith(V2_STATE_PREFIX) ? key.substring(V2_PREFIX_LENGTH) : key; 9784 let options = meta && meta[saveKey]; 9785 if (options && options.disabled) { 9786 return; 9787 } 9788 ret[(options && options.alias) || saveKey] = value[saveKey]; 9789 }); 9790 return realReplacer.call(this, key, ret); 9791 }; 9792} 9793/** 9794 * JSONCoder 9795 * 9796 * The JSONCoder utility enhances the serialization and deserialization capabilities beyond 9797 * the standard JSON.stringify and JSON.parse methods. While JSON.stringify serializes 9798 * object properties and their values, it drops functions, class, property, and function decorators, 9799 * and does not support Map, Set, or Date serialization. JSONCoder addresses these limitations, 9800 * providing robust support for a wider range of JavaScript features. 9801 * 9802 * Main Features: 9803 * - Adds support for serializing and deserializing class instances, including methods and decorators. 9804 * - Supports serialization of complex data structures like Map, Set, and Date. 9805 * - Provides full reconstruction of class instances through the JSONCoder.parseTo method. 9806 * 9807 * Usage Scenarios: 9808 * - Serializing class instances to JSON for network transmission or storage. 9809 * - Deserializing JSON data back into fully functional class instances, preserving methods and decorators. 9810 * - Converting JSON data received from network or database into state management observable view models (e.g., @ObservedV2 class objects). 9811 * 9812 * The name 'JSONCoder' is derived from the 'JSON stringify/parse', reflecting its purpose to enhance JSON serialization and deserialization for classes. 9813 * 9814 */ 9815class JSONCoder { 9816 /** 9817 * Serializes the given object into a string. This string includes additional meta info 9818 * allowing `stringify` to fully reconstruct the original object, including its class 9819 * type and properties. 9820 * 9821 * @template T - The type of the object being serialized. 9822 * @param { T } value - The object to serialize. 9823 * @param { (this: JSONAny, key: string, value: JSONAny) => JSONAny } [replacer] - A function that alters the behavior when stringify 9824 * @param { string | number } [space] - For format 9825 * @returns { string } The serialized string representation of the object. 9826 */ 9827 static stringify(value, replacer, space) { 9828 return JSON.stringify(value, ObservedReplacer(replacer), space); 9829 } 9830 /** 9831 * Parses a JSON string or object and applies the nested key-values to a class object. 9832 * The main usage scenario is to convert JSON data received from a network or database 9833 * to a state management observable view model. 9834 * 9835 * @template T - The type of the object being parsed. 9836 * @param { TypeConstructor<T> | TransformOptions<T> } type - The class prototype or constructor function that has no parameters. 9837 * @param { object | string } source - The JSON string or JSON object. 9838 * @returns { T | T[] } The parsed object of type T or T[]. 9839 */ 9840 static parse(type, source) { 9841 const json = typeof source === 'string' ? JSON.parse(source) : source; 9842 const options = JSONCoder.getOptions(type); 9843 return Array.isArray(json) ? 9844 JSONCoder.parseIntoArray([], json, options) : 9845 JSONCoder.parseInto(JSONCoder.newItem(json, options), json); 9846 } 9847 /** 9848 * Deserializes a string produced by `parseTo` back into the original object, 9849 * fully reconstructing its class type and properties. 9850 * 9851 * @template T - The original object being parsed. 9852 * @param { T | T[] } type - The original object. 9853 * @param { object | string } source - The JSON string or JSON object. 9854 * @param { TypeConstructor<T> | TransformOptions<T> } [type] - The class prototype or constructor function that has no parameters. 9855 * @returns { T | T[] } The parsed object of type T or T[]. 9856 */ 9857 static parseTo(target, source, type) { 9858 const json = typeof source === 'string' ? JSON.parse(source) : source; 9859 const t1 = Array.isArray(json); 9860 const t2 = Array.isArray(target); 9861 const options = JSONCoder.getOptions(type); 9862 if (t1 && t2) { 9863 JSONCoder.parseIntoArray(target, json, options); 9864 } 9865 else if (!t1 && !t2) { 9866 JSONCoder.parseInto(target, json); 9867 } 9868 else { 9869 throw new Error(`The type of target '${t2}' mismatches the type of source '${t1}'`); 9870 } 9871 return target; 9872 } 9873 /** 9874 * Get the type options from the object creator. 9875 * 9876 * @template T - The object being parsed. 9877 * @param { TypeConstructor<T> | TransformOptions<T> | string } [type] - The type info of the object creator. 9878 * @returns { TransformOptions<T> } The options of the type info. 9879 */ 9880 static getOptions(type) { 9881 const paramType = typeof type; 9882 const options = {}; 9883 if (paramType === 'object') { 9884 Object.assign(options, type); 9885 } 9886 else if (paramType === 'function') { 9887 options.factory = (_) => type; 9888 } 9889 else if (paramType === 'string') { 9890 options.alias = type; 9891 } 9892 return options; 9893 } 9894 static getAlias2Prop(meta, target) { 9895 const ret = new Map(); 9896 Object.keys(meta).forEach(prop => { 9897 const options = meta[prop]; 9898 ret.set(options.alias || prop, prop); 9899 }); 9900 return ret; 9901 } 9902 static parseInto(target, source) { 9903 if (typeof source !== 'object') { 9904 throw new Error(`The type of target '${typeof target}' mismatches the type of source '${typeof source}'`); 9905 } 9906 const meta = Meta.gets(target); 9907 const alias2prop = JSONCoder.getAlias2Prop(meta, target); 9908 Object.keys(source).forEach((key) => { 9909 const prop = alias2prop.get(key) || key; 9910 const options = meta && meta[prop]; 9911 if (options && options.disabled) { 9912 return; 9913 } 9914 JSONCoder.parseItemInto(target, prop, source, options); 9915 }); 9916 return target; 9917 } 9918 static parseItemInto(target, targetKey, source, options) { 9919 if (source === null || source === undefined) { 9920 return; 9921 } 9922 let tarType = typeof target[targetKey]; 9923 if (tarType === 'function') { 9924 return; 9925 } 9926 const sourceKey = (options === null || options === void 0 ? void 0 : options.alias) || targetKey; 9927 // Handling invalid values 9928 const value = JSONCoder.getTargetValue(source[sourceKey], options); 9929 if (value === undefined || value === null) { 9930 if (tarType === 'object') { 9931 if (target[targetKey] instanceof Map || target[targetKey] instanceof Set) { 9932 target[targetKey].clear(); 9933 } 9934 else if (Array.isArray(target[targetKey])) { 9935 target[targetKey].splice(0, target[targetKey].length); 9936 } 9937 else if (options && options.factory) { 9938 // if options.factory exists, can be assigned to undefined or null 9939 target[targetKey] = value; 9940 } 9941 } 9942 // other scene ignore all 9943 return; 9944 } 9945 // value is array, it maybe array or map or set 9946 if (Array.isArray(value)) { 9947 target[targetKey] = JSONCoder.parseIntoArray(target[targetKey], value, options); 9948 return; 9949 } 9950 // if target[targetKey] invalid, then attempt create 9951 if (target[targetKey] === null || target[targetKey] === undefined) { 9952 target[targetKey] = JSONCoder.newItem(value, options); 9953 tarType = typeof target[targetKey]; 9954 } 9955 if (typeof value !== 'object') { 9956 // value is Primitive Type 9957 if (target[targetKey] instanceof Date) { 9958 target[targetKey] = new Date(value); 9959 } 9960 else if (tarType === 'string') { 9961 target[targetKey] = value.toString(); 9962 } 9963 else if (tarType === typeof value) { 9964 target[targetKey] = value; 9965 } 9966 else if (target[targetKey] !== undefined) { 9967 throw new Error(`The type of target '${tarType}' mismatches the type of source '${typeof value}'`); 9968 } 9969 return; 9970 } 9971 // value is object, target[targetKey] is undefined or null 9972 if (target[targetKey] === null) { 9973 throw new Error(`Miss @Type in object defined, the property name is ${targetKey}`); 9974 } 9975 else if (target[targetKey] === undefined) { 9976 // ignore target[targetKey] undefined 9977 return; 9978 } 9979 this.parseInto(target[targetKey], value); 9980 } 9981 static newItem(json, options) { 9982 const type = options === null || options === void 0 ? void 0 : options.factory(json); 9983 return type && new type(); 9984 } 9985 static getTargetValue(value, options) { 9986 // future can convert the value to different type or value 9987 return value; 9988 } 9989 static parseIntoArray(target, source, options) { 9990 if (typeof target !== 'object') { 9991 throw new Error(`The type of target '${typeof target}' mismatches the type of source '${typeof source}'`); 9992 } 9993 // here, source maybe a array or map or set 9994 if (target instanceof Map) { 9995 target.clear(); 9996 for (let i = 0; i < source.length; ++i) { 9997 // If target is a map, item must be an array. Otherwise, ignore it 9998 const item = source[i]; 9999 if (!Array.isArray(item) || item.length < 2 || typeof item[0] !== 'string') { 10000 continue; 10001 } 10002 target.set(item[0], typeof item[1] !== 'object' ? item[1] : JSONCoder.parse(options, item[1])); 10003 } 10004 return target; 10005 } 10006 if (target instanceof Set) { 10007 target.clear(); 10008 for (let i = 0; i < source.length; ++i) { 10009 const item = source[i]; 10010 target.add(typeof item !== 'object' ? item : JSONCoder.parse(options, item)); 10011 } 10012 return target; 10013 } 10014 target.length = source.length; 10015 for (let i = 0; i < source.length; ++i) { 10016 const item = source[i]; 10017 if (typeof item !== 'object') { 10018 target[i] = item; 10019 continue; 10020 } 10021 if (i === 0) { 10022 if (!(options === null || options === void 0 ? void 0 : options.factory)) { 10023 target.length = 0; 10024 throw new Error(`Miss @Type in array defined`); 10025 } 10026 } 10027 target[i] = Array.isArray(item) ? 10028 JSONCoder.parseIntoArray(target[i] || [], item, options) : 10029 JSONCoder.parseInto(target[i] || JSONCoder.newItem(item, options), item); 10030 } 10031 return target; 10032 } 10033} 10034/* 10035 * Copyright (c) 2024 Huawei Device Co., Ltd. 10036 * Licensed under the Apache License, Version 2.0 (the "License"); 10037 * you may not use this file except in compliance with the License. 10038 * You may obtain a copy of the License at 10039 * 10040 * http://www.apache.org/licenses/LICENSE-2.0 10041 * 10042 * Unless required by applicable law or agreed to in writing, software 10043 * distributed under the License is distributed on an "AS IS" BASIS, 10044 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10045 * See the License for the specific language governing permissions and 10046 * limitations under the License. 10047 */ 10048class RefInfo { 10049 static get(target) { 10050 if (!target || typeof target !== 'object') { 10051 stateMgmtConsole.warn(`makeObserved target is not a valid object, return target directly`); 10052 return { proxy: target }; 10053 } 10054 // makeObserved does not support @Observed, @ObservedV2/@Trace class or makeObserved proxy, will return target directly 10055 if (ObservedObject.IsObservedObject(target) || ObserveV2.IsObservedObjectV2(target) || ObserveV2.IsMakeObserved(target)) { 10056 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`); 10057 return { proxy: target }; 10058 } 10059 let ret = RefInfo.obj2ref.get(target); 10060 if (!ret) { 10061 if (Array.isArray(target) || SendableType.isArray(target)) { 10062 ret = { proxy: new Proxy(target, RefInfo.arrayProxy) }; 10063 } 10064 else if (target instanceof Set || SendableType.isSet(target) || 10065 target instanceof Map || SendableType.isMap(target)) { 10066 ret = { proxy: new Proxy(target, RefInfo.setMapProxy) }; 10067 } 10068 else { 10069 ret = { proxy: new Proxy(target, RefInfo.objectProxy) }; 10070 } 10071 RefInfo.obj2ref.set(target, ret); 10072 } 10073 return ret; 10074 } 10075} 10076RefInfo.obj2ref = new WeakMap(); 10077RefInfo.setMapProxy = new SetMapProxyHandler(true); 10078RefInfo.arrayProxy = new ArrayProxyHandler(true); 10079RefInfo.objectProxy = new ObjectProxyHandler(true); 10080/* 10081 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 10082 * Licensed under the Apache License, Version 2.0 (the "License"); 10083 * you may not use this file except in compliance with the License. 10084 * You may obtain a copy of the License at 10085 * 10086 * http://www.apache.org/licenses/LICENSE-2.0 10087 * 10088 * Unless required by applicable law or agreed to in writing, software 10089 * distributed under the License is distributed on an "AS IS" BASIS, 10090 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10091 * See the License for the specific language governing permissions and 10092 * limitations under the License. 10093 * 10094 * all definitions in this file are framework internal 10095*/ 10096var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 10097 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 10098 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 10099 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; 10100 return c > 3 && r && Object.defineProperty(target, key, r), r; 10101}; 10102// implementation for existing state observation system 10103class __RepeatItemPU { 10104 constructor(owningView, initialItem, initialIndex) { 10105 this._observedItem = new ObservedPropertyPU(initialItem, owningView, 'Repeat item'); 10106 if (initialIndex !== undefined) { 10107 this._observedIndex = new ObservedPropertyPU(initialIndex, owningView, 'Repeat index'); 10108 } 10109 } 10110 get item() { 10111 return this._observedItem.get(); 10112 } 10113 get index() { 10114 var _a; 10115 return (_a = this._observedIndex) === null || _a === void 0 ? void 0 : _a.get(); 10116 } 10117 updateItem(newItemValue) { 10118 this._observedItem.set(newItemValue); 10119 } 10120 updateIndex(newIndex) { 10121 var _a, _b, _c; 10122 if (!((_a = this._observedIndex) === null || _a === void 0 ? void 0 : _a.hasDependencies())) { 10123 return; 10124 } 10125 if (((_b = this._observedIndex) === null || _b === void 0 ? void 0 : _b.getUnmonitored()) !== newIndex) { 10126 (_c = this._observedIndex) === null || _c === void 0 ? void 0 : _c.set(newIndex); 10127 } 10128 } 10129} 10130// Framework internal, deep observation 10131// Using @ObservedV2_Internal instead of @ObservedV2 to avoid forcing V2 usage. 10132let __RepeatItemV2 = class __RepeatItemV2 { 10133 constructor(initialItem, initialIndex) { 10134 this.item = initialItem; 10135 this.index = initialIndex; 10136 } 10137 updateItem(newItemValue) { 10138 this.item = newItemValue; 10139 } 10140 updateIndex(newIndex) { 10141 if (this.index !== undefined) { 10142 this.index = newIndex; 10143 } 10144 } 10145}; 10146__decorate([ 10147 Trace_Internal 10148], __RepeatItemV2.prototype, "item", void 0); 10149__decorate([ 10150 Trace_Internal 10151], __RepeatItemV2.prototype, "index", void 0); 10152__RepeatItemV2 = __decorate([ 10153 ObservedV2_Internal 10154], __RepeatItemV2); 10155// helper 10156class __RepeatDefaultKeyGen { 10157 // Return the same IDs for the same items 10158 static func(item) { 10159 try { 10160 return __RepeatDefaultKeyGen.funcImpl(item); 10161 } 10162 catch (e) { 10163 throw new Error(`Repeat(). Default id gen failed. Application Error!`); 10164 } 10165 } 10166 // Return the same IDs for the same pairs <item, index> 10167 static funcWithIndex(item, index) { 10168 return `${index}__` + __RepeatDefaultKeyGen.func(item); 10169 } 10170 static funcImpl(item) { 10171 // fast keygen logic can be used with objects/symbols only 10172 if (typeof item !== 'object' && typeof item !== 'symbol') { 10173 return JSON.stringify(item); 10174 } 10175 // generate a numeric key, store mappings in WeakMap 10176 if (!this.weakMap_.has(item)) { 10177 return this.weakMap_.set(item, ++this.lastKey_), `${this.lastKey_}`; 10178 } 10179 // use cached key 10180 return `${this.weakMap_.get(item)}`; 10181 } 10182} 10183__RepeatDefaultKeyGen.weakMap_ = new WeakMap(); 10184__RepeatDefaultKeyGen.lastKey_ = 0; 10185; 10186; 10187// __Repeat implements ForEach with child re-use for both existing state observation 10188// and deep observation , for non-virtual and virtual code paths (TODO) 10189class __Repeat { 10190 constructor(owningView, arr) { 10191 this.config = {}; 10192 this.isVirtualScroll = false; 10193 this.config.owningView_ = owningView instanceof ViewV2 ? owningView : undefined; 10194 this.config.arr = arr !== null && arr !== void 0 ? arr : []; 10195 this.config.itemGenFuncs = {}; 10196 this.config.keyGenFunc = __RepeatDefaultKeyGen.funcWithIndex; 10197 this.config.typeGenFunc = (() => ''); 10198 this.config.totalCountSpecified = false; 10199 this.config.totalCount = this.config.arr.length; 10200 this.config.templateOptions = {}; 10201 // to be used with ViewV2 10202 const mkRepeatItemV2 = (item, index) => new __RepeatItemV2(item, index); 10203 // to be used with ViewPU 10204 const mkRepeatItemPU = (item, index) => new __RepeatItemPU(owningView, item, index); 10205 const isViewV2 = (this.config.owningView_ instanceof ViewV2); 10206 this.config.mkRepeatItem = isViewV2 ? mkRepeatItemV2 : mkRepeatItemPU; 10207 } 10208 each(itemGenFunc) { 10209 this.config.itemGenFuncs[''] = itemGenFunc; 10210 this.config.templateOptions[''] = this.normTemplateOptions({}); 10211 return this; 10212 } 10213 key(keyGenFunc) { 10214 this.config.keyGenFunc = keyGenFunc; 10215 return this; 10216 } 10217 virtualScroll(options) { 10218 if (Number.isInteger(options === null || options === void 0 ? void 0 : options.totalCount)) { 10219 this.config.totalCount = options.totalCount; 10220 this.config.totalCountSpecified = true; 10221 } 10222 else { 10223 this.config.totalCountSpecified = false; 10224 } 10225 this.isVirtualScroll = true; 10226 return this; 10227 } 10228 // function to decide which template to use, each template has an id 10229 templateId(typeGenFunc) { 10230 const typeGenFuncImpl = (item, index) => { 10231 try { 10232 return typeGenFunc(item, index); 10233 } 10234 catch (e) { 10235 stateMgmtConsole.applicationError(`Repeat with virtual scroll. Exception in templateId():`, e === null || e === void 0 ? void 0 : e.message); 10236 return ''; 10237 } 10238 }; 10239 // typeGenFunc wrapper with ttype validation 10240 const typeGenFuncSafe = (item, index) => { 10241 const itemType = typeGenFuncImpl(item, index); 10242 const itemFunc = this.config.itemGenFuncs[itemType]; 10243 if (typeof itemFunc !== 'function') { 10244 stateMgmtConsole.applicationError(`Repeat with virtual scroll. Missing Repeat.template for id '${itemType}'`); 10245 return ''; 10246 } 10247 return itemType; 10248 }; 10249 this.config.typeGenFunc = typeGenFuncSafe; 10250 return this; 10251 } 10252 // template: id + builder function to render specific type of data item 10253 template(type, itemGenFunc, options) { 10254 this.config.itemGenFuncs[type] = itemGenFunc; 10255 this.config.templateOptions[type] = this.normTemplateOptions(options); 10256 return this; 10257 } 10258 updateArr(arr) { 10259 this.config.arr = arr !== null && arr !== void 0 ? arr : []; 10260 return this; 10261 } 10262 render(isInitialRender) { 10263 var _a, _b, _c; 10264 if (!((_a = this.config.itemGenFuncs) === null || _a === void 0 ? void 0 : _a[''])) { 10265 throw new Error(`__Repeat item builder function unspecified. Usage error`); 10266 } 10267 if (!this.isVirtualScroll) { 10268 // Repeat 10269 (_b = this.impl) !== null && _b !== void 0 ? _b : (this.impl = new __RepeatImpl()); 10270 this.impl.render(this.config, isInitialRender); 10271 } 10272 else { 10273 // RepeatVirtualScroll 10274 (_c = this.impl) !== null && _c !== void 0 ? _c : (this.impl = new __RepeatVirtualScrollImpl()); 10275 this.impl.render(this.config, isInitialRender); 10276 } 10277 } 10278 // drag and drop API 10279 onMove(handler) { 10280 this.config.onMoveHandler = handler; 10281 return this; 10282 } 10283 // normalize template options 10284 normTemplateOptions(options) { 10285 const value = (options && Number.isInteger(options.cachedCount) && options.cachedCount >= 0) 10286 ? { 10287 cachedCount: Math.max(0, options.cachedCount), 10288 cachedCountSpecified: true 10289 } 10290 : { 10291 cachedCountSpecified: false 10292 }; 10293 return value; 10294 } 10295} 10296; // __Repeat<T> 10297/* 10298 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 10299 * Licensed under the Apache License, Version 2.0 (the "License"); 10300 * you may not use this file except in compliance with the License. 10301 * You may obtain a copy of the License at 10302 * 10303 * http://www.apache.org/licenses/LICENSE-2.0 10304 * 10305 * Unless required by applicable law or agreed to in writing, software 10306 * distributed under the License is distributed on an "AS IS" BASIS, 10307 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10308 * See the License for the specific language governing permissions and 10309 * limitations under the License. 10310 * 10311 * all definitions in this file are framework internal 10312*/ 10313class __RepeatImpl { 10314 /**/ 10315 constructor() { 10316 this.key2Item_ = new Map(); 10317 } 10318 /**/ 10319 render(config, isInitialRender) { 10320 this.arr_ = config.arr; 10321 this.itemGenFuncs_ = config.itemGenFuncs; 10322 this.typeGenFunc_ = config.typeGenFunc; 10323 this.keyGenFunction_ = config.keyGenFunc; 10324 this.mkRepeatItem_ = config.mkRepeatItem; 10325 this.onMoveHandler_ = config.onMoveHandler; 10326 isInitialRender ? this.initialRender() : this.reRender(); 10327 } 10328 genKeys() { 10329 const key2Item = new Map(); 10330 this.arr_.forEach((item, index) => { 10331 const key = this.keyGenFunction_(item, index); 10332 key2Item.set(key, { key, index }); 10333 }); 10334 if (key2Item.size < this.arr_.length) { 10335 stateMgmtConsole.warn('__RepeatImpl: Duplicates detected, fallback to index-based keyGen.'); 10336 // Causes all items to be re-rendered 10337 this.keyGenFunction_ = __RepeatDefaultKeyGen.funcWithIndex; 10338 return this.genKeys(); 10339 } 10340 return key2Item; 10341 } 10342 initialRender() { 10343 //console.log('__RepeatImpl initialRender() 0') 10344 this.key2Item_ = this.genKeys(); 10345 RepeatNative.startRender(); 10346 let index = 0; 10347 this.key2Item_.forEach((itemInfo, key) => { 10348 itemInfo.repeatItem = this.mkRepeatItem_(this.arr_[index], index); 10349 this.initialRenderItem(key, itemInfo.repeatItem); 10350 index++; 10351 }); 10352 let removedChildElmtIds = new Array(); 10353 // Fetch the removedChildElmtIds from C++ to unregister those elmtIds with UINodeRegisterProxy 10354 RepeatNative.onMove(this.onMoveHandler_); 10355 RepeatNative.finishRender(removedChildElmtIds); 10356 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 10357 10358 } 10359 reRender() { 10360 const oldKey2Item = this.key2Item_; 10361 this.key2Item_ = this.genKeys(); 10362 // identify array items that have been deleted 10363 // these are candidates for re-use 10364 const deletedKeysAndIndex = new Array(); 10365 for (const [key, feInfo] of oldKey2Item) { 10366 if (!this.key2Item_.has(key)) { 10367 deletedKeysAndIndex.push(feInfo); 10368 } 10369 } 10370 // C++: mv children_ aside to tempchildren_ 10371 RepeatNative.startRender(); 10372 let index = 0; 10373 this.key2Item_.forEach((itemInfo, key) => { 10374 const item = this.arr_[index]; 10375 let oldItemInfo = oldKey2Item.get(key); 10376 if (oldItemInfo) { 10377 // case #1 retained array item 10378 // moved from oldIndex to index 10379 const oldIndex = oldItemInfo.index; 10380 itemInfo.repeatItem = oldItemInfo.repeatItem; 10381 10382 itemInfo.repeatItem.updateIndex(index); 10383 // C++ mv from tempChildren[oldIndex] to end of children_ 10384 RepeatNative.moveChild(oldIndex); 10385 // TBD moveChild() only when item types are same 10386 } 10387 else if (deletedKeysAndIndex.length) { 10388 // case #2: 10389 // new array item, there is an deleted array items whose 10390 // UINode children cab re-used 10391 const oldItemInfo = deletedKeysAndIndex.pop(); 10392 const reuseKey = oldItemInfo.key; 10393 const oldKeyIndex = oldItemInfo.index; 10394 const oldRepeatItem = oldItemInfo.repeatItem; 10395 itemInfo.repeatItem = oldRepeatItem; 10396 10397 itemInfo.repeatItem.updateItem(item); 10398 itemInfo.repeatItem.updateIndex(index); 10399 // update key2item_ Map 10400 this.key2Item_.set(key, itemInfo); 10401 // TBD moveChild() only when item types are same 10402 // C++ mv from tempChildren[oldIndex] to end of children_ 10403 RepeatNative.moveChild(oldKeyIndex); 10404 } 10405 else { 10406 // case #3: 10407 // new array item, there are no deleted array items 10408 // render new UINode children 10409 itemInfo.repeatItem = this.mkRepeatItem_(item, index); 10410 this.initialRenderItem(key, itemInfo.repeatItem); 10411 } 10412 index++; 10413 }); 10414 // keep this.id2item_. by removing all entries for remaining 10415 // deleted items 10416 deletedKeysAndIndex.forEach(delItem => { 10417 this.key2Item_.delete(delItem.key); 10418 }); 10419 // Finish up for.each update 10420 // C++ tempChildren.clear() , trigger re-layout 10421 let removedChildElmtIds = new Array(); 10422 // Fetch the removedChildElmtIds from C++ to unregister those elmtIds with UINodeRegisterProxy 10423 RepeatNative.onMove(this.onMoveHandler_); 10424 RepeatNative.finishRender(removedChildElmtIds); 10425 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 10426 10427 } 10428 initialRenderItem(key, repeatItem) { 10429 var _a, _b; 10430 //console.log('__RepeatImpl initialRenderItem()') 10431 // render new UINode children 10432 10433 // C++: initial render will render to the end of children_ 10434 RepeatNative.createNewChildStart(key); 10435 // execute the itemGen function 10436 const itemType = (_a = this.typeGenFunc_(repeatItem.item, repeatItem.index)) !== null && _a !== void 0 ? _a : ''; 10437 const itemFunc = (_b = this.itemGenFuncs_[itemType]) !== null && _b !== void 0 ? _b : this.itemGenFuncs_['']; 10438 itemFunc(repeatItem); 10439 RepeatNative.createNewChildFinish(key); 10440 } 10441} 10442; 10443/* 10444 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 10445 * Licensed under the Apache License, Version 2.0 (the "License"); 10446 * you may not use this file except in compliance with the License. 10447 * You may obtain a copy of the License at 10448 * 10449 * http://www.apache.org/licenses/LICENSE-2.0 10450 * 10451 * Unless required by applicable law or agreed to in writing, software 10452 * distributed under the License is distributed on an "AS IS" BASIS, 10453 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10454 * See the License for the specific language governing permissions and 10455 * limitations under the License. 10456 * 10457 * all definitions in this file are framework internal 10458*/ 10459// Implements ForEach with child re-use for both existing state observation and 10460// deep observation. For virtual-scroll code paths 10461class __RepeatVirtualScrollImpl { 10462 constructor() { 10463 this.totalCountSpecified = false; 10464 // index <-> key maps 10465 this.key4Index_ = new Map(); 10466 this.index4Key_ = new Map(); 10467 // Map key -> RepeatItem 10468 // added to closure of following lambdas 10469 this.repeatItem4Key_ = new Map(); 10470 // RepeatVirtualScrollNode elmtId 10471 this.repeatElmtId_ = -1; 10472 // Last known active range (as sparse array) 10473 this.lastActiveRangeData_ = []; 10474 } 10475 render(config, isInitialRender) { 10476 this.arr_ = config.arr; 10477 this.itemGenFuncs_ = config.itemGenFuncs; 10478 this.keyGenFunc_ = config.keyGenFunc; 10479 this.typeGenFunc_ = config.typeGenFunc; 10480 // if totalCountSpecified==false, then need to create dependency on array length 10481 // so when array length changes, will update totalCount 10482 this.totalCountSpecified = config.totalCountSpecified; 10483 this.totalCount_ = (!this.totalCountSpecified || config.totalCount < 0) 10484 ? this.arr_.length 10485 : config.totalCount; 10486 this.templateOptions_ = config.templateOptions; 10487 this.mkRepeatItem_ = config.mkRepeatItem; 10488 this.onMoveHandler_ = config.onMoveHandler; 10489 if (isInitialRender) { 10490 this.initialRender(config.owningView_, ObserveV2.getCurrentRecordedId()); 10491 } 10492 else { 10493 this.reRender(); 10494 } 10495 } 10496 /**/ 10497 initialRender(owningView, repeatElmtId) { 10498 this.repeatElmtId_ = repeatElmtId; 10499 const onCreateNode = (forIndex) => { 10500 10501 if (forIndex < 0 || forIndex >= this.totalCount_ || forIndex >= this.arr_.length) { 10502 // STATE_MGMT_NOTE check also index < totalCount 10503 throw new Error(`__RepeatVirtualScrollImpl (${this.repeatElmtId_}) onCreateNode: for index=${forIndex} \ 10504 with data array length ${this.arr_.length}, totalCount=${this.totalCount_} out of range error.`); 10505 } 10506 // create dependency array item [forIndex] -> Repeat 10507 // so Repeat updates when the array item changes 10508 // STATE_MGMT_NOTE observe dependencies, adding the array is insurgent for Array of objects 10509 ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, forIndex.toString()); 10510 const repeatItem = this.mkRepeatItem_(this.arr_[forIndex], forIndex); 10511 let forKey = this.getOrMakeKey4Index(forIndex); 10512 this.repeatItem4Key_.set(forKey, repeatItem); 10513 // execute the itemGen function 10514 this.initialRenderItem(repeatItem); 10515 10516 }; // onCreateNode 10517 const onUpdateNode = (fromKey, forIndex) => { 10518 if (!fromKey || fromKey === '' || forIndex < 0 || forIndex >= this.totalCount_ || forIndex >= this.arr_.length) { 10519 throw new Error(`__RepeatVirtualScrollImpl (${this.repeatElmtId_}) onUpdateNode: fromKey "${fromKey}", \ 10520 forIndex=${forIndex}, with data array length ${this.arr_.length}, totalCount=${this.totalCount_}, invalid function input error.`); 10521 } 10522 // create dependency array item [forIndex] -> Repeat 10523 // so Repeat updates when the array item changes 10524 // STATE_MGMT_NOTE observe dependencies, adding the array is insurgent for Array of objects 10525 ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, forIndex.toString()); 10526 const repeatItem = this.repeatItem4Key_.get(fromKey); 10527 if (!repeatItem) { 10528 stateMgmtConsole.error(`__RepeatVirtualScrollImpl (${this.repeatElmtId_}) onUpdateNode: fromKey "${fromKey}", forIndex=${forIndex}, can not find RepeatItem for key. Unrecoverable error`); 10529 return; 10530 } 10531 const forKey = this.getOrMakeKey4Index(forIndex); 10532 10533 // update Map according to made update: 10534 // del fromKey entry and add forKey 10535 this.repeatItem4Key_.delete(fromKey); 10536 this.repeatItem4Key_.set(forKey, repeatItem); 10537 if (repeatItem.item !== this.arr_[forIndex] || repeatItem.index !== forIndex) { 10538 // repeatItem needs update, will trigger partial update to using UINodes: 10539 repeatItem.updateItem(this.arr_[forIndex]); 10540 repeatItem.updateIndex(forIndex); 10541 10542 ObserveV2.getObserve().updateDirty2(true); 10543 } 10544 }; // onUpdateNode 10545 const onGetKeys4Range = (from, to) => { 10546 if (to > this.totalCount_ || to > this.arr_.length) { 10547 stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId ${this.repeatElmtId_}: onGetKeys4Range from ${from} to ${to} \ 10548 with data array length ${this.arr_.length}, totalCount=${this.totalCount_} \ 10549 Error!. Application fails to add more items to source data array. on time. Trying with corrected input parameters ...`); 10550 to = this.totalCount_; 10551 from = Math.min(to, from); 10552 } 10553 10554 const result = new Array(); 10555 // deep observe dependencies, 10556 // create dependency array item [i] -> Repeat 10557 // so Repeat updates when the array item or nested objects changes 10558 // not enough: ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, i.toString()); 10559 ViewStackProcessor.StartGetAccessRecordingFor(this.repeatElmtId_); 10560 ObserveV2.getObserve().startRecordDependencies(owningView, this.repeatElmtId_, false); 10561 for (let i = from; i <= to && i < this.arr_.length; i++) { 10562 result.push(this.getOrMakeKey4Index(i)); 10563 } 10564 ObserveV2.getObserve().stopRecordDependencies(); 10565 ViewStackProcessor.StopGetAccessRecording(); 10566 let needsRerender = false; 10567 result.forEach((key, index) => { 10568 const forIndex = index + from; 10569 // if repeatItem exists, and needs update then do the update, and call sync update as well 10570 // thereby ensure cached items are up-to-date on C++ side. C++ does not need to request update 10571 // from TS side 10572 const repeatItem4Key = this.repeatItem4Key_.get(key); 10573 // make sure the index is up-to-date 10574 if (repeatItem4Key && (repeatItem4Key.item !== this.arr_[forIndex] || repeatItem4Key.index !== forIndex)) { 10575 // repeatItem needs update, will trigger partial update to using UINodes: 10576 repeatItem4Key.updateItem(this.arr_[forIndex]); 10577 repeatItem4Key.updateIndex(forIndex); 10578 needsRerender = true; 10579 } 10580 }); // forEach 10581 if (needsRerender) { 10582 10583 ObserveV2.getObserve().updateDirty2(true); 10584 } 10585 10586 return result; 10587 }; // const onGetKeys4Range 10588 const onGetTypes4Range = (from, to) => { 10589 if (to > this.totalCount_ || to > this.arr_.length) { 10590 stateMgmtConsole.applicationError(`Repeat with virtualScroll elmtId: ${this.repeatElmtId_}: onGetTypes4Range from ${from} to ${to} \ 10591 with data array length ${this.arr_.length}, totalCount=${this.totalCount_} \ 10592 Error! Application fails to add more items to source data array.on time.Trying with corrected input parameters ...`); 10593 to = this.totalCount_; 10594 from = Math.min(to, from); 10595 } 10596 10597 const result = new Array(); 10598 // deep observe dependencies, 10599 // create dependency array item [i] -> Repeat 10600 // so Repeat updates when the array item or nested objects changes 10601 // not enough: ObserveV2.getObserve().addRef4Id(this.repeatElmtId_, this.arr_, i.toString()); 10602 ViewStackProcessor.StartGetAccessRecordingFor(this.repeatElmtId_); 10603 ObserveV2.getObserve().startRecordDependencies(owningView, this.repeatElmtId_, false); 10604 for (let i = from; i <= to && i < this.arr_.length; i++) { 10605 let ttype = this.typeGenFunc_(this.arr_[i], i); 10606 result.push(ttype); 10607 } // for 10608 ObserveV2.getObserve().stopRecordDependencies(); 10609 ViewStackProcessor.StopGetAccessRecording(); 10610 10611 return result; 10612 }; // const onGetTypes4Range 10613 const onSetActiveRange = (from, to) => { 10614 10615 // make sparse copy of this.arr_ 10616 this.lastActiveRangeData_ = new Array(this.arr_.length); 10617 if (from <= to) { 10618 for (let i = Math.max(0, from); i <= to && i < this.arr_.length; i++) { 10619 const item = this.arr_[i]; 10620 const ttype = this.typeGenFunc_(this.arr_[i], i); 10621 this.lastActiveRangeData_[i] = { item, ttype }; 10622 } 10623 } 10624 else { 10625 for (let i = 0; i <= to && i < this.arr_.length; i++) { 10626 const item = this.arr_[i]; 10627 const ttype = this.typeGenFunc_(this.arr_[i], i); 10628 this.lastActiveRangeData_[i] = { item, ttype }; 10629 } 10630 for (let i = Math.max(0, from); i < this.arr_.length; i++) { 10631 const item = this.arr_[i]; 10632 const ttype = this.typeGenFunc_(this.arr_[i], i); 10633 this.lastActiveRangeData_[i] = { item, ttype }; 10634 } 10635 } 10636 }; 10637 10638 RepeatVirtualScrollNative.create(this.totalCount_, Object.entries(this.templateOptions_), { 10639 onCreateNode, 10640 onUpdateNode, 10641 onGetKeys4Range, 10642 onGetTypes4Range, 10643 onSetActiveRange 10644 }); 10645 RepeatVirtualScrollNative.onMove(this.onMoveHandler_); 10646 10647 } 10648 reRender() { 10649 10650 // When this.totalCount_ == 0 need render to clear visible items 10651 if (this.hasVisibleItemsChanged() || this.totalCount_ === 0) { 10652 this.purgeKeyCache(); 10653 RepeatVirtualScrollNative.updateRenderState(this.totalCount_, true); 10654 10655 } 10656 else { 10657 // avoid re-render when data pushed outside visible area 10658 RepeatVirtualScrollNative.updateRenderState(this.totalCount_, false); 10659 10660 } 10661 } 10662 initialRenderItem(repeatItem) { 10663 // execute the itemGen function 10664 const itemType = this.typeGenFunc_(repeatItem.item, repeatItem.index); 10665 const itemFunc = this.itemGenFuncs_[itemType]; 10666 itemFunc(repeatItem); 10667 } 10668 hasVisibleItemsChanged() { 10669 var _a, _b; 10670 // has any item or ttype in the active range changed? 10671 for (let i in this.lastActiveRangeData_) { 10672 if (!(i in this.arr_)) { 10673 return true; 10674 } 10675 const oldItem = (_a = this.lastActiveRangeData_[+i]) === null || _a === void 0 ? void 0 : _a.item; 10676 const oldType = (_b = this.lastActiveRangeData_[+i]) === null || _b === void 0 ? void 0 : _b.ttype; 10677 const newItem = this.arr_[+i]; 10678 const newType = this.typeGenFunc_(this.arr_[+i], +i); 10679 if (oldItem !== newItem) { 10680 10681 return true; 10682 } 10683 if (oldType !== newType) { 10684 10685 return true; 10686 } 10687 } 10688 10689 return false; 10690 } 10691 /** 10692 * maintain: index <-> key mapping 10693 * create new key from keyGen function if not in cache 10694 * check for duplicate key, and create random key if duplicate found 10695 * @param forIndex 10696 * @returns unique key 10697 */ 10698 getOrMakeKey4Index(forIndex) { 10699 let key = this.key4Index_.get(forIndex); 10700 if (!key) { 10701 key = this.keyGenFunc_(this.arr_[forIndex], forIndex); 10702 const usedIndex = this.index4Key_.get(key); 10703 if (usedIndex !== undefined) { 10704 // duplicate key 10705 stateMgmtConsole.applicationError(`Repeat key gen function elmtId ${this.repeatElmtId_}: Detected duplicate key ${key} for indices ${forIndex} and ${usedIndex}. \ 10706 Generated random key will decrease Repeat performance. Correct the Key gen function in your application!`); 10707 key = `___${forIndex}_+_${key}_+_${Math.random()}`; 10708 } 10709 this.key4Index_.set(forIndex, key); 10710 this.index4Key_.set(key, forIndex); 10711 } 10712 return key; 10713 } 10714 purgeKeyCache() { 10715 this.key4Index_.clear(); 10716 this.index4Key_.clear(); 10717 } 10718} 10719; 10720/* 10721 * Copyright (c) 2024 Huawei Device Co., Ltd. 10722 * Licensed under the Apache License, Version 2.0 (the "License"); 10723 * you may not use this file except in compliance with the License. 10724 * You may obtain a copy of the License at 10725 * 10726 * http://www.apache.org/licenses/LICENSE-2.0 10727 * 10728 * Unless required by applicable law or agreed to in writing, software 10729 * distributed under the License is distributed on an "AS IS" BASIS, 10730 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10731 * See the License for the specific language governing permissions and 10732 * limitations under the License. 10733 */ 10734; 10735class StorageHelper { 10736 constructor() { 10737 this.oldTypeValues_ = new Map(); 10738 } 10739 getConnectedKey(type, keyOrDefaultCreator) { 10740 if (keyOrDefaultCreator === null || keyOrDefaultCreator === undefined) { 10741 stateMgmtConsole.applicationWarn(StorageHelper.NULL_OR_UNDEFINED_KEY + ', try to use the type name as key'); 10742 } 10743 if (typeof keyOrDefaultCreator === 'string') { 10744 return keyOrDefaultCreator; 10745 } 10746 return this.getTypeName(type); 10747 } 10748 getKeyOrTypeName(keyOrType) { 10749 if (typeof keyOrType === 'function') { 10750 keyOrType = this.getTypeName(keyOrType); 10751 } 10752 return keyOrType; 10753 } 10754 checkTypeByName(key, type, oldType) { 10755 if (this.getTypeName(type) !== oldType) { 10756 throw new Error(`The type mismatches when use the key '${key}' in storage`); 10757 } 10758 } 10759 checkTypeByInstanceOf(key, type, ins) { 10760 this.checkTypeIsFunc(type); 10761 if (!(ins instanceof type)) { 10762 throw new Error(`The type mismatches when use the key '${key}' in storage`); 10763 } 10764 } 10765 getTypeName(type) { 10766 this.checkTypeIsFunc(type); 10767 let name = type.name; 10768 while (name === undefined) { 10769 type = Object.getPrototypeOf(type); 10770 if (!type) { 10771 break; 10772 } 10773 name = type.name; 10774 } 10775 return name; 10776 } 10777 isKeyValid(key) { 10778 if (typeof key !== 'string') { 10779 throw new Error(StorageHelper.INVALID_TYPE); 10780 } 10781 // The key string is empty 10782 if (key === '') { 10783 stateMgmtConsole.applicationError(StorageHelper.EMPTY_STRING_KEY); 10784 return false; 10785 } 10786 const len = key.length; 10787 // The key string length should shorter than 1024 10788 if (len >= 1024) { 10789 stateMgmtConsole.applicationError(StorageHelper.INVALID_LENGTH_KEY); 10790 return false; 10791 } 10792 if (len < 2 || len > 255) { 10793 stateMgmtConsole.applicationWarn(StorageHelper.INVALID_LENGTH_KEY); 10794 } 10795 if (!/^[0-9a-zA-Z_]+$/.test(key)) { 10796 stateMgmtConsole.applicationWarn(StorageHelper.INVALID_CHARACTER_KEY); 10797 } 10798 return true; 10799 } 10800 checkTypeIsFunc(type) { 10801 if (typeof type !== 'function') { 10802 throw new Error(StorageHelper.INVALID_TYPE); 10803 } 10804 } 10805} 10806StorageHelper.INVALID_DEFAULT_VALUE = 'The default creator should be function when first connect'; 10807StorageHelper.DELETE_NOT_EXIST_KEY = 'The key to be deleted does not exist'; 10808StorageHelper.INVALID_TYPE = 'The type should have function constructor signature when use storage'; 10809StorageHelper.EMPTY_STRING_KEY = 'Cannot use empty string as the key'; 10810StorageHelper.INVALID_LENGTH_KEY = 'Cannot use the key! The length of key should be 2 to 255'; 10811StorageHelper.INVALID_CHARACTER_KEY = 'Cannot use the key! The value of key can only consist of letters, digits and underscores'; 10812StorageHelper.NULL_OR_UNDEFINED_KEY = `The parameter cannot be null or undefined`; 10813class AppStorageV2Impl extends StorageHelper { 10814 constructor() { 10815 super(); 10816 this.memorizedValues_ = new Map(); 10817 } 10818 static instance() { 10819 if (AppStorageV2Impl.instance_) { 10820 return AppStorageV2Impl.instance_; 10821 } 10822 AppStorageV2Impl.instance_ = new AppStorageV2Impl(); 10823 return AppStorageV2Impl.instance_; 10824 } 10825 connect(type, keyOrDefaultCreator, defaultCreator) { 10826 const key = this.getConnectedKey(type, keyOrDefaultCreator); 10827 if (!this.isKeyValid(key)) { 10828 return undefined; 10829 } 10830 if (typeof keyOrDefaultCreator === 'function') { 10831 defaultCreator = keyOrDefaultCreator; 10832 } 10833 if (!this.memorizedValues_.has(key)) { 10834 if (typeof defaultCreator !== 'function') { 10835 throw new Error(AppStorageV2Impl.INVALID_DEFAULT_VALUE); 10836 } 10837 const defaultValue = defaultCreator(); 10838 this.checkTypeByInstanceOf(key, type, defaultValue); 10839 this.memorizedValues_.set(key, defaultValue); 10840 this.oldTypeValues_.set(key, this.getTypeName(type)); 10841 return defaultValue; 10842 } 10843 this.checkTypeByName(key, type, this.oldTypeValues_.get(key)); 10844 const existedValue = this.memorizedValues_.get(key); 10845 return existedValue; 10846 } 10847 remove(keyOrType) { 10848 if (keyOrType === null || keyOrType === undefined) { 10849 stateMgmtConsole.applicationWarn(AppStorageV2Impl.NULL_OR_UNDEFINED_KEY); 10850 return; 10851 } 10852 const key = this.getKeyOrTypeName(keyOrType); 10853 if (!this.isKeyValid(key)) { 10854 return; 10855 } 10856 this.removeFromMemory(key); 10857 } 10858 getMemoryKeys() { 10859 return Array.from(this.memorizedValues_.keys()); 10860 } 10861 removeFromMemory(key) { 10862 const isDeleted = this.memorizedValues_.delete(key); 10863 if (!isDeleted) { 10864 stateMgmtConsole.applicationWarn(AppStorageV2Impl.DELETE_NOT_EXIST_KEY); 10865 } 10866 else { 10867 this.oldTypeValues_.delete(key); 10868 } 10869 } 10870} 10871AppStorageV2Impl.instance_ = undefined; 10872class PersistenceV2Impl extends StorageHelper { 10873 constructor() { 10874 super(); 10875 this.cb_ = undefined; 10876 this.map_ = new Proxy(new Map(), new SetMapProxyHandler()); 10877 this.keysArr_ = new Set(); 10878 this.idToKey_ = new Map(); 10879 } 10880 static instance() { 10881 if (PersistenceV2Impl.instance_) { 10882 return PersistenceV2Impl.instance_; 10883 } 10884 PersistenceV2Impl.instance_ = new PersistenceV2Impl(); 10885 return PersistenceV2Impl.instance_; 10886 } 10887 static configureBackend(storage) { 10888 PersistenceV2Impl.storage_ = storage; 10889 } 10890 connect(type, keyOrDefaultCreator, defaultCreator) { 10891 this.checkTypeIsClassObject(type); 10892 const key = this.getRightKey(type, keyOrDefaultCreator); 10893 if (!this.isKeyValid(key)) { 10894 return undefined; 10895 } 10896 if (typeof keyOrDefaultCreator === 'function') { 10897 defaultCreator = keyOrDefaultCreator; 10898 } 10899 // In memory 10900 if (this.map_.has(key)) { 10901 const existedValue = this.map_.get(key); 10902 this.checkTypeByName(key, type, this.oldTypeValues_.get(key)); 10903 return existedValue; 10904 } 10905 const observedValue = this.getConnectDefaultValue(key, type, defaultCreator); 10906 if (!observedValue) { 10907 return undefined; 10908 } 10909 const id = ++PersistenceV2Impl.nextPersistId_; 10910 this.idToKey_.set(id, key); 10911 // Not in memory, but in disk 10912 if (PersistenceV2Impl.storage_.has(key)) { 10913 return this.getValueFromDisk(key, id, observedValue, type); 10914 } 10915 // Neither in memory or in disk 10916 return this.setValueToDisk(key, id, observedValue, type); 10917 } 10918 keys() { 10919 try { 10920 if (!this.keysArr_.size) { 10921 this.keysArr_ = this.getKeysArrFromStorage(); 10922 } 10923 } 10924 catch (err) { 10925 if (this.cb_ && typeof this.cb_ === 'function') { 10926 this.cb_('', "unknown" /* Unknown */, `fail to get all persisted keys`); 10927 return; 10928 } 10929 throw new Error(err); 10930 } 10931 return Array.from(this.keysArr_); 10932 } 10933 remove(keyOrType) { 10934 if (keyOrType === null || keyOrType === undefined) { 10935 stateMgmtConsole.applicationWarn(PersistenceV2Impl.NULL_OR_UNDEFINED_KEY); 10936 return; 10937 } 10938 this.checkTypeIsClassObject(keyOrType); 10939 const key = this.getKeyOrTypeName(keyOrType); 10940 if (!this.isKeyValid(key)) { 10941 return; 10942 } 10943 this.disconnectValue(key); 10944 } 10945 save(keyOrType) { 10946 if (keyOrType === null || keyOrType === undefined) { 10947 stateMgmtConsole.applicationWarn(PersistenceV2Impl.NULL_OR_UNDEFINED_KEY); 10948 return; 10949 } 10950 this.checkTypeIsClassObject(keyOrType); 10951 const key = this.getKeyOrTypeName(keyOrType); 10952 if (!this.isKeyValid(key)) { 10953 return; 10954 } 10955 if (!this.map_.has(key)) { 10956 stateMgmtConsole.applicationWarn(`Cannot save the key '${key}'! The key is disconnected`); 10957 return; 10958 } 10959 try { 10960 PersistenceV2Impl.storage_.set(key, JSONCoder.stringify(this.map_.get(key))); 10961 } 10962 catch (err) { 10963 this.errorHelper(key, "serialization" /* Serialization */, err); 10964 } 10965 } 10966 notifyOnError(cb) { 10967 this.cb_ = cb; 10968 } 10969 onChangeObserved(persistKeys) { 10970 this.writeAllChangedToFile(persistKeys); 10971 } 10972 connectNewValue(key, newValue, typeName) { 10973 this.map_.set(key, newValue); 10974 this.oldTypeValues_.set(key, typeName); 10975 this.storeKeyToPersistenceV2(key); 10976 } 10977 disconnectValue(key) { 10978 this.map_.delete(key); 10979 this.oldTypeValues_.delete(key); 10980 this.removeFromPersistenceV2(key); 10981 } 10982 checkTypeIsClassObject(keyOrType) { 10983 if ((typeof keyOrType !== 'string' && typeof keyOrType !== 'function') || 10984 PersistenceV2Impl.NOT_SUPPORT_TYPES_.includes(keyOrType)) { 10985 throw new Error(PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_); 10986 } 10987 } 10988 getRightKey(type, keyOrDefaultCreator) { 10989 const key = this.getConnectedKey(type, keyOrDefaultCreator); 10990 if (key === undefined) { 10991 throw new Error(PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_); 10992 } 10993 if (key === PersistenceV2Impl.KEYS_ARR_) { 10994 this.errorHelper(key, "quota" /* Quota */, `The key '${key}' cannot be used`); 10995 return undefined; 10996 } 10997 return key; 10998 } 10999 getConnectDefaultValue(key, type, defaultCreator) { 11000 if (!PersistenceV2Impl.storage_) { 11001 this.errorHelper(key, "unknown" /* Unknown */, `The storage is null`); 11002 return undefined; 11003 } 11004 if (typeof defaultCreator !== 'function') { 11005 throw new Error(PersistenceV2Impl.INVALID_DEFAULT_VALUE); 11006 } 11007 const observedValue = defaultCreator(); 11008 this.checkTypeByInstanceOf(key, type, observedValue); 11009 if (this.isNotClassObject(observedValue)) { 11010 throw new Error(PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_); 11011 } 11012 return observedValue; 11013 } 11014 getValueFromDisk(key, id, observedValue, type) { 11015 let newObservedValue; 11016 try { 11017 const json = PersistenceV2Impl.storage_.get(key); 11018 // Adding ref for persistence 11019 ObserveV2.getObserve().startRecordDependencies(this, id); 11020 newObservedValue = JSONCoder.parseTo(observedValue, json); 11021 ObserveV2.getObserve().stopRecordDependencies(); 11022 } 11023 catch (err) { 11024 this.errorHelper(key, "serialization" /* Serialization */, err); 11025 } 11026 this.checkTypeByInstanceOf(key, type, newObservedValue); 11027 this.connectNewValue(key, newObservedValue, this.getTypeName(type)); 11028 return newObservedValue; 11029 } 11030 setValueToDisk(key, id, observedValue, type) { 11031 ObserveV2.getObserve().startRecordDependencies(this, id); 11032 // Map is a proxy object. When it is connected for the first time, map.has is used to add dependencies, 11033 // and map.set is used to trigger writing to disk. 11034 const hasKey = this.map_.has(key); 11035 ObserveV2.getObserve().stopRecordDependencies(); 11036 // Writing to persistence by ref 11037 if (!hasKey) { 11038 this.connectNewValue(key, observedValue, this.getTypeName(type)); 11039 } 11040 return observedValue; 11041 } 11042 writeAllChangedToFile(persistKeys) { 11043 for (let i = 0; i < persistKeys.length; ++i) { 11044 const id = persistKeys[i]; 11045 const key = this.idToKey_.get(id); 11046 try { 11047 const hasKey = this.map_.has(key); 11048 if (hasKey) { 11049 const value = this.map_.get(key); 11050 ObserveV2.getObserve().startRecordDependencies(this, id); 11051 const json = JSONCoder.stringify(value); 11052 ObserveV2.getObserve().stopRecordDependencies(); 11053 if (this.isOverSizeLimit(json)) { 11054 stateMgmtConsole.applicationError(`Cannot store the key '${key}'! The length of data must be less than ${PersistenceV2Impl.MAX_DATA_LENGTH_}`); 11055 } 11056 else { 11057 PersistenceV2Impl.storage_.set(key, json); 11058 } 11059 } 11060 } 11061 catch (err) { 11062 if (this.cb_ && typeof this.cb_ === 'function') { 11063 this.cb_(key, "serialization" /* Serialization */, err); 11064 continue; 11065 } 11066 stateMgmtConsole.applicationError(`For '${key}' key: ` + err); 11067 } 11068 } 11069 } 11070 isOverSizeLimit(json) { 11071 if (typeof json !== 'string') { 11072 return false; 11073 } 11074 return json.length >= PersistenceV2Impl.MAX_DATA_LENGTH_; 11075 } 11076 isNotClassObject(value) { 11077 return Array.isArray(value) || this.isNotSupportType(value); 11078 } 11079 isNotSupportType(value) { 11080 for (let i = 0; i < PersistenceV2Impl.NOT_SUPPORT_TYPES_.length; ++i) { 11081 if (value instanceof PersistenceV2Impl.NOT_SUPPORT_TYPES_[i]) { 11082 return true; 11083 } 11084 } 11085 return false; 11086 } 11087 storeKeyToPersistenceV2(key) { 11088 try { 11089 if (this.keysArr_.has(key)) { 11090 return; 11091 } 11092 // Initializing the keys arr in memory 11093 if (!this.keysArr_.size) { 11094 this.keysArr_ = this.getKeysArrFromStorage(); 11095 } 11096 this.keysArr_.add(key); 11097 // Updating the keys arr in disk 11098 this.storeKeysArrToStorage(this.keysArr_); 11099 } 11100 catch (err) { 11101 this.errorHelper(key, "unknown" /* Unknown */, `fail to store the key '${key}'`); 11102 } 11103 } 11104 removeFromPersistenceV2(key) { 11105 try { 11106 if (!PersistenceV2Impl.storage_.has(key)) { 11107 stateMgmtConsole.applicationWarn(PersistenceV2Impl.DELETE_NOT_EXIST_KEY); 11108 return; 11109 } 11110 PersistenceV2Impl.storage_.delete(key); 11111 // The first call to remove 11112 if (!this.keysArr_.has(key)) { 11113 this.keysArr_ = this.getKeysArrFromStorage(); 11114 } 11115 this.keysArr_.delete(key); 11116 this.storeKeysArrToStorage(this.keysArr_); 11117 } 11118 catch (err) { 11119 this.errorHelper(key, "unknown" /* Unknown */, `fail to remove the key '${key}'`); 11120 } 11121 } 11122 getKeysArrFromStorage() { 11123 if (!PersistenceV2Impl.storage_.has(PersistenceV2Impl.KEYS_ARR_)) { 11124 return this.keysArr_; 11125 } 11126 const jsonKeysArr = PersistenceV2Impl.storage_.get(PersistenceV2Impl.KEYS_ARR_); 11127 return new Set(JSON.parse(jsonKeysArr)); 11128 } 11129 storeKeysArrToStorage(keysArr) { 11130 PersistenceV2Impl.storage_.set(PersistenceV2Impl.KEYS_ARR_, JSON.stringify(Array.from(keysArr))); 11131 } 11132 errorHelper(key, reason, message) { 11133 if (this.cb_ && typeof this.cb_ === 'function') { 11134 this.cb_(key, reason, message); 11135 return; 11136 } 11137 if (!key) { 11138 key = 'unknown'; 11139 } 11140 throw new Error(`For '${key}' key: ` + message); 11141 } 11142} 11143PersistenceV2Impl.MIN_PERSISTENCE_ID = 0x1010000000000; 11144PersistenceV2Impl.nextPersistId_ = PersistenceV2Impl.MIN_PERSISTENCE_ID; 11145PersistenceV2Impl.NOT_SUPPORT_TYPE_MESSAGE_ = 'Not support! Can only use the class object in Persistence'; 11146PersistenceV2Impl.KEYS_ARR_ = '___keys_arr'; 11147PersistenceV2Impl.MAX_DATA_LENGTH_ = 8000; 11148PersistenceV2Impl.NOT_SUPPORT_TYPES_ = [Array, Set, Map, WeakSet, WeakMap, Date, Boolean, Number, String, Symbol, BigInt, RegExp, Function, Promise, ArrayBuffer]; 11149PersistenceV2Impl.instance_ = undefined; 11150/* 11151 * Copyright (c) 2024 Huawei Device Co., Ltd. 11152 * Licensed under the Apache License, Version 2.0 (the "License"); 11153 * you may not use this file except in compliance with the License. 11154 * You may obtain a copy of the License at 11155 * 11156 * http://www.apache.org/licenses/LICENSE-2.0 11157 * 11158 * Unless required by applicable law or agreed to in writing, software 11159 * distributed under the License is distributed on an "AS IS" BASIS, 11160 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11161 * See the License for the specific language governing permissions and 11162 * limitations under the License. 11163 */ 11164class UIUtilsImpl { 11165 getTarget(source) { 11166 if (!source || typeof source !== 'object') { 11167 return source; 11168 } 11169 if (ObservedObject.IsObservedObject(source)) { 11170 // V1 Proxy object 11171 return ObservedObject.GetRawObject(source); 11172 } 11173 else if (source[ObserveV2.SYMBOL_PROXY_GET_TARGET]) { 11174 // V2 Proxy object 11175 return source[ObserveV2.SYMBOL_PROXY_GET_TARGET]; 11176 } 11177 else { 11178 // other situation, not handle 11179 return source; 11180 } 11181 } 11182 makeObserved(target) { 11183 // mark makeObserved using V2 feature 11184 ConfigureStateMgmt.instance.usingV2ObservedTrack('makeObserved', 'use'); 11185 return RefInfo.get(target).proxy; 11186 } 11187 static instance() { 11188 if (UIUtilsImpl.instance_) { 11189 return UIUtilsImpl.instance_; 11190 } 11191 UIUtilsImpl.instance_ = new UIUtilsImpl(); 11192 return UIUtilsImpl.instance_; 11193 } 11194} 11195UIUtilsImpl.instance_ = undefined; 11196/* 11197 * Copyright (c) 2024 Huawei Device Co., Ltd. 11198 * Licensed under the Apache License, Version 2.0 (the "License"); 11199 * you may not use this file except in compliance with the License. 11200 * You may obtain a copy of the License at 11201 * 11202 * http://www.apache.org/licenses/LICENSE-2.0 11203 * 11204 * Unless required by applicable law or agreed to in writing, software 11205 * distributed under the License is distributed on an "AS IS" BASIS, 11206 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11207 * See the License for the specific language governing permissions and 11208 * limitations under the License. 11209 */ 11210class GestureStyle extends NativeGestureStyle { 11211 constructor(arg) { 11212 super(arg); 11213 this.arg_ = arg; 11214 } 11215} 11216/* 11217 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 11218 * Licensed under the Apache License, Version 2.0 (the "License"); 11219 * you may not use this file except in compliance with the License. 11220 * You may obtain a copy of the License at 11221 * 11222 * http://www.apache.org/licenses/LICENSE-2.0 11223 * 11224 * Unless required by applicable law or agreed to in writing, software 11225 * distributed under the License is distributed on an "AS IS" BASIS, 11226 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11227 * See the License for the specific language governing permissions and 11228 * limitations under the License. 11229 */ 11230 11231PersistenceV2Impl.configureBackend(new Storage()); 11232PersistentStorage.configureBackend(new Storage()); 11233Environment.configureBackend(new EnvironmentSetting()); 11234 11235 11236