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