• 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 '../../core/base/Constant';
17import {
18    AbstractBinopExpr,
19    AbstractExpr,
20    ArkAwaitExpr,
21    ArkCastExpr,
22    ArkDeleteExpr,
23    ArkInstanceInvokeExpr,
24    ArkInstanceOfExpr,
25    ArkNewArrayExpr,
26    ArkNewExpr,
27    ArkNormalBinopExpr,
28    ArkStaticInvokeExpr,
29    ArkTypeOfExpr,
30    ArkUnopExpr,
31    ArkYieldExpr,
32    NormalBinaryOperator,
33} from '../../core/base/Expr';
34import { Local } from '../../core/base/Local';
35import { ArkClass, ClassCategory } from '../../core/model/ArkClass';
36import { ArkMethod } from '../../core/model/ArkMethod';
37import { ClassSignature, MethodSignature } from '../../core/model/ArkSignature';
38import { ArkCodeBuffer } from '../ArkStream';
39import Logger, { LOG_MODULE_TYPE } from '../../utils/logger';
40import { PrinterUtils } from '../base/PrinterUtils';
41import { SourceMethod } from './SourceMethod';
42import {
43    AliasType,
44    ArrayType,
45    ClassType,
46    FunctionType,
47    GenericType,
48    IntersectionType,
49    LiteralType,
50    PrimitiveType,
51    StringType,
52    TupleType,
53    Type,
54    UnclearReferenceType,
55    UnionType,
56    UnknownType,
57    VoidType,
58} from '../../core/base/Type';
59import { SourceClass } from './SourceClass';
60import { Value } from '../../core/base/Value';
61import { AbstractRef, ArkArrayRef, ArkInstanceFieldRef, ArkStaticFieldRef, ArkThisRef } from '../../core/base/Ref';
62import { ArkFile } from '../../core/model/ArkFile';
63import {
64    COMPONENT_CREATE_FUNCTION,
65    COMPONENT_CUSTOMVIEW,
66    COMPONENT_IF,
67    COMPONENT_POP_FUNCTION
68} from '../../core/common/EtsConst';
69import { INSTANCE_INIT_METHOD_NAME } from '../../core/common/Const';
70import { ArkAssignStmt } from '../../core/base/Stmt';
71import { ArkNamespace } from '../../core/model/ArkNamespace';
72import { AbstractTypeExpr, KeyofTypeExpr, TypeQueryExpr } from '../../core/base/TypeExpr';
73import { ArkBaseModel } from '../../core/model/ArkBaseModel';
74import { ArkField } from '../../core/model/ArkField';
75import { ExportInfo } from '../../core/model/ArkExport';
76import { ImportInfo } from '../../core/model/ArkImport';
77import { BIGINT_KEYWORD, SUPER_NAME } from '../../core/common/TSConst';
78
79const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'SourceTransformer');
80
81export interface TransformerContext {
82    getArkFile(): ArkFile;
83
84    getDeclaringArkNamespace(): ArkNamespace | undefined;
85
86    getMethod(signature: MethodSignature): ArkMethod | null;
87
88    getClass(signature: ClassSignature): ArkClass | null;
89
90    getPrinter(): ArkCodeBuffer;
91
92    transTemp2Code(temp: Local, isLeftOp: boolean): string;
93
94    isInBuilderMethod(): boolean;
95}
96
97export class SourceTransformer {
98    protected context: TransformerContext;
99
100    constructor(context: TransformerContext) {
101        this.context = context;
102    }
103
104    private anonymousMethodToString(method: ArkMethod, indent: string): string {
105        let mtdPrinter = new SourceMethod(method, indent);
106        mtdPrinter.setInBuilder(this.context.isInBuilderMethod());
107        return mtdPrinter.dump().trimStart();
108    }
109
110    private anonymousClassToString(cls: ArkClass, indent: string): string {
111        let clsPrinter = new SourceClass(cls, indent);
112        return clsPrinter.dump().trimStart();
113    }
114
115    public instanceInvokeExprToString(invokeExpr: ArkInstanceInvokeExpr, isAttr: boolean): string {
116        let methodName = invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName();
117        if (methodName === INSTANCE_INIT_METHOD_NAME) {
118            return '';
119        }
120        let args: string[] = [];
121        invokeExpr.getArgs().forEach(v => {
122            args.push(this.valueToString(v));
123        });
124        let genericCode = isAttr ? '' : this.genericTypesToString(invokeExpr.getRealGenericTypes());
125        if (isAttr && this.context.isInBuilderMethod()) {
126            return `.${methodName}${genericCode}(${args.join(', ')})`;
127        }
128
129        return `${this.valueToString(invokeExpr.getBase())}.${methodName}${genericCode}(${args.join(', ')})`;
130    }
131
132    private transBuilderMethod(className: string, methodName: string, args: string[], invokeExpr: ArkStaticInvokeExpr, genericCode: string): string | null {
133        if (className === COMPONENT_CUSTOMVIEW) {
134            if (methodName === COMPONENT_CREATE_FUNCTION) {
135                // Anonymous @Builder method
136                if (args.length > 1) {
137                    // remove the substring '() =>' or '(x, y): type =>' at the beginning of args[1]
138                    const pattern = /^\([^)]*\)\s*:\s*\w*\s*=>\s*/;
139                    args[1] = args[1].replace(pattern, '');
140                }
141                return `${args.join(' ')}`;
142            }
143            if (methodName === COMPONENT_POP_FUNCTION) {
144                return '';
145            }
146        }
147
148        if (PrinterUtils.isComponentCreate(invokeExpr)) {
149            if (className === COMPONENT_IF) {
150                return `if (${args.join(', ')})`;
151            }
152            return `${className}${genericCode}(${args.join(', ')})`;
153        }
154
155        if (PrinterUtils.isComponentIfBranchInvoke(invokeExpr)) {
156            let arg0 = invokeExpr.getArg(0) as Constant;
157            if (arg0.getValue() === '0') {
158                return ``;
159            } else {
160                return '} else {';
161            }
162        }
163
164        if (PrinterUtils.isComponentPop(invokeExpr)) {
165            return '}';
166        }
167
168        return null;
169    }
170
171    public staticInvokeExprToString(invokeExpr: ArkStaticInvokeExpr): string {
172        let methodSignature = invokeExpr.getMethodSignature();
173        let method = this.context.getMethod(methodSignature);
174        if (method && PrinterUtils.isAnonymousMethod(method.getName())) {
175            return this.anonymousMethodToString(method, this.context.getPrinter().getIndent());
176        }
177
178        let classSignature = methodSignature.getDeclaringClassSignature();
179        let className = PrinterUtils.getStaticInvokeClassFullName(classSignature, this.context.getDeclaringArkNamespace());
180        let methodName = methodSignature.getMethodSubSignature().getMethodName();
181        let args: string[] = [];
182        invokeExpr.getArgs().forEach(v => {
183            args.push(this.valueToString(v));
184        });
185
186        let genericCode = this.genericTypesToString(invokeExpr.getRealGenericTypes());
187
188        if (this.context.isInBuilderMethod()) {
189            const res = this.transBuilderMethod(className, methodName, args, invokeExpr, genericCode);
190            if (res !== null) {
191                return res;
192            }
193        }
194
195        if (className && className.length > 0 && methodName !== SUPER_NAME) {
196            return `${className}.${methodName}${genericCode}(${args.join(', ')})`;
197        }
198        return `${methodName}${genericCode}(${args.join(', ')})`;
199    }
200
201    private genericTypesToString(types: Type[] | undefined): string {
202        if (!types) {
203            return '';
204        }
205
206        let code = this.typeArrayToString(types);
207        if (code.length > 0) {
208            return `<${code}>`;
209        }
210        return '';
211    }
212
213    public typeArrayToString(types: Type[], split: string = ', '): string {
214        let typesStr: string[] = [];
215        types.forEach(t => {
216            typesStr.push(this.typeToString(t));
217        });
218
219        return typesStr.join(split);
220    }
221
222    public static constToString(value: Constant): string {
223        if (value.getType().toString() === 'string') {
224            return `'${PrinterUtils.escape(value.getValue())}'`;
225        } else if (value.getType().toString() === BIGINT_KEYWORD) {
226            return `${value.getValue()}n`;
227        } else {
228            return value.getValue();
229        }
230    }
231
232    private exprToString(expr: AbstractExpr): string {
233        if (expr instanceof ArkInstanceInvokeExpr) {
234            const isAttr = PrinterUtils.isComponentAttributeInvoke(expr);
235            return `${this.instanceInvokeExprToString(expr, isAttr)}`;
236        }
237
238        if (expr instanceof ArkStaticInvokeExpr) {
239            return `${this.staticInvokeExprToString(expr)}`;
240        }
241
242        if (expr instanceof ArkNewArrayExpr) {
243            return `new Array<${this.typeToString(expr.getBaseType())}>(${expr.getSize()})`;
244        }
245
246        if (expr instanceof ArkNewExpr) {
247            return `new ${this.typeToString(expr.getType())}()`;
248        }
249
250        if (expr instanceof ArkDeleteExpr) {
251            return `delete ${this.valueToString(expr.getField())}`;
252        }
253
254        if (expr instanceof AbstractBinopExpr) {
255            let op1: Value = expr.getOp1();
256            let op2: Value = expr.getOp2();
257            let operator: string = expr.getOperator();
258
259            return `${this.valueToString(op1, false, operator)} ${operator} ${this.valueToString(op2, false, operator)}`;
260        }
261
262        if (expr instanceof ArkTypeOfExpr) {
263            return `typeof(${this.valueToString(expr.getOp())})`;
264        }
265
266        if (expr instanceof ArkInstanceOfExpr) {
267            return `${this.valueToString(expr.getOp())} instanceof ${this.typeToString(expr.getType())}`;
268        }
269
270        if (expr instanceof ArkCastExpr) {
271            let baseOp = expr.getOp();
272            return `${this.valueToString(baseOp)} as ${this.typeToString(expr.getType())}`;
273        }
274
275        if (expr instanceof ArkUnopExpr) {
276            return `${expr.getOperator()}${this.valueToString(expr.getOp())}`;
277        }
278
279        if (expr instanceof ArkAwaitExpr) {
280            return `await ${this.valueToString(expr.getPromise())}`;
281        }
282
283        if (expr instanceof ArkYieldExpr) {
284            return `yield ${this.valueToString(expr.getYieldValue())}`;
285        }
286
287        logger.info(`exprToString ${expr.constructor} not support.`);
288        // ArkPhiExpr
289        return `${expr}`;
290    }
291
292    public refToString(value: AbstractRef): string {
293        if (value instanceof ArkInstanceFieldRef) {
294            return `${this.valueToString(value.getBase())}.${value.getFieldName()}`;
295        }
296
297        if (value instanceof ArkStaticFieldRef) {
298            return `${value.getFieldSignature().getBaseName()}.${value.getFieldName()}`;
299        }
300
301        if (value instanceof ArkArrayRef) {
302            let index = value.getIndex();
303            if (index instanceof Constant && index.getType() instanceof StringType && PrinterUtils.isTemp(index.getValue())) {
304                return `${this.valueToString(value.getBase())}[${this.valueToString(new Local(index.getValue()))}]`;
305            }
306            return `${this.valueToString(value.getBase())}[${this.valueToString(value.getIndex())}]`;
307        }
308
309        if (value instanceof ArkThisRef) {
310            return 'this';
311        }
312
313        // ArkCaughtExceptionRef
314        logger.info(`refToString ${value.constructor} not support.`);
315        return `${value}`;
316    }
317
318    public valueToString(value: Value, isLeftOp: boolean = false, operator?: string): string {
319        if (value instanceof AbstractExpr) {
320            return this.exprToString(value);
321        }
322
323        if (value instanceof AbstractRef) {
324            return this.refToString(value);
325        }
326
327        if (value instanceof Constant) {
328            return SourceTransformer.constToString(value);
329        }
330
331        if (value instanceof Local) {
332            return this.localToString(value, isLeftOp, operator);
333        }
334
335        logger.info(`valueToString ${value.constructor} not support.`);
336        return `${value}`;
337    }
338
339    private localToString(value: Local, isLeftOp: boolean = false, operator?: string): string {
340        if (PrinterUtils.isAnonymousMethod(value.getName())) {
341            let methodSignature = (value.getType() as FunctionType).getMethodSignature();
342            let anonymousMethod = this.context.getMethod(methodSignature);
343            if (anonymousMethod) {
344                return this.anonymousMethodToString(anonymousMethod, this.context.getPrinter().getIndent());
345            }
346        }
347        if (PrinterUtils.isAnonymousClass(value.getName())) {
348            let clsSignature = (value.getType() as ClassType).getClassSignature();
349            let cls = this.context.getClass(clsSignature);
350            if (cls) {
351                return this.anonymousClassToString(cls, this.context.getPrinter().getIndent());
352            }
353        }
354
355        if (operator === NormalBinaryOperator.Division || operator === NormalBinaryOperator.Multiplication || operator === NormalBinaryOperator.Remainder) {
356            if (PrinterUtils.isTemp(value.getName())) {
357                let stmt = value.getDeclaringStmt();
358                if (stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof ArkNormalBinopExpr) {
359                    return `(${this.context.transTemp2Code(value, isLeftOp)})`;
360                }
361            }
362        }
363
364        return this.context.transTemp2Code(value, isLeftOp);
365    }
366
367    public literalObjectToString(type: ClassType): string {
368        let name = type.getClassSignature().getClassName();
369        if (PrinterUtils.isAnonymousClass(name)) {
370            let cls = this.context.getClass(type.getClassSignature());
371            if (cls) {
372                return this.anonymousClassToString(cls, this.context.getPrinter().getIndent());
373            }
374        }
375        return name;
376    }
377
378    public typeToString(type: Type): string {
379        if (type instanceof LiteralType) {
380            return this.literalType2string(type);
381        }
382
383        if (type instanceof PrimitiveType || type instanceof GenericType) {
384            return type.getName();
385        }
386
387        if (type instanceof UnionType || type instanceof IntersectionType) {
388            return this.multipleType2string(type);
389        }
390
391        if (type instanceof UnknownType) {
392            return 'any';
393        }
394
395        if (type instanceof VoidType) {
396            return 'void';
397        }
398
399        if (type instanceof ClassType) {
400            return this.classType2string(type);
401        }
402        if (type instanceof ArrayType) {
403            return this.arrayType2string(type);
404        }
405        if (type instanceof TupleType) {
406            return this.tupleType2string(type);
407        }
408
409        if (type instanceof FunctionType) {
410            let methodSignature = type.getMethodSignature();
411            let method = this.context.getMethod(methodSignature);
412            if (method && PrinterUtils.isAnonymousMethod(method.getName())) {
413                return new SourceMethod(method).toArrowFunctionTypeString();
414            }
415        }
416
417        if (type instanceof UnclearReferenceType) {
418            return this.unclearReferenceType2string(type);
419        }
420
421        if (type instanceof AliasType) {
422            return this.aliasType2string(type);
423        }
424
425        if (type instanceof KeyofTypeExpr) {
426            return this.keyofTypeExpr2string(type);
427        }
428
429        if (type instanceof TypeQueryExpr) {
430            return this.typeQueryExpr2string(type);
431        }
432
433        if (!type) {
434            return 'any';
435        }
436
437        logger.info(`valueToString ${type.constructor} not support.`);
438        return type.toString();
439    }
440
441    private literalType2string(type: LiteralType): string {
442        let literalName = type.getLiteralName();
443        if (typeof literalName === 'string' && literalName.endsWith('Keyword')) {
444            return literalName.substring(0, literalName.length - 'Keyword'.length).toLowerCase();
445        }
446        return `${literalName}`;
447    }
448
449    private multipleType2string(type: UnionType | IntersectionType): string {
450        let typesStr: string[] = [];
451        for (const member of type.getTypes()) {
452            if (member instanceof UnionType || member instanceof IntersectionType) {
453                typesStr.push(`(${this.typeToString(member)})`);
454            } else {
455                typesStr.push(this.typeToString(member));
456            }
457        }
458        if (type instanceof UnionType) {
459            return typesStr.join(' | ');
460        } else {
461            return typesStr.join(' & ');
462        }
463    }
464
465    private arrayType2string(type: ArrayType): string {
466        const readonly = type.getReadonlyFlag() ? 'readonly ' : '';
467        const dimensions: string[] = [];
468        for (let i = 0; i < type.getDimension(); i++) {
469            dimensions.push('[]');
470        }
471
472        let baseType = type.getBaseType();
473        if (baseType instanceof UnionType || baseType instanceof IntersectionType || baseType instanceof AbstractTypeExpr) {
474            return `${readonly}(${this.typeToString(baseType)})${dimensions.join('')}`;
475        }
476        return `${readonly}${this.typeToString(baseType)}${dimensions.join('')}`;
477    }
478
479    private tupleType2string(type: TupleType): string {
480        const readonly = type.getReadonlyFlag() ? 'readonly ' : '';
481        let typesStr: string[] = [];
482        for (const member of type.getTypes()) {
483            typesStr.push(this.typeToString(member));
484        }
485        return `${readonly}[${typesStr.join(', ')}]`;
486    }
487
488    private aliasType2string(type: AliasType): string {
489        let typesStr: string[] = [];
490        let genericTypes = type.getRealGenericTypes() ?? type.getGenericTypes();
491        if (genericTypes) {
492            for (const gType of genericTypes) {
493                typesStr.push(this.typeToString(gType));
494            }
495        }
496        if (typesStr.length > 0) {
497            return `${type.getName()}<${typesStr.join(', ')}>`;
498        }
499        return type.getName();
500    }
501
502    private keyofTypeExpr2string(type: KeyofTypeExpr): string {
503        if (type.getOpType() instanceof UnionType || type.getOpType() instanceof IntersectionType) {
504            return `keyof (${this.typeToString(type.getOpType())})`;
505        }
506        return `keyof ${this.typeToString(type.getOpType())}`;
507    }
508
509    private typeQueryExpr2string(type: TypeQueryExpr): string {
510        const gTypes = type.getGenerateTypes();
511        const genericStr = this.genericTypesToString(gTypes);
512        const opValue = type.getOpValue();
513        if (opValue instanceof ArkBaseModel) {
514            if (opValue instanceof ArkClass || opValue instanceof ArkMethod || opValue instanceof ArkNamespace || opValue instanceof ArkField) {
515                return `typeof ${opValue.getName()}${genericStr}`;
516            } else if (opValue instanceof ExportInfo) {
517                return `typeof ${opValue.getExportClauseName()}${genericStr}`;
518            } else if (opValue instanceof ImportInfo) {
519                return `typeof ${opValue.getImportClauseName()}${genericStr}`;
520            } else {
521                return `typeof *invalid*`;
522            }
523        } else {
524            return `typeof ${this.valueToString(opValue)}${genericStr}`;
525        }
526    }
527
528    private unclearReferenceType2string(type: UnclearReferenceType): string {
529        let genericTypes = type.getGenericTypes();
530        if (genericTypes.length > 0) {
531            return `${type.getName()}<${genericTypes.map(value => this.typeToString(value)).join(', ')}>`;
532        }
533        return type.getName();
534    }
535
536    private classType2string(type: ClassType): string {
537        const name = PrinterUtils.getStaticInvokeClassFullName(type.getClassSignature());
538        if (PrinterUtils.isDefaultClass(name)) {
539            return 'any';
540        }
541        if (PrinterUtils.isAnonymousClass(name)) {
542            let cls = this.context.getClass(type.getClassSignature());
543            if (cls && cls.getCategory() === ClassCategory.TYPE_LITERAL) {
544                return this.anonymousClassToString(cls, this.context.getPrinter().getIndent());
545            }
546            return 'Object';
547        }
548        let genericTypes = type.getRealGenericTypes();
549        if (genericTypes && genericTypes.length > 0) {
550            return `${name}${this.genericTypesToString(genericTypes)}`;
551        }
552        return name;
553    }
554}
555