1/* 2 * Copyright (c) 2024 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/** 17 * 18 * This file includes only framework internal classes and functions 19 * non are part of SDK. Do not access from app. 20 * 21 */ 22 23/** 24 * ComputedV2 25 * one ComputedV2 object per @Computed variable 26 * computedId_ - similar to elmtId, identify one ComputedV2 in Observe.idToCmp Map 27 * observeObjectAccess = calculate the compute function and create dependencies to 28 * source variables 29 * fireChange - execute compute function and re-new dependencies with observeObjectAccess 30 */ 31class ComputedV2 { 32 33 // start with high number to avoid same id as elmtId for components. 34 public static readonly MIN_COMPUTED_ID = 0x1000000000; 35 private static nextCompId_ = ComputedV2.MIN_COMPUTED_ID; 36 37 // name of @Computed property 38 private prop_: string; 39 40 // owning object of @Computed property 41 private target_: object; 42 43 // computation function for property 44 private propertyComputeFunc_: () => any; 45 private computedId_: number; 46 47 public static readonly COMPUTED_PREFIX = '___comp_'; 48 public static readonly COMPUTED_CACHED_PREFIX = '___comp_cached_'; 49 50 constructor(target: object, prop: string, func: (...args: any[]) => any) { 51 this.target_ = target; 52 this.propertyComputeFunc_ = func; 53 this.computedId_ = ++ComputedV2.nextCompId_; 54 this.prop_ = prop; 55 } 56 57 public InitRun(): number { 58 let cachedProp = ComputedV2.COMPUTED_CACHED_PREFIX + this.prop_; 59 let propertyKey = this.prop_; 60 Reflect.defineProperty(this.target_, propertyKey, { 61 get() { 62 ObserveV2.getObserve().addRef(this, propertyKey); 63 return ObserveV2.autoProxyObject(this, cachedProp); 64 }, 65 enumerable: true 66 }); 67 68 this.target_[cachedProp] = this.observeObjectAccess(); 69 return this.computedId_; 70 } 71 72 public fireChange(): void { 73 let newVal = this.observeObjectAccess(); 74 let cachedProp = ComputedV2.COMPUTED_CACHED_PREFIX + this.prop_; 75 if (this.target_[cachedProp] !== newVal) { 76 this.target_[cachedProp] = newVal; 77 ObserveV2.getObserve().fireChange(this.target_, this.prop_); 78 } 79 } 80 81 public getTarget() : object { 82 return this.target_; 83 } 84 85 public getProp() : string { 86 return this.prop_; 87 } 88 89 // register current watchId while executing compute function 90 private observeObjectAccess(): Object | undefined { 91 ObserveV2.getObserve().startRecordDependencies(this, this.computedId_); 92 let ret; 93 94 try { 95 ret = this.propertyComputeFunc_.call(this.target_); 96 } catch (e) { 97 stateMgmtConsole.applicationError(`@Computed Exception caught for ${this.propertyComputeFunc_.name}`, e.toString()); 98 ret = undefined; 99 throw e; 100 } finally { 101 ObserveV2.getObserve().stopRecordDependencies(); 102 } 103 104 return ret; 105 } 106} 107 108interface AsyncAddComputedJobEntryV2 { 109 target: Object; 110 name: string; 111} 112class AsyncAddComputedV2 { 113 static computedVars : Array<AsyncAddComputedJobEntryV2> = new Array<AsyncAddComputedJobEntryV2>(); 114 115 static addComputed(target: Object, name: string): void { 116 if (AsyncAddComputedV2.computedVars.length === 0) { 117 Promise.resolve(true) 118 .then(AsyncAddComputedV2.run) 119 .catch(error => { 120 stateMgmtConsole.applicationError(`Exception caught in @Computed ${name}`, error); 121 _arkUIUncaughtPromiseError(error); 122 }); 123 } 124 AsyncAddComputedV2.computedVars.push({target: target, name: name}); 125 } 126 127 static run(): void { 128 AsyncAddComputedV2.computedVars.forEach((computedVar : AsyncAddComputedJobEntryV2) => 129 ObserveV2.getObserve().constructComputed(computedVar.target, computedVar.name)); 130 // according to stackoverflow this is the fastest way to clear an Array 131 // ref https://stackoverflow.com/questions/1232040/how-do-i-empty-an-array-in-javascript 132 AsyncAddComputedV2.computedVars.length = 0; 133 } 134} 135