• 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 { ClassType, Type, UnknownType } from '../../base/Type';
17import { BodyBuilder } from './BodyBuilder';
18import { buildViewTree } from '../../graph/builder/ViewTreeBuilder';
19import { ArkClass, ClassCategory } from '../ArkClass';
20import { ArkMethod } from '../ArkMethod';
21import ts from 'ohos-typescript';
22import {
23    buildDecorators,
24    buildGenericType,
25    buildModifiers,
26    buildParameters,
27    buildReturnType,
28    buildTypeParameters,
29    handlePropertyAccessExpression,
30} from './builderUtils';
31import Logger, { LOG_MODULE_TYPE } from '../../../utils/logger';
32import { ArkParameterRef, ArkThisRef, ClosureFieldRef } from '../../base/Ref';
33import { ArkBody } from '../ArkBody';
34import { Cfg } from '../../graph/Cfg';
35import { ArkInstanceInvokeExpr, ArkStaticInvokeExpr } from '../../base/Expr';
36import { MethodSignature, MethodSubSignature } from '../ArkSignature';
37import { ArkAssignStmt, ArkInvokeStmt, ArkReturnStmt, ArkReturnVoidStmt, Stmt } from '../../base/Stmt';
38import { BasicBlock } from '../../graph/BasicBlock';
39import { Local } from '../../base/Local';
40import { Value } from '../../base/Value';
41import { CONSTRUCTOR_NAME, SUPER_NAME, THIS_NAME } from '../../common/TSConst';
42import { ANONYMOUS_METHOD_PREFIX, CALL_SIGNATURE_NAME, DEFAULT_ARK_CLASS_NAME, DEFAULT_ARK_METHOD_NAME, NAME_DELIMITER, NAME_PREFIX } from '../../common/Const';
43import { ArkSignatureBuilder } from './ArkSignatureBuilder';
44import { IRUtils } from '../../common/IRUtils';
45import { ArkErrorCode } from '../../common/ArkError';
46
47const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'ArkMethodBuilder');
48
49export type MethodLikeNode =
50    | ts.FunctionDeclaration
51    | ts.MethodDeclaration
52    | ts.ConstructorDeclaration
53    | ts.ArrowFunction
54    | ts.AccessorDeclaration
55    | ts.FunctionExpression
56    | ts.MethodSignature
57    | ts.ConstructSignatureDeclaration
58    | ts.CallSignatureDeclaration
59    | ts.FunctionTypeNode;
60
61export function buildDefaultArkMethodFromArkClass(declaringClass: ArkClass, mtd: ArkMethod, sourceFile: ts.SourceFile, node?: ts.ModuleDeclaration): void {
62    mtd.setDeclaringArkClass(declaringClass);
63
64    const methodSubSignature = ArkSignatureBuilder.buildMethodSubSignatureFromMethodName(DEFAULT_ARK_METHOD_NAME, true);
65    const methodSignature = new MethodSignature(mtd.getDeclaringArkClass().getSignature(), methodSubSignature);
66    mtd.setImplementationSignature(methodSignature);
67    mtd.setLineCol(0);
68
69    const defaultMethodNode = node ? node : sourceFile;
70
71    let bodyBuilder = new BodyBuilder(mtd.getSignature(), defaultMethodNode, mtd, sourceFile);
72    mtd.setBodyBuilder(bodyBuilder);
73}
74
75export function buildArkMethodFromArkClass(
76    methodNode: MethodLikeNode,
77    declaringClass: ArkClass,
78    mtd: ArkMethod,
79    sourceFile: ts.SourceFile,
80    declaringMethod?: ArkMethod
81): void {
82    mtd.setDeclaringArkClass(declaringClass);
83    declaringMethod !== undefined && mtd.setOuterMethod(declaringMethod);
84
85    ts.isFunctionDeclaration(methodNode) && mtd.setAsteriskToken(methodNode.asteriskToken !== undefined);
86
87    // All MethodLikeNode except FunctionTypeNode have questionToken.
88    !ts.isFunctionTypeNode(methodNode) && mtd.setQuestionToken(methodNode.questionToken !== undefined);
89
90    mtd.setCode(methodNode.getText(sourceFile));
91    mtd.setModifiers(buildModifiers(methodNode));
92    mtd.setDecorators(buildDecorators(methodNode, sourceFile));
93
94    if (methodNode.typeParameters) {
95        mtd.setGenericTypes(buildTypeParameters(methodNode.typeParameters, sourceFile, mtd));
96    }
97
98    // build methodDeclareSignatures and methodSignature as well as corresponding positions
99    const methodName = buildMethodName(methodNode, declaringClass, sourceFile, declaringMethod);
100    const methodParameters: MethodParameter[] = [];
101    buildParameters(methodNode.parameters, mtd, sourceFile).forEach(parameter => {
102        buildGenericType(parameter.getType(), mtd);
103        methodParameters.push(parameter);
104    });
105    let returnType = UnknownType.getInstance();
106    if (methodNode.type) {
107        returnType = buildGenericType(buildReturnType(methodNode.type, sourceFile, mtd), mtd);
108    }
109    const methodSubSignature = new MethodSubSignature(methodName, methodParameters, returnType, mtd.isStatic());
110    const methodSignature = new MethodSignature(mtd.getDeclaringArkClass().getSignature(), methodSubSignature);
111    const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, methodNode.getStart(sourceFile));
112    if (isMethodImplementation(methodNode)) {
113        mtd.setImplementationSignature(methodSignature);
114        mtd.setLine(line + 1);
115        mtd.setColumn(character + 1);
116        let bodyBuilder = new BodyBuilder(mtd.getSignature(), methodNode, mtd, sourceFile);
117        mtd.setBodyBuilder(bodyBuilder);
118    } else {
119        mtd.setDeclareSignatures(methodSignature);
120        mtd.setDeclareLinesAndCols([line + 1], [character + 1]);
121    }
122
123    if (mtd.hasBuilderDecorator()) {
124        mtd.setViewTree(buildViewTree(mtd));
125    } else if (declaringClass.hasComponentDecorator() && mtd.getSubSignature().toString() === 'build()' && !mtd.isStatic()) {
126        declaringClass.setViewTree(buildViewTree(mtd));
127    }
128    checkAndUpdateMethod(mtd, declaringClass);
129    declaringClass.addMethod(mtd);
130    IRUtils.setComments(mtd, methodNode, sourceFile, mtd.getDeclaringArkFile().getScene().getOptions());
131}
132
133function buildMethodName(node: MethodLikeNode, declaringClass: ArkClass, sourceFile: ts.SourceFile, declaringMethod?: ArkMethod): string {
134    let name: string = '';
135    if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
136        name = node.name ? node.name.text : buildAnonymousMethodName(node, declaringClass);
137    } else if (ts.isFunctionTypeNode(node)) {
138        //TODO: check name type
139        name = node.name ? node.name.getText(sourceFile) : buildAnonymousMethodName(node, declaringClass);
140    } else if (ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) {
141        if (ts.isIdentifier(node.name)) {
142            name = (node.name as ts.Identifier).text;
143        } else if (ts.isComputedPropertyName(node.name)) {
144            if (ts.isIdentifier(node.name.expression)) {
145                name = node.name.expression.text;
146            } else if (ts.isPropertyAccessExpression(node.name.expression)) {
147                name = handlePropertyAccessExpression(node.name.expression);
148            } else {
149                logger.warn('Other method ComputedPropertyName found!');
150            }
151        } else {
152            logger.warn('Other method declaration type found!');
153        }
154    }
155    //TODO, hard code
156    else if (ts.isConstructorDeclaration(node)) {
157        name = CONSTRUCTOR_NAME;
158    } else if (ts.isConstructSignatureDeclaration(node)) {
159        name = 'construct-signature';
160    } else if (ts.isCallSignatureDeclaration(node)) {
161        name = CALL_SIGNATURE_NAME;
162    } else if (ts.isGetAccessor(node) && ts.isIdentifier(node.name)) {
163        name = 'Get-' + node.name.text;
164    } else if (ts.isSetAccessor(node) && ts.isIdentifier(node.name)) {
165        name = 'Set-' + node.name.text;
166    } else if (ts.isArrowFunction(node)) {
167        name = buildAnonymousMethodName(node, declaringClass);
168    }
169
170    if (declaringMethod !== undefined && !declaringMethod.isDefaultArkMethod()) {
171        name = buildNestedMethodName(name, declaringMethod.getName());
172    }
173    return name;
174}
175
176function buildAnonymousMethodName(node: MethodLikeNode, declaringClass: ArkClass): string {
177    return `${ANONYMOUS_METHOD_PREFIX}${declaringClass.getAnonymousMethodNumber()}`;
178}
179
180function buildNestedMethodName(originName: string, declaringMethodName: string): string {
181    if (originName.startsWith(NAME_PREFIX)) {
182        return `${originName}${NAME_DELIMITER}${declaringMethodName}`;
183    }
184    return `${NAME_PREFIX}${originName}${NAME_DELIMITER}${declaringMethodName}`;
185}
186
187export class ObjectBindingPatternParameter {
188    private propertyName: string = '';
189    private name: string = '';
190    private optional: boolean = false;
191
192    constructor() {}
193
194    public getName(): string {
195        return this.name;
196    }
197
198    public setName(name: string): void {
199        this.name = name;
200    }
201
202    public getPropertyName(): string {
203        return this.propertyName;
204    }
205
206    public setPropertyName(propertyName: string): void {
207        this.propertyName = propertyName;
208    }
209
210    public isOptional(): boolean {
211        return this.optional;
212    }
213
214    public setOptional(optional: boolean): void {
215        this.optional = optional;
216    }
217}
218
219export class ArrayBindingPatternParameter {
220    private propertyName: string = '';
221    private name: string = '';
222    private optional: boolean = false;
223
224    constructor() {}
225
226    public getName(): string {
227        return this.name;
228    }
229
230    public setName(name: string): void {
231        this.name = name;
232    }
233
234    public getPropertyName(): string {
235        return this.propertyName;
236    }
237
238    public setPropertyName(propertyName: string): void {
239        this.propertyName = propertyName;
240    }
241
242    public isOptional(): boolean {
243        return this.optional;
244    }
245
246    public setOptional(optional: boolean): void {
247        this.optional = optional;
248    }
249}
250
251export class MethodParameter implements Value {
252    private name: string = '';
253    private type!: Type;
254    private optional: boolean = false;
255    private dotDotDotToken: boolean = false;
256    private objElements: ObjectBindingPatternParameter[] = [];
257    private arrayElements: ArrayBindingPatternParameter[] = [];
258
259    constructor() {}
260
261    public getName(): string {
262        return this.name;
263    }
264
265    public setName(name: string): void {
266        this.name = name;
267    }
268
269    public getType(): Type {
270        return this.type;
271    }
272
273    public setType(type: Type): void {
274        this.type = type;
275    }
276
277    public isOptional(): boolean {
278        return this.optional;
279    }
280
281    public setOptional(optional: boolean): void {
282        this.optional = optional;
283    }
284
285    public hasDotDotDotToken(): boolean {
286        return this.dotDotDotToken;
287    }
288
289    public setDotDotDotToken(dotDotDotToken: boolean): void {
290        this.dotDotDotToken = dotDotDotToken;
291    }
292
293    public addObjElement(element: ObjectBindingPatternParameter): void {
294        this.objElements.push(element);
295    }
296
297    public getObjElements(): ObjectBindingPatternParameter[] {
298        return this.objElements;
299    }
300
301    public setObjElements(objElements: ObjectBindingPatternParameter[]): void {
302        this.objElements = objElements;
303    }
304
305    public addArrayElement(element: ArrayBindingPatternParameter): void {
306        this.arrayElements.push(element);
307    }
308
309    public getArrayElements(): ArrayBindingPatternParameter[] {
310        return this.arrayElements;
311    }
312
313    public setArrayElements(arrayElements: ArrayBindingPatternParameter[]): void {
314        this.arrayElements = arrayElements;
315    }
316
317    public getUses(): Value[] {
318        return [];
319    }
320}
321
322function needDefaultConstructorInClass(arkClass: ArkClass): boolean {
323    const originClassType = arkClass.getCategory();
324    return (
325        arkClass.getMethodWithName(CONSTRUCTOR_NAME) === null &&
326        (originClassType === ClassCategory.CLASS || originClassType === ClassCategory.OBJECT) &&
327        arkClass.getName() !== DEFAULT_ARK_CLASS_NAME &&
328        !arkClass.isDeclare()
329    );
330}
331
332function recursivelyCheckAndBuildSuperConstructor(arkClass: ArkClass): void {
333    let superClass: ArkClass | null = arkClass.getSuperClass();
334    while (superClass !== null) {
335        if (superClass.getMethodWithName(CONSTRUCTOR_NAME) === null) {
336            buildDefaultConstructor(superClass);
337        }
338        superClass = superClass.getSuperClass();
339    }
340}
341
342export function buildDefaultConstructor(arkClass: ArkClass): boolean {
343    if (!needDefaultConstructorInClass(arkClass)) {
344        return false;
345    }
346
347    recursivelyCheckAndBuildSuperConstructor(arkClass);
348
349    const defaultConstructor: ArkMethod = new ArkMethod();
350    defaultConstructor.setDeclaringArkClass(arkClass);
351    defaultConstructor.setCode('');
352    defaultConstructor.setIsGeneratedFlag(true);
353    defaultConstructor.setLineCol(0);
354
355    const thisLocal = new Local(THIS_NAME, new ClassType(arkClass.getSignature()));
356    const locals: Set<Local> = new Set([thisLocal]);
357    const basicBlock = new BasicBlock();
358    basicBlock.setId(0);
359
360    let parameters: MethodParameter[] = [];
361    let parameterArgs: Value[] = [];
362    const superConstructor = arkClass.getSuperClass()?.getMethodWithName(CONSTRUCTOR_NAME);
363    if (superConstructor) {
364        parameters = superConstructor.getParameters();
365
366        for (let index = 0; index < parameters.length; index++) {
367            const parameterRef = new ArkParameterRef(index, parameters[index].getType());
368            const parameterLocal = new Local(parameters[index].getName(), parameterRef.getType());
369            locals.add(parameterLocal);
370            parameterArgs.push(parameterLocal);
371            basicBlock.addStmt(new ArkAssignStmt(parameterLocal, parameterRef));
372            index++;
373        }
374    }
375
376    basicBlock.addStmt(new ArkAssignStmt(thisLocal, new ArkThisRef(new ClassType(arkClass.getSignature()))));
377
378    if (superConstructor) {
379        const superMethodSubSignature = new MethodSubSignature(SUPER_NAME, parameters, superConstructor.getReturnType());
380        const superMethodSignature = new MethodSignature(arkClass.getSignature(), superMethodSubSignature);
381        const superInvokeExpr = new ArkStaticInvokeExpr(superMethodSignature, parameterArgs);
382        basicBlock.addStmt(new ArkInvokeStmt(superInvokeExpr));
383    }
384
385    const methodSubSignature = new MethodSubSignature(CONSTRUCTOR_NAME, parameters, thisLocal.getType(), defaultConstructor.isStatic());
386    defaultConstructor.setImplementationSignature(new MethodSignature(arkClass.getSignature(), methodSubSignature));
387    basicBlock.addStmt(new ArkReturnStmt(thisLocal));
388
389    const cfg = new Cfg();
390    cfg.addBlock(basicBlock);
391    cfg.setStartingStmt(basicBlock.getHead()!);
392    cfg.setDeclaringMethod(defaultConstructor);
393    cfg.getStmts().forEach(s => s.setCfg(cfg));
394
395    defaultConstructor.setBody(new ArkBody(locals, cfg));
396    checkAndUpdateMethod(defaultConstructor, arkClass);
397    arkClass.addMethod(defaultConstructor);
398
399    return true;
400}
401
402export function buildInitMethod(initMethod: ArkMethod, fieldInitializerStmts: Stmt[], thisLocal: Local): void {
403    const classType = new ClassType(initMethod.getDeclaringArkClass().getSignature());
404    const assignStmt = new ArkAssignStmt(thisLocal, new ArkThisRef(classType));
405    const block = new BasicBlock();
406    block.setId(0);
407    block.addStmt(assignStmt);
408    const locals: Set<Local> = new Set([thisLocal]);
409    for (const stmt of fieldInitializerStmts) {
410        block.addStmt(stmt);
411        if (stmt.getDef() && stmt.getDef() instanceof Local) {
412            locals.add(stmt.getDef() as Local);
413        }
414    }
415    block.addStmt(new ArkReturnVoidStmt());
416    const cfg = new Cfg();
417    cfg.addBlock(block);
418    for (const stmt of block.getStmts()) {
419        stmt.setCfg(cfg);
420    }
421    cfg.setStartingStmt(assignStmt);
422    cfg.buildDefUseStmt(locals);
423    cfg.setDeclaringMethod(initMethod);
424    initMethod.setBody(new ArkBody(locals, cfg));
425}
426
427export function addInitInConstructor(constructor: ArkMethod): void {
428    const thisLocal = constructor.getBody()?.getLocals().get(THIS_NAME);
429    if (!thisLocal) {
430        return;
431    }
432    const cfg = constructor.getCfg();
433    if (cfg === undefined) {
434        return;
435    }
436    const blocks = cfg.getBlocks();
437    const firstBlockStmts = [...blocks][0].getStmts();
438    let index = 0;
439    for (let i = 0; i < firstBlockStmts.length; i++) {
440        const stmt = firstBlockStmts[i];
441        if (stmt instanceof ArkInvokeStmt && stmt.getInvokeExpr().getMethodSignature().getMethodSubSignature().getMethodName() === SUPER_NAME) {
442            index++;
443            continue;
444        }
445        if (stmt instanceof ArkAssignStmt) {
446            const rightOp = stmt.getRightOp();
447            if (rightOp instanceof ArkParameterRef || rightOp instanceof ArkThisRef || rightOp instanceof ClosureFieldRef) {
448                index++;
449                continue;
450            }
451        }
452        break;
453    }
454    const initInvokeStmt = new ArkInvokeStmt(
455        new ArkInstanceInvokeExpr(thisLocal, constructor.getDeclaringArkClass().getInstanceInitMethod().getSignature(), [])
456    );
457    initInvokeStmt.setCfg(cfg);
458    firstBlockStmts.splice(index, 0, initInvokeStmt);
459}
460
461export function isMethodImplementation(node: MethodLikeNode): boolean {
462    if (
463        ts.isFunctionDeclaration(node) ||
464        ts.isMethodDeclaration(node) ||
465        ts.isConstructorDeclaration(node) ||
466        ts.isGetAccessorDeclaration(node) ||
467        ts.isSetAccessorDeclaration(node) ||
468        ts.isFunctionExpression(node) ||
469        ts.isArrowFunction(node)
470    ) {
471        if (node.body !== undefined) {
472            return true;
473        }
474    }
475    return false;
476}
477
478export function checkAndUpdateMethod(method: ArkMethod, cls: ArkClass): void {
479    let presentMethod: ArkMethod | null;
480    if (method.isStatic()) {
481        presentMethod = cls.getStaticMethodWithName(method.getName());
482    } else {
483        presentMethod = cls.getMethodWithName(method.getName());
484    }
485    if (presentMethod === null) {
486        return;
487    }
488
489    if (method.validate().errCode !== ArkErrorCode.OK || presentMethod.validate().errCode !== ArkErrorCode.OK) {
490        return;
491    }
492    const presentDeclareSignatures = presentMethod.getDeclareSignatures();
493    const presentDeclareLineCols = presentMethod.getDeclareLineCols();
494    const presentImplSignature = presentMethod.getImplementationSignature();
495    const newDeclareSignature = method.getDeclareSignatures();
496    const newDeclareLineCols = method.getDeclareLineCols();
497    const newImplSignature = method.getImplementationSignature();
498
499    if (presentDeclareSignatures !== null && presentImplSignature === null) {
500        if (newDeclareSignature === null || presentMethod.getDeclareSignatureIndex(newDeclareSignature[0]) >= 0) {
501            method.setDeclareSignatures(presentDeclareSignatures);
502            method.setDeclareLineCols(presentDeclareLineCols as number[]);
503        } else {
504            method.setDeclareSignatures(presentDeclareSignatures.concat(newDeclareSignature));
505            method.setDeclareLineCols((presentDeclareLineCols as number[]).concat(newDeclareLineCols as number[]));
506        }
507        return;
508    }
509    if (presentDeclareSignatures === null && presentImplSignature !== null) {
510        if (newImplSignature === null) {
511            method.setImplementationSignature(presentImplSignature);
512            method.setLineCol(presentMethod.getLineCol() as number);
513        }
514        return;
515    }
516}
517