• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024-2025 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
16import { Constant } from '../../base/Constant';
17import { Decorator } from '../../base/Decorator';
18import {
19    AbstractInvokeExpr,
20    ArkConditionExpr,
21    ArkInstanceInvokeExpr,
22    ArkNewExpr,
23    ArkNormalBinopExpr,
24    ArkPtrInvokeExpr,
25    ArkStaticInvokeExpr,
26} from '../../base/Expr';
27import { Local } from '../../base/Local';
28import { ArkArrayRef, ArkInstanceFieldRef, ArkThisRef } from '../../base/Ref';
29import { ArkAssignStmt, ArkInvokeStmt, Stmt } from '../../base/Stmt';
30import { ClassType, FunctionType, Type } from '../../base/Type';
31import { Value } from '../../base/Value';
32import {
33    BUILDER_DECORATOR,
34    BUILDER_PARAM_DECORATOR,
35    COMPONENT_BRANCH_FUNCTION,
36    COMPONENT_CREATE_FUNCTION,
37    COMPONENT_CUSTOMVIEW,
38    COMPONENT_FOR_EACH,
39    COMPONENT_IF,
40    COMPONENT_IF_BRANCH,
41    COMPONENT_LAZY_FOR_EACH,
42    COMPONENT_POP_FUNCTION,
43    COMPONENT_REPEAT,
44    isEtsContainerComponent,
45    SPECIAL_CONTAINER_COMPONENT,
46} from '../../common/EtsConst';
47import { ArkClass, ClassCategory } from '../../model/ArkClass';
48import { ArkField } from '../../model/ArkField';
49import { ArkMethod } from '../../model/ArkMethod';
50import { ClassSignature, MethodSignature } from '../../model/ArkSignature';
51import { Cfg } from '../Cfg';
52import Logger, { LOG_MODULE_TYPE } from '../../../utils/logger';
53import { ViewTree, ViewTreeNode } from '../ViewTree';
54import { ModelUtils } from '../../common/ModelUtils';
55import { Scene } from '../../../Scene';
56import { TEMP_LOCAL_PREFIX } from '../../common/Const';
57
58const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'ViewTreeBuilder');
59const COMPONENT_CREATE_FUNCTIONS: Set<string> = new Set([COMPONENT_CREATE_FUNCTION, COMPONENT_BRANCH_FUNCTION]);
60
61function backtraceLocalInitValue(value: Local): Local | Value {
62    let stmt = value.getDeclaringStmt();
63    if (stmt instanceof ArkAssignStmt) {
64        let rightOp = stmt.getRightOp();
65        if (rightOp instanceof Local) {
66            return backtraceLocalInitValue(rightOp);
67        } else if (rightOp instanceof ArkInstanceFieldRef && rightOp.getBase().getName().startsWith(TEMP_LOCAL_PREFIX)) {
68            return backtraceLocalInitValue(rightOp.getBase());
69        } else if (rightOp instanceof ArkArrayRef) {
70            return backtraceLocalInitValue(rightOp.getBase());
71        }
72        return rightOp;
73    }
74    return value;
75}
76
77type ObjectLiteralMap = Map<ArkField, Value | ObjectLiteralMap>;
78
79function parseObjectLiteral(objectLiteralCls: ArkClass | null, scene: Scene): ObjectLiteralMap {
80    let map: ObjectLiteralMap = new Map();
81    if (objectLiteralCls?.getCategory() !== ClassCategory.OBJECT) {
82        return map;
83    }
84    objectLiteralCls?.getFields().forEach(field => {
85        let stmts = field.getInitializer();
86        if (stmts.length === 0) {
87            return;
88        }
89
90        let assignStmt = stmts[stmts.length - 1];
91        if (!(assignStmt instanceof ArkAssignStmt)) {
92            return;
93        }
94
95        let value = assignStmt.getRightOp();
96        if (value instanceof Local) {
97            value = backtraceLocalInitValue(value);
98        }
99
100        map.set(field, value);
101        if (value instanceof ArkNewExpr) {
102            let subCls = ModelUtils.getArkClassInBuild(scene, value.getClassType());
103            let childMap = parseObjectLiteral(subCls, scene);
104            if (childMap) {
105                map.set(field, childMap);
106            }
107        }
108    });
109
110    return map;
111}
112
113class StateValuesUtils {
114    private declaringArkClass: ArkClass;
115
116    constructor(declaringArkClass: ArkClass) {
117        this.declaringArkClass = declaringArkClass;
118    }
119
120    public static getInstance(declaringArkClass: ArkClass): StateValuesUtils {
121        return new StateValuesUtils(declaringArkClass);
122    }
123
124    public parseStmtUsesStateValues(
125        stmt: Stmt,
126        uses: Set<ArkField> = new Set(),
127        wholeMethod: boolean = false,
128        visitor: Set<MethodSignature | Stmt> = new Set()
129    ): Set<ArkField> {
130        if (visitor.has(stmt)) {
131            return uses;
132        }
133        visitor.add(stmt);
134        let values = stmt.getUses();
135        if (stmt instanceof ArkAssignStmt) {
136            values.push(stmt.getLeftOp());
137        }
138
139        for (const v of values) {
140            this.parseValueUsesStateValues(v, uses, wholeMethod, visitor);
141        }
142        return uses;
143    }
144
145    private objectLiteralMapUsedStateValues(uses: Set<ArkField>, map: ObjectLiteralMap): void {
146        for (const [_, value] of map) {
147            if (value instanceof ArkInstanceFieldRef) {
148                let srcField = this.declaringArkClass.getFieldWithName(value.getFieldName());
149                let decorators = srcField?.getStateDecorators();
150                if (srcField && decorators && decorators.length > 0) {
151                    uses.add(srcField);
152                }
153            } else if (value instanceof Map) {
154                this.objectLiteralMapUsedStateValues(uses, value);
155            } else if (value instanceof ArkNormalBinopExpr || value instanceof ArkConditionExpr) {
156                this.parseValueUsesStateValues(value.getOp1(), uses);
157                this.parseValueUsesStateValues(value.getOp2(), uses);
158            }
159        }
160    }
161
162    public parseObjectUsedStateValues(type: Type, uses: Set<ArkField> = new Set()): Set<ArkField> {
163        if (!(type instanceof ClassType)) {
164            return uses;
165        }
166        let cls = ModelUtils.getArkClassInBuild(this.declaringArkClass.getDeclaringArkFile().getScene(), type);
167        let map = parseObjectLiteral(cls, this.declaringArkClass.getDeclaringArkFile().getScene());
168        this.objectLiteralMapUsedStateValues(uses, map);
169        return uses;
170    }
171
172    private parseMethodUsesStateValues(methodSignature: MethodSignature, uses: Set<ArkField>, visitor: Set<MethodSignature | Stmt> = new Set()): void {
173        if (visitor.has(methodSignature)) {
174            return;
175        }
176        visitor.add(methodSignature);
177        let method = this.declaringArkClass.getDeclaringArkFile().getScene().getMethod(methodSignature);
178        if (!method) {
179            return;
180        }
181        let stmts = method.getCfg()?.getStmts();
182        if (!stmts) {
183            return;
184        }
185        for (const stmt of stmts) {
186            this.parseStmtUsesStateValues(stmt, uses, true, visitor);
187        }
188    }
189
190    private parseValueUsesStateValues(
191        v: Value,
192        uses: Set<ArkField> = new Set(),
193        wholeMethod: boolean = false,
194        visitor: Set<MethodSignature | Stmt> = new Set()
195    ): Set<ArkField> {
196        if (v instanceof ArkInstanceFieldRef) {
197            let field = this.declaringArkClass.getField(v.getFieldSignature());
198            let decorators = field?.getStateDecorators();
199            if (field && decorators && decorators.length > 0) {
200                uses.add(field);
201            }
202        } else if (v instanceof ArkInstanceInvokeExpr) {
203            this.parseMethodUsesStateValues(v.getMethodSignature(), uses, visitor);
204        } else if (v instanceof Local) {
205            if (v.getName() === 'this') {
206                return uses;
207            }
208            let type = v.getType();
209            if (type instanceof FunctionType) {
210                this.parseMethodUsesStateValues(type.getMethodSignature(), uses, visitor);
211                return uses;
212            }
213            this.parseObjectUsedStateValues(type, uses);
214            let declaringStmt = v.getDeclaringStmt();
215            if (!wholeMethod && declaringStmt) {
216                this.parseStmtUsesStateValues(declaringStmt, uses, wholeMethod, visitor);
217            }
218        }
219
220        return uses;
221    }
222}
223
224enum ViewTreeNodeType {
225    SystemComponent,
226    CustomComponent,
227    Builder,
228    BuilderParam,
229}
230
231class ViewTreeNodeImpl implements ViewTreeNode {
232    name: string;
233    stmts: Map<string, [Stmt, (MethodSignature | ArkInstanceFieldRef | Constant)[]]>;
234    attributes: Map<string, [Stmt, (MethodSignature | ArkInstanceFieldRef | Constant)[]]>;
235    stateValues: Set<ArkField>;
236    parent: ViewTreeNode | null;
237    children: ViewTreeNodeImpl[];
238    classSignature?: MethodSignature | ClassSignature | undefined;
239    signature?: MethodSignature | ClassSignature | undefined;
240    stateValuesTransfer?: Map<ArkField, ArkMethod | ArkField> | undefined;
241    builderParam?: ArkField | undefined;
242    builder?: MethodSignature | undefined;
243    private type: ViewTreeNodeType;
244
245    constructor(name: string) {
246        this.name = name;
247        this.attributes = new Map();
248        this.stmts = this.attributes;
249        this.stateValues = new Set();
250        this.parent = null;
251        this.children = [];
252        this.type = ViewTreeNodeType.SystemComponent;
253    }
254
255    /**
256     * Whether the node type is Builder.
257     * @returns true: node is Builder, false others.
258     */
259    public isBuilder(): boolean {
260        return this.type === ViewTreeNodeType.Builder;
261    }
262
263    /**
264     * @internal
265     */
266    public isBuilderParam(): boolean {
267        return this.type === ViewTreeNodeType.BuilderParam;
268    }
269
270    /**
271     * Whether the node type is custom component.
272     * @returns true: node is custom component, false others.
273     */
274    public isCustomComponent(): boolean {
275        return this.type === ViewTreeNodeType.CustomComponent;
276    }
277
278    /**
279     * walk node and node's children
280     * @param selector Node selector function, return true skipping the follow-up nodes.
281     * @returns
282     *  - true: There are nodes that meet the selector.
283     *  - false: does not exist.
284     */
285    public walk(selector: (item: ViewTreeNode) => boolean, visitor: Set<ViewTreeNode> = new Set()): boolean {
286        if (visitor.has(this)) {
287            return false;
288        }
289
290        let ret: boolean = selector(this);
291        visitor.add(this);
292
293        for (const child of this.children) {
294            ret = ret || child.walk(selector, visitor);
295            if (ret) {
296                break;
297            }
298        }
299        return ret;
300    }
301
302    public static createCustomComponent(): ViewTreeNodeImpl {
303        let instance = new ViewTreeNodeImpl(COMPONENT_CUSTOMVIEW);
304        instance.type = ViewTreeNodeType.CustomComponent;
305        return instance;
306    }
307
308    public static createBuilderNode(): ViewTreeNodeImpl {
309        let instance = new ViewTreeNodeImpl(BUILDER_DECORATOR);
310        instance.type = ViewTreeNodeType.Builder;
311        return instance;
312    }
313
314    public static createBuilderParamNode(): ViewTreeNodeImpl {
315        let instance = new ViewTreeNodeImpl(BUILDER_PARAM_DECORATOR);
316        instance.type = ViewTreeNodeType.BuilderParam;
317        return instance;
318    }
319
320    public changeBuilderParam2BuilderNode(builder: ArkMethod): void {
321        this.name = BUILDER_DECORATOR;
322        this.type = ViewTreeNodeType.Builder;
323        this.signature = builder.getSignature();
324        this.classSignature = this.signature;
325        const root = builder.getViewTree()?.getRoot();
326        if (root) {
327            for (let child of root.children) {
328                this.children.push(child as ViewTreeNodeImpl);
329            }
330        } else {
331            logger.error(`ViewTree->changeBuilderParam2BuilderNode ${builder.getSignature().toString()} @Builder viewtree fail.`);
332        }
333    }
334
335    public hasBuilderParam(): boolean {
336        return this.walk(item => {
337            return (item as ViewTreeNodeImpl).isBuilderParam();
338        });
339    }
340
341    public clone(parent: ViewTreeNodeImpl, map: Map<ViewTreeNodeImpl, ViewTreeNodeImpl> = new Map()): ViewTreeNodeImpl {
342        let newNode = new ViewTreeNodeImpl(this.name);
343        newNode.attributes = this.attributes;
344        newNode.stmts = newNode.attributes;
345        newNode.stateValues = this.stateValues;
346        newNode.parent = parent;
347        newNode.type = this.type;
348        newNode.signature = this.signature;
349        newNode.classSignature = newNode.signature;
350        newNode.builderParam = this.builderParam;
351        newNode.builder = this.builder;
352        map.set(this, newNode);
353
354        for (const child of this.children) {
355            if (map.has(child)) {
356                newNode.children.push(map.get(child)!);
357            } else {
358                newNode.children.push(child.clone(newNode, map));
359            }
360        }
361
362        return newNode;
363    }
364
365    public addStmt(tree: ViewTreeImpl, stmt: Stmt): void {
366        this.parseAttributes(stmt);
367        if (this.name !== COMPONENT_FOR_EACH && this.name !== COMPONENT_LAZY_FOR_EACH) {
368            this.parseStateValues(tree, stmt);
369        }
370    }
371
372    private parseAttributes(stmt: Stmt): void {
373        let expr: AbstractInvokeExpr | undefined;
374        if (stmt instanceof ArkAssignStmt) {
375            let op = stmt.getRightOp();
376            if (op instanceof ArkInstanceInvokeExpr) {
377                expr = op;
378            } else if (op instanceof ArkStaticInvokeExpr) {
379                expr = op;
380            }
381        } else if (stmt instanceof ArkInvokeStmt) {
382            let invoke = stmt.getInvokeExpr();
383            if (invoke instanceof ArkInstanceInvokeExpr) {
384                expr = invoke;
385            } else if (invoke instanceof ArkStaticInvokeExpr) {
386                expr = invoke;
387            }
388        }
389        if (expr) {
390            let key = expr.getMethodSignature().getMethodSubSignature().getMethodName();
391            let relationValues: (Constant | ArkInstanceFieldRef | MethodSignature)[] = [];
392            for (const arg of expr.getArgs()) {
393                if (arg instanceof Local) {
394                    this.getBindValues(arg, relationValues);
395                } else if (arg instanceof Constant) {
396                    relationValues.push(arg);
397                }
398            }
399            this.attributes.set(key, [stmt, relationValues]);
400        }
401    }
402
403    private getBindValues(local: Local, relationValues: (Constant | ArkInstanceFieldRef | MethodSignature)[], visitor: Set<Local> = new Set()): void {
404        if (visitor.has(local)) {
405            return;
406        }
407        visitor.add(local);
408        const stmt = local.getDeclaringStmt();
409        if (!stmt) {
410            let type = local.getType();
411            if (type instanceof FunctionType) {
412                relationValues.push(type.getMethodSignature());
413            }
414            return;
415        }
416        for (const v of stmt.getUses()) {
417            if (v instanceof Constant) {
418                relationValues.push(v);
419            } else if (v instanceof ArkInstanceFieldRef) {
420                relationValues.push(v);
421            } else if (v instanceof Local) {
422                this.getBindValues(v, relationValues, visitor);
423            }
424        }
425    }
426
427    public parseStateValues(tree: ViewTreeImpl, stmt: Stmt): void {
428        let stateValues: Set<ArkField> = StateValuesUtils.getInstance(tree.getDeclaringArkClass()).parseStmtUsesStateValues(stmt);
429        stateValues.forEach(field => {
430            this.stateValues.add(field);
431            tree.addStateValue(field, this);
432        }, this);
433    }
434}
435
436class TreeNodeStack {
437    protected root: ViewTreeNodeImpl | null = null;
438    protected stack: ViewTreeNodeImpl[];
439
440    constructor() {
441        this.stack = [];
442    }
443
444    /**
445     * @internal
446     */
447    public push(node: ViewTreeNodeImpl): void {
448        let parent = this.getParent();
449        node.parent = parent;
450        this.stack.push(node);
451        if (parent === null || parent === undefined) {
452            this.root = node;
453        } else {
454            parent.children.push(node);
455        }
456    }
457
458    /**
459     * @internal
460     */
461    public pop(): void {
462        this.stack.pop();
463    }
464
465    /**
466     * @internal
467     */
468    public top(): ViewTreeNodeImpl | null {
469        return this.isEmpty() ? null : this.stack[this.stack.length - 1];
470    }
471
472    /**
473     * @internal
474     */
475    public isEmpty(): boolean {
476        return this.stack.length === 0;
477    }
478
479    /**
480     * @internal
481     */
482    public popAutomicComponent(name: string): void {
483        if (this.isEmpty()) {
484            return;
485        }
486
487        let node = this.stack[this.stack.length - 1];
488        if (name !== node.name && !this.isContainer(node.name)) {
489            this.stack.pop();
490        }
491    }
492
493    /**
494     * @internal
495     */
496    public popComponentExpect(name: string): TreeNodeStack {
497        for (let i = this.stack.length - 1; i >= 0; i--) {
498            if (this.stack[i].name !== name) {
499                this.stack.pop();
500            } else {
501                break;
502            }
503        }
504        return this;
505    }
506
507    private getParent(): ViewTreeNodeImpl | null {
508        if (this.stack.length === 0) {
509            return null;
510        }
511
512        let node = this.stack[this.stack.length - 1];
513        if (!this.isContainer(node.name)) {
514            this.stack.pop();
515        }
516        return this.stack[this.stack.length - 1];
517    }
518
519    protected isContainer(name: string): boolean {
520        return isEtsContainerComponent(name) || SPECIAL_CONTAINER_COMPONENT.has(name) || name === BUILDER_DECORATOR;
521    }
522}
523
524export class ViewTreeImpl extends TreeNodeStack implements ViewTree {
525    private render: ArkMethod;
526    private buildViewStatus: boolean;
527    private stateValues: Map<ArkField, Set<ViewTreeNode>>;
528    private fieldTypes: Map<string, Decorator | Type>;
529
530    /**
531     * @internal
532     */
533    constructor(render: ArkMethod) {
534        super();
535        this.render = render;
536        this.stateValues = new Map();
537        this.fieldTypes = new Map();
538        this.buildViewStatus = false;
539    }
540
541    /**
542     * ViewTree root node.
543     * @returns root node
544     */
545    public getRoot(): ViewTreeNode | null {
546        this.buildViewTree();
547        return this.root;
548    }
549
550    /**
551     * Map of the component controlled by the state variable
552     * @returns
553     */
554    public getStateValues(): Map<ArkField, Set<ViewTreeNode>> {
555        this.buildViewTree();
556        return this.stateValues;
557    }
558
559    /**
560     * @deprecated Use {@link getStateValues} instead.
561     */
562    public isClassField(name: string): boolean {
563        return this.fieldTypes.has(name);
564    }
565
566    /**
567     * @deprecated Use {@link getStateValues} instead.
568     */
569    public getClassFieldType(name: string): Decorator | Type | undefined {
570        return this.fieldTypes.get(name);
571    }
572
573    /**
574     * @internal
575     */
576    private buildViewTree(): void {
577        if (!this.render || this.isInitialized()) {
578            return;
579        }
580        this.buildViewStatus = true;
581        this.loadClasssFieldTypes();
582
583        if (this.render.hasBuilderDecorator()) {
584            let node = ViewTreeNodeImpl.createBuilderNode();
585            node.signature = this.render.getSignature();
586            node.classSignature = node.signature;
587            this.push(node);
588        }
589
590        if (this.render.getCfg()) {
591            this.buildViewTreeFromCfg(this.render.getCfg() as Cfg);
592        }
593    }
594
595    /**
596     * @internal
597     */
598    private isInitialized(): boolean {
599        return this.root != null || this.buildViewStatus;
600    }
601
602    /**
603     * @internal
604     */
605    public addStateValue(field: ArkField, node: ViewTreeNode): void {
606        if (!this.stateValues.has(field)) {
607            this.stateValues.set(field, new Set());
608        }
609        let sets = this.stateValues.get(field);
610        sets?.add(node);
611    }
612
613    /**
614     * @internal
615     */
616    private isCreateFunc(name: string): boolean {
617        return COMPONENT_CREATE_FUNCTIONS.has(name);
618    }
619
620    private loadClasssFieldTypes(): void {
621        for (const field of this.render.getDeclaringArkClass().getFields()) {
622            let decorators = field.getStateDecorators();
623            if (decorators.length > 0) {
624                if (decorators.length === 1) {
625                    this.fieldTypes.set(field.getName(), decorators[0]);
626                } else {
627                    this.fieldTypes.set(field.getName(), decorators[0]);
628                }
629            } else {
630                this.fieldTypes.set(field.getName(), field.getSignature().getType());
631            }
632        }
633    }
634
635    /**
636     * @internal
637     */
638    public getDeclaringArkClass(): ArkClass {
639        return this.render.getDeclaringArkClass();
640    }
641
642    /**
643     * @internal
644     */
645    private findMethod(methodSignature: MethodSignature): ArkMethod | null {
646        let method = this.render.getDeclaringArkFile().getScene().getMethod(methodSignature);
647        if (method) {
648            return method;
649        }
650
651        // class
652        method = this.getDeclaringArkClass().getMethod(methodSignature);
653        if (method) {
654            return method;
655        }
656
657        return this.findMethodWithName(methodSignature.getMethodSubSignature().getMethodName());
658    }
659
660    /**
661     * @internal
662     */
663    private findMethodWithName(name: string): ArkMethod | null {
664        let method = this.getDeclaringArkClass().getMethodWithName(name);
665        if (method) {
666            return method;
667        }
668
669        // namespace
670        this.getDeclaringArkClass()
671            .getDeclaringArkNamespace()
672            ?.getAllMethodsUnderThisNamespace()
673            .forEach(value => {
674                if (value.getName() === name) {
675                    method = value;
676                }
677            });
678        if (method) {
679            return method;
680        }
681
682        this.getDeclaringArkClass()
683            .getDeclaringArkFile()
684            .getAllNamespacesUnderThisFile()
685            .forEach(namespace => {
686                namespace.getAllMethodsUnderThisNamespace().forEach(value => {
687                    if (value.getName() === name) {
688                        method = value;
689                    }
690                });
691            });
692        return method;
693    }
694
695    /**
696     * @internal
697     */
698    private findClass(classSignature: ClassSignature): ArkClass | null {
699        return ModelUtils.getClass(this.render, classSignature);
700    }
701
702    private findBuilderMethod(value: Value): ArkMethod | undefined | null {
703        let method: ArkMethod | undefined | null;
704        if (value instanceof ArkInstanceFieldRef) {
705            method = this.findMethodWithName(value.getFieldName());
706        } else if (value instanceof ArkStaticInvokeExpr) {
707            method = this.findMethod(value.getMethodSignature());
708        } else if (value instanceof Local && value.getType() instanceof FunctionType) {
709            method = this.findMethod((value.getType() as FunctionType).getMethodSignature());
710        } else if (value instanceof Local) {
711            method = this.findMethodWithName(value.getName());
712        }
713        if (method && !method.hasBuilderDecorator()) {
714            method = this.findMethodInvokeBuilderMethod(method);
715        }
716
717        return method;
718    }
719
720    /**
721     * @internal
722     */
723    private addBuilderNode(method: ArkMethod): ViewTreeNodeImpl {
724        let builderViewTree = method.getViewTree();
725        if (!builderViewTree || !builderViewTree.getRoot()) {
726            logger.error(`ViewTree->addBuilderNode ${method.getSignature().toString()} build viewtree fail.`);
727            // add empty node
728            let node = ViewTreeNodeImpl.createBuilderNode();
729            node.signature = method.getSignature();
730            node.classSignature = node.signature;
731            this.push(node);
732            this.pop();
733            return node;
734        }
735
736        let root = builderViewTree.getRoot() as ViewTreeNodeImpl;
737        this.push(root);
738        if (method.getDeclaringArkClass() === this.render.getDeclaringArkClass()) {
739            for (const [field, nodes] of builderViewTree.getStateValues()) {
740                for (const node of nodes) {
741                    this.addStateValue(field, node);
742                }
743            }
744        }
745        this.pop();
746        return root;
747    }
748
749    /**
750     * @internal
751     */
752    private addCustomComponentNode(cls: ArkClass, arg: Value | undefined, builder: ArkMethod | undefined): ViewTreeNodeImpl {
753        let node = ViewTreeNodeImpl.createCustomComponent();
754        node.signature = cls.getSignature();
755        node.classSignature = node.signature;
756        node.stateValuesTransfer = this.parseObjectLiteralExpr(cls, arg, builder);
757        if (arg instanceof Local && arg.getType()) {
758            let stateValues = StateValuesUtils.getInstance(this.getDeclaringArkClass()).parseObjectUsedStateValues(arg.getType());
759            stateValues.forEach(field => {
760                node.stateValues.add(field);
761                this.addStateValue(field, node);
762            });
763        }
764        this.push(node);
765        let componentViewTree = cls.getViewTree();
766        if (!componentViewTree || !componentViewTree.getRoot()) {
767            logger.error(`ViewTree->addCustomComponentNode ${cls.getSignature().toString()} build viewtree fail.`);
768            return node;
769        }
770        let root = componentViewTree.getRoot() as ViewTreeNodeImpl;
771        if (root.hasBuilderParam()) {
772            root = this.cloneBuilderParamNode(node, root);
773        }
774        node.children.push(root);
775
776        return node;
777    }
778
779    private cloneBuilderParamNode(node: ViewTreeNodeImpl, root: ViewTreeNodeImpl): ViewTreeNodeImpl {
780        root = root.clone(node);
781        if (node.stateValuesTransfer) {
782            root.walk(item => {
783                let child = item as ViewTreeNodeImpl;
784                if (!child.isBuilderParam() || !child.builderParam) {
785                    return false;
786                }
787                let method = node.stateValuesTransfer?.get(child.builderParam) as ArkMethod;
788                if (method) {
789                    child.changeBuilderParam2BuilderNode(method);
790                }
791
792                return false;
793            });
794        }
795        return root;
796    }
797
798    /**
799     * @internal
800     */
801    private addBuilderParamNode(field: ArkField): ViewTreeNodeImpl {
802        let node = ViewTreeNodeImpl.createBuilderParamNode();
803        node.builderParam = field;
804        this.push(node);
805        this.pop();
806
807        return node;
808    }
809
810    /**
811     * @internal
812     */
813    private addSystemComponentNode(name: string): ViewTreeNodeImpl {
814        let node = new ViewTreeNodeImpl(name);
815        this.push(node);
816
817        return node;
818    }
819
820    private findMethodInvokeBuilderMethod(method: ArkMethod): ArkMethod | undefined {
821        let stmts = method.getCfg()?.getStmts();
822        if (!stmts) {
823            return undefined;
824        }
825        for (const stmt of stmts) {
826            let expr: AbstractInvokeExpr | undefined;
827
828            if (stmt instanceof ArkInvokeStmt) {
829                expr = stmt.getInvokeExpr();
830            } else if (stmt instanceof ArkAssignStmt) {
831                let rightOp = stmt.getRightOp();
832                if (rightOp instanceof ArkInstanceInvokeExpr || rightOp instanceof ArkStaticInvokeExpr) {
833                    expr = rightOp;
834                }
835            }
836
837            if (expr === undefined) {
838                continue;
839            }
840
841            let method = this.findMethod(expr.getMethodSignature());
842            if (method?.hasBuilderDecorator()) {
843                return method;
844            }
845        }
846        return undefined;
847    }
848
849    private parseFieldInObjectLiteral(field: ArkField, cls: ArkClass, transferMap: Map<ArkField, ArkField | ArkMethod>): void {
850        let dstField = cls.getFieldWithName(field.getName());
851        if (dstField?.getStateDecorators().length === 0 && !dstField?.hasBuilderParamDecorator()) {
852            return;
853        }
854
855        let stmts = field.getInitializer();
856        if (stmts.length === 0) {
857            return;
858        }
859
860        let assignStmt = stmts[stmts.length - 1];
861        if (!(assignStmt instanceof ArkAssignStmt)) {
862            return;
863        }
864
865        let value = assignStmt.getRightOp();
866        if (value instanceof Local) {
867            value = backtraceLocalInitValue(value);
868        }
869        if (dstField?.hasBuilderParamDecorator()) {
870            let method = this.findBuilderMethod(value);
871            if (method) {
872                transferMap.set(dstField, method);
873            }
874        } else {
875            let srcField: ArkField | undefined | null;
876            if (value instanceof ArkInstanceFieldRef) {
877                srcField = this.getDeclaringArkClass().getFieldWithName(value.getFieldName());
878            }
879            if (srcField && dstField) {
880                transferMap.set(dstField, srcField);
881            }
882        }
883    }
884
885    private parseObjectLiteralExpr(cls: ArkClass, object: Value | undefined, builder: ArkMethod | undefined): Map<ArkField, ArkField | ArkMethod> | undefined {
886        let transferMap: Map<ArkField, ArkField | ArkMethod> = new Map();
887        if (object instanceof Local && object.getType() instanceof ClassType) {
888            let anonymousSig = (object.getType() as ClassType).getClassSignature();
889            let anonymous = this.findClass(anonymousSig);
890            anonymous?.getFields().forEach(field => {
891                this.parseFieldInObjectLiteral(field, cls, transferMap);
892            });
893        }
894        // If the builder exists, there will be a unique BuilderParam
895        if (builder) {
896            cls.getFields().forEach(value => {
897                if (value.hasBuilderParamDecorator()) {
898                    transferMap.set(value, builder);
899                }
900            });
901        }
902
903        if (transferMap.size === 0) {
904            return undefined;
905        }
906        return transferMap;
907    }
908
909    private viewComponentCreationParser(name: string, stmt: Stmt, expr: AbstractInvokeExpr): ViewTreeNodeImpl | undefined {
910        let temp = expr.getArg(0) as Local;
911        let arg: Value | undefined;
912        temp.getUsedStmts().forEach(value => {
913            if (value instanceof ArkInvokeStmt) {
914                let invokerExpr = value.getInvokeExpr();
915                let methodName = invokerExpr.getMethodSignature().getMethodSubSignature().getMethodName();
916                if (methodName === 'constructor') {
917                    arg = invokerExpr.getArg(0);
918                }
919            }
920        });
921
922        let builderMethod: ArkMethod | undefined;
923        let builder = expr.getArg(1) as Local;
924        if (builder) {
925            let method = this.findMethod((builder.getType() as FunctionType).getMethodSignature());
926            if (!method?.hasBuilderDecorator()) {
927                method?.addDecorator(new Decorator(BUILDER_DECORATOR));
928            }
929            if (!method?.hasViewTree()) {
930                method?.setViewTree(new ViewTreeImpl(method));
931            }
932            if (method) {
933                builderMethod = method;
934            }
935        }
936
937        let initValue = backtraceLocalInitValue(temp);
938        if (!(initValue instanceof ArkNewExpr)) {
939            return undefined;
940        }
941
942        let clsSignature = (initValue.getType() as ClassType).getClassSignature();
943        if (clsSignature) {
944            let cls = this.findClass(clsSignature);
945            if (cls && cls.hasComponentDecorator()) {
946                return this.addCustomComponentNode(cls, arg, builderMethod);
947            } else {
948                logger.error(`ViewTree->viewComponentCreationParser not found class ${clsSignature.toString()}. ${stmt.toString()}`);
949            }
950        }
951        return undefined;
952    }
953
954    private waterFlowCreationParser(name: string, stmt: Stmt, expr: AbstractInvokeExpr): ViewTreeNodeImpl {
955        let node = this.addSystemComponentNode(name);
956        let object = expr.getArg(0);
957        if (object instanceof Local && object.getType() instanceof ClassType) {
958            let anonymousSig = (object.getType() as ClassType).getClassSignature();
959            let anonymous = this.findClass(anonymousSig);
960            let footer = anonymous?.getFieldWithName('footer');
961            if (!footer) {
962                return node;
963            }
964            let stmts = footer.getInitializer();
965            let assignStmt = stmts[stmts.length - 1];
966            if (!(assignStmt instanceof ArkAssignStmt)) {
967                return node;
968            }
969
970            let value = assignStmt.getRightOp();
971            let method = this.findBuilderMethod(value);
972            if (method?.hasBuilderDecorator()) {
973                return this.addBuilderNode(method);
974            }
975        }
976
977        return node;
978    }
979
980    private forEachCreationParser(name: string, stmt: Stmt, expr: AbstractInvokeExpr): ViewTreeNodeImpl {
981        let node = this.addSystemComponentNode(name);
982        let values = expr.getArg(0) as Local;
983        let declaringStmt = values?.getDeclaringStmt();
984        if (declaringStmt) {
985            let stateValues = StateValuesUtils.getInstance(this.getDeclaringArkClass()).parseStmtUsesStateValues(declaringStmt);
986            stateValues.forEach(field => {
987                node.stateValues.add(field);
988                this.addStateValue(field, node);
989            });
990        }
991
992        let type = (expr.getArg(1) as Local).getType() as FunctionType;
993        let method = this.findMethod(type.getMethodSignature());
994        if (method && method.getCfg()) {
995            this.buildViewTreeFromCfg(method.getCfg() as Cfg);
996        }
997        return node;
998    }
999
1000    private repeatCreationParser(name: string, stmt: Stmt, expr: AbstractInvokeExpr): ViewTreeNodeImpl {
1001        let node = this.addSystemComponentNode(name);
1002        let arg = expr.getArg(0) as Local;
1003        let declaringStmt = arg?.getDeclaringStmt();
1004        if (declaringStmt) {
1005            let stateValues = StateValuesUtils.getInstance(this.getDeclaringArkClass()).parseStmtUsesStateValues(declaringStmt);
1006            stateValues.forEach(field => {
1007                node.stateValues.add(field);
1008                this.addStateValue(field, node);
1009            });
1010        }
1011
1012        return node;
1013    }
1014
1015    private ifBranchCreationParser(name: string, stmt: Stmt, expr: AbstractInvokeExpr): ViewTreeNodeImpl {
1016        this.popComponentExpect(COMPONENT_IF);
1017        return this.addSystemComponentNode(COMPONENT_IF_BRANCH);
1018    }
1019
1020    private COMPONENT_CREATE_PARSERS: Map<string, (name: string, stmt: Stmt, expr: AbstractInvokeExpr) => ViewTreeNodeImpl | undefined> = new Map([
1021        ['ForEach.create', this.forEachCreationParser.bind(this)],
1022        ['LazyForEach.create', this.forEachCreationParser.bind(this)],
1023        ['Repeat.create', this.repeatCreationParser.bind(this)],
1024        ['View.create', this.viewComponentCreationParser.bind(this)],
1025        ['If.branch', this.ifBranchCreationParser.bind(this)],
1026        ['WaterFlow.create', this.waterFlowCreationParser.bind(this)],
1027    ]);
1028
1029    private componentCreateParse(componentName: string, methodName: string, stmt: Stmt, expr: ArkStaticInvokeExpr): ViewTreeNodeImpl | undefined {
1030        let parserFn = this.COMPONENT_CREATE_PARSERS.get(`${componentName}.${methodName}`);
1031        if (parserFn) {
1032            let node = parserFn(componentName, stmt, expr);
1033            node?.addStmt(this, stmt);
1034            return node;
1035        }
1036        this.popAutomicComponent(componentName);
1037        let node = this.addSystemComponentNode(componentName);
1038        node.addStmt(this, stmt);
1039        return node;
1040    }
1041
1042    private parseStaticInvokeExpr(local2Node: Map<Local, ViewTreeNode>, stmt: Stmt, expr: ArkStaticInvokeExpr): ViewTreeNodeImpl | undefined {
1043        let methodSignature = expr.getMethodSignature();
1044        let method = this.findMethod(methodSignature);
1045        if (method?.hasBuilderDecorator()) {
1046            let node = this.addBuilderNode(method);
1047            node.parseStateValues(this, stmt);
1048            return node;
1049        }
1050
1051        let name = methodSignature.getDeclaringClassSignature().getClassName();
1052        let methodName = methodSignature.getMethodSubSignature().getMethodName();
1053
1054        if (this.isCreateFunc(methodName)) {
1055            return this.componentCreateParse(name, methodName, stmt, expr);
1056        }
1057
1058        let currentNode = this.top();
1059        if (name === currentNode?.name) {
1060            currentNode.addStmt(this, stmt);
1061            if (methodName === COMPONENT_POP_FUNCTION) {
1062                this.pop();
1063            }
1064            return currentNode;
1065        } else if (name === COMPONENT_IF && methodName === COMPONENT_POP_FUNCTION) {
1066            this.popComponentExpect(COMPONENT_IF);
1067            this.pop();
1068        }
1069        return undefined;
1070    }
1071
1072    /**
1073     * $temp4.margin({ top: 20 });
1074     * @param viewTree
1075     * @param local2Node
1076     * @param expr
1077     */
1078    private parseInstanceInvokeExpr(local2Node: Map<Local, ViewTreeNodeImpl>, stmt: Stmt, expr: ArkInstanceInvokeExpr): ViewTreeNodeImpl | undefined {
1079        let temp = expr.getBase();
1080        if (local2Node.has(temp)) {
1081            let component = local2Node.get(temp);
1082            if (component?.name === COMPONENT_REPEAT && expr.getMethodSignature().getMethodSubSignature().getMethodName() === 'each') {
1083                let arg = expr.getArg(0);
1084                let type = arg.getType();
1085                if (type instanceof FunctionType) {
1086                    let method = this.findMethod(type.getMethodSignature());
1087                    this.buildViewTreeFromCfg(method?.getCfg() as Cfg);
1088                }
1089                this.pop();
1090            } else {
1091                component?.addStmt(this, stmt);
1092            }
1093
1094            return component;
1095        }
1096
1097        let name = expr.getBase().getName();
1098        if (name.startsWith(TEMP_LOCAL_PREFIX)) {
1099            let initValue = backtraceLocalInitValue(expr.getBase());
1100            if (initValue instanceof ArkThisRef) {
1101                name = 'this';
1102            }
1103        }
1104
1105        let methodName = expr.getMethodSignature().getMethodSubSignature().getMethodName();
1106        let field = this.getDeclaringArkClass().getFieldWithName(methodName);
1107        if (name === 'this' && field?.hasBuilderParamDecorator()) {
1108            return this.addBuilderParamNode(field);
1109        }
1110
1111        let method = this.findMethod(expr.getMethodSignature());
1112        if (name === 'this' && method?.hasBuilderDecorator()) {
1113            return this.addBuilderNode(method);
1114        }
1115
1116        return undefined;
1117    }
1118
1119    private parsePtrInvokeExpr(local2Node: Map<Local, ViewTreeNodeImpl>, stmt: Stmt, expr: ArkPtrInvokeExpr): ViewTreeNodeImpl | undefined {
1120        let temp = expr.getFuncPtrLocal();
1121        if (temp instanceof Local && local2Node.has(temp)) {
1122            let component = local2Node.get(temp);
1123            if (component?.name === COMPONENT_REPEAT && expr.getMethodSignature().getMethodSubSignature().getMethodName() === 'each') {
1124                let arg = expr.getArg(0);
1125                let type = arg.getType();
1126                if (type instanceof FunctionType) {
1127                    let method = this.findMethod(type.getMethodSignature());
1128                    this.buildViewTreeFromCfg(method?.getCfg() as Cfg);
1129                }
1130                this.pop();
1131            } else {
1132                component?.addStmt(this, stmt);
1133            }
1134
1135            return component;
1136        } else if (temp instanceof ArkInstanceFieldRef) {
1137            let name = temp.getBase().getName();
1138            if (name.startsWith(TEMP_LOCAL_PREFIX)) {
1139                let initValue = backtraceLocalInitValue(temp.getBase());
1140                if (initValue instanceof ArkThisRef) {
1141                    name = 'this';
1142                }
1143            }
1144
1145            let methodName = temp.getFieldName();
1146            let field = this.getDeclaringArkClass().getFieldWithName(methodName);
1147            if (name === 'this' && field?.hasBuilderParamDecorator()) {
1148                return this.addBuilderParamNode(field);
1149            }
1150
1151            let method = this.findMethod(expr.getMethodSignature());
1152            if (name === 'this' && method?.hasBuilderDecorator()) {
1153                return this.addBuilderNode(method);
1154            }
1155        }
1156        return undefined;
1157    }
1158
1159    /**
1160     * $temp3 = View.create($temp2);
1161     * $temp4 = View.pop();
1162     * $temp4.margin({ top: 20 });
1163     *
1164     * $temp2 = List.create();
1165     * $temp5 = $temp2.width('100%');
1166     * $temp6 = $temp5.height('100%');
1167     * $temp6.backgroundColor('#FFDCDCDC');
1168     * @param viewTree
1169     * @param local2Node
1170     * @param stmt
1171     * @returns
1172     */
1173    private parseAssignStmt(local2Node: Map<Local, ViewTreeNodeImpl>, stmt: ArkAssignStmt): void {
1174        let left = stmt.getLeftOp();
1175        let right = stmt.getRightOp();
1176
1177        if (!(left instanceof Local)) {
1178            return;
1179        }
1180
1181        let component: ViewTreeNodeImpl | undefined;
1182        if (right instanceof ArkStaticInvokeExpr) {
1183            component = this.parseStaticInvokeExpr(local2Node, stmt, right);
1184        } else if (right instanceof ArkInstanceInvokeExpr) {
1185            component = this.parseInstanceInvokeExpr(local2Node, stmt, right);
1186        } else if (right instanceof ArkPtrInvokeExpr) {
1187            component = this.parsePtrInvokeExpr(local2Node, stmt, right);
1188        }
1189        if (component) {
1190            local2Node.set(left, component);
1191        }
1192    }
1193
1194    private parseInvokeStmt(local2Node: Map<Local, ViewTreeNodeImpl>, stmt: ArkInvokeStmt): void {
1195        let expr = stmt.getInvokeExpr();
1196        if (expr instanceof ArkStaticInvokeExpr) {
1197            this.parseStaticInvokeExpr(local2Node, stmt, expr);
1198        } else if (expr instanceof ArkInstanceInvokeExpr) {
1199            this.parseInstanceInvokeExpr(local2Node, stmt, expr);
1200        } else if (expr instanceof ArkPtrInvokeExpr) {
1201            this.parsePtrInvokeExpr(local2Node, stmt, expr);
1202        }
1203    }
1204
1205    private buildViewTreeFromCfg(cfg: Cfg, local2Node: Map<Local, ViewTreeNodeImpl> = new Map()): void {
1206        if (!cfg) {
1207            return;
1208        }
1209        let blocks = cfg.getBlocks();
1210        for (const block of blocks) {
1211            for (const stmt of block.getStmts()) {
1212                if (!(stmt instanceof ArkInvokeStmt || stmt instanceof ArkAssignStmt)) {
1213                    continue;
1214                }
1215
1216                if (stmt instanceof ArkAssignStmt) {
1217                    this.parseAssignStmt(local2Node, stmt);
1218                } else if (stmt instanceof ArkInvokeStmt) {
1219                    this.parseInvokeStmt(local2Node, stmt);
1220                }
1221            }
1222        }
1223    }
1224}
1225
1226export function buildViewTree(render: ArkMethod): ViewTree {
1227    return new ViewTreeImpl(render);
1228}
1229