• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      set(_) {
66        const error = `@Computed ${propertyKey} is readonly, cannot set value for it`;
67        stateMgmtConsole.applicationError(error);
68        throw new Error(error);
69      },
70      enumerable: true
71    });
72
73    this.target_[cachedProp] = this.observeObjectAccess();
74    return this.computedId_;
75  }
76
77  public fireChange(): void {
78    let newVal = this.observeObjectAccess();
79    let cachedProp = ComputedV2.COMPUTED_CACHED_PREFIX + this.prop_;
80    if (this.target_[cachedProp] !== newVal) {
81      this.target_[cachedProp] = newVal;
82        ObserveV2.getObserve().fireChange(this.target_, this.prop_);
83    }
84  }
85
86  public getTarget() : object {
87    return this.target_;
88  }
89
90  public getProp() : string {
91    return this.prop_;
92  }
93
94  public getComputedFuncName(): string {
95    return this.propertyComputeFunc_.name;
96  }
97
98  // register current watchId while executing compute function
99  private observeObjectAccess(): Object | undefined {
100    ObserveV2.getObserve().startRecordDependencies(this, this.computedId_);
101    let ret;
102
103    try {
104        ret = this.propertyComputeFunc_.call(this.target_);
105    } catch (e) {
106        stateMgmtConsole.applicationError(`@Computed Exception caught for ${this.propertyComputeFunc_.name}`, e.toString());
107        ret = undefined;
108        throw e;
109    } finally {
110        ObserveV2.getObserve().stopRecordDependencies();
111    }
112
113    return ret;
114  }
115
116  public static clearComputedFromTarget(target: Object): void {
117    let meta: Object;
118    if (!target || typeof target !== 'object' ||
119        !(meta = target[ObserveV2.COMPUTED_REFS]) || typeof meta !== 'object') {
120      return;
121    }
122
123    stateMgmtConsole.debug(`ComputedV2: clearComputedFromTarget: from target ${target.constructor?.name} computedIds to clear ${JSON.stringify(Array.from(Object.values(meta)))}`);
124    Array.from(Object.values(meta)).forEach((computed: ComputedV2) => ObserveV2.getObserve().clearWatch(computed.computedId_));
125  }
126
127   /**
128   * @function resetComputed
129   * @description
130   * Recalculates the value of the specified computed property based on the current values
131   * of the input variables
132   *
133   * @param {string} computedName - The name of the computed property to be reset.
134  */
135   public resetComputed(computedName: string) : void {
136    let newVal = this.observeObjectAccess();
137
138    let cachedProp = ComputedV2.COMPUTED_CACHED_PREFIX + computedName;
139    if (this.target_[cachedProp] !== newVal) {
140      this.target_[cachedProp] = newVal;
141      ObserveV2.getObserve().fireChange(this.target_, computedName);
142    }
143  }
144
145}
146
147interface AsyncAddComputedJobEntryV2 {
148  target: Object;
149  name: string;
150}
151class AsyncAddComputedV2 {
152  static computedVars : Array<AsyncAddComputedJobEntryV2> = new Array<AsyncAddComputedJobEntryV2>();
153
154  static addComputed(target: Object, name: string): void {
155    if (AsyncAddComputedV2.computedVars.length === 0) {
156      Promise.resolve(true)
157      .then(AsyncAddComputedV2.run)
158      .catch(error => {
159        stateMgmtConsole.applicationError(`Exception caught in @Computed ${name}`, error);
160        _arkUIUncaughtPromiseError(error);
161      });
162    }
163    AsyncAddComputedV2.computedVars.push({target: target, name: name});
164  }
165
166  static run(): void {
167    AsyncAddComputedV2.computedVars.forEach((computedVar : AsyncAddComputedJobEntryV2) =>
168      ObserveV2.getObserve().constructComputed(computedVar.target, computedVar.name));
169    // according to stackoverflow this is the fastest way to clear an Array
170    // ref https://stackoverflow.com/questions/1232040/how-do-i-empty-an-array-in-javascript
171    AsyncAddComputedV2.computedVars.length = 0;
172  }
173}
174