• 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 { Printer } from './Printer';
17import { ArkFile } from '../core/model/ArkFile';
18import { ArkMethod } from '../core/model/ArkMethod';
19import { ArkNamespace } from '../core/model/ArkNamespace';
20import { ArkClass } from '../core/model/ArkClass';
21import { ArkField } from '../core/model/ArkField';
22import {
23    AliasType,
24    AnnotationNamespaceType,
25    AnnotationTypeQueryType,
26    AnyType,
27    ArrayType,
28    BigIntType,
29    BooleanType,
30    ClassType,
31    EnumValueType,
32    FunctionType,
33    GenericType,
34    IntersectionType,
35    LiteralType,
36    NeverType,
37    NullType,
38    NumberType,
39    StringType,
40    TupleType,
41    Type,
42    UnclearReferenceType,
43    UndefinedType,
44    UnionType,
45    UnknownType,
46    VoidType,
47} from '../core/base/Type';
48import { Value } from '../core/base/Value';
49import {
50    ArkAssignStmt,
51    ArkIfStmt,
52    ArkInvokeStmt,
53    ArkReturnStmt,
54    ArkReturnVoidStmt,
55    ArkThrowStmt,
56    Stmt,
57} from '../core/base/Stmt';
58import {
59    ArkAwaitExpr,
60    ArkCastExpr,
61    ArkConditionExpr,
62    ArkDeleteExpr,
63    ArkInstanceInvokeExpr,
64    ArkInstanceOfExpr,
65    ArkNewArrayExpr,
66    ArkNewExpr,
67    ArkNormalBinopExpr,
68    ArkPhiExpr,
69    ArkPtrInvokeExpr,
70    ArkStaticInvokeExpr,
71    ArkTypeOfExpr,
72    ArkUnopExpr,
73    ArkYieldExpr,
74} from '../core/base/Expr';
75import { Constant } from '../core/base/Constant';
76import { MethodParameter } from '../core/model/builder/ArkMethodBuilder';
77import { ImportInfo } from '../core/model/ArkImport';
78import { ExportInfo } from '../core/model/ArkExport';
79import { AliasTypeSignature, ClassSignature, FieldSignature, FileSignature, MethodSignature, NamespaceSignature } from '../core/model/ArkSignature';
80import { LineColPosition } from '../core/base/Position';
81import { ArkArrayRef, ArkCaughtExceptionRef, ArkInstanceFieldRef, ArkParameterRef, ArkStaticFieldRef, ArkThisRef, ClosureFieldRef, GlobalRef } from '../core/base/Ref';
82import { Local } from '../core/base/Local';
83import { Cfg } from '../core/graph/Cfg';
84import { BasicBlock } from '../core/graph/BasicBlock';
85import { ArkBody } from '../core/model/ArkBody';
86import { Decorator } from '../core/base/Decorator';
87
88export class JsonPrinter extends Printer {
89    constructor(private arkFile: ArkFile) {
90        super();
91    }
92
93    public dump(): string {
94        const jsonObject = this.serializeArkFile(this.arkFile);
95        return JSON.stringify(jsonObject, null, 2);
96    }
97
98    private serializeArkFile(file: ArkFile): object {
99        return {
100            signature: this.serializeFileSignature(file.getFileSignature()),
101            namespaces: file.getNamespaces().map(ns => this.serializeNamespace(ns)),
102            classes: file.getClasses().map(cls => this.serializeClass(cls)),
103            importInfos: file.getImportInfos().map(info => this.serializeImportInfo(info)),
104            exportInfos: file.getExportInfos().map(info => this.serializeExportInfo(info)),
105        };
106    }
107
108    private serializeNamespace(namespace: ArkNamespace): object {
109        return {
110            signature: this.serializeNamespaceSignature(namespace.getSignature()),
111            classes: namespace.getClasses().map(cls => this.serializeClass(cls)),
112            namespaces: namespace.getNamespaces().map(ns => this.serializeNamespace(ns)),
113        };
114    }
115
116    private serializeClass(clazz: ArkClass): object {
117        return {
118            signature: this.serializeClassSignature(clazz.getSignature()),
119            modifiers: clazz.getModifiers(),
120            decorators: clazz.getDecorators().map((decorator) => this.serializeDecorator(decorator)),
121            typeParameters: clazz.getGenericsTypes()?.map((type) => this.serializeType(type)),
122            category: clazz.getCategory(),
123            superClassName: clazz.getSuperClassName(),
124            implementedInterfaceNames: clazz.getImplementedInterfaceNames(),
125            fields: clazz.getFields().map(field => this.serializeField(field)),
126            methods: clazz.getMethods(true).map(method => this.serializeMethod(method)),
127        };
128    }
129
130    private serializeField(field: ArkField): object {
131        return {
132            signature: this.serializeFieldSignature(field.getSignature()),
133            modifiers: field.getModifiers(),
134            decorators: field.getDecorators().map(decorator => this.serializeDecorator(decorator)),
135            questionToken: field.getQuestionToken(),
136            exclamationToken: field.getExclamationToken(),
137        };
138    }
139
140    private serializeMethod(method: ArkMethod): object {
141        let body = method.getBody();
142        return {
143            signature: this.serializeMethodSignature(method.getSignature()),
144            modifiers: method.getModifiers(),
145            decorators: method.getDecorators().map(decorator => this.serializeDecorator(decorator)),
146            typeParameters: method.getGenericTypes()?.map(type => this.serializeType(type)),
147            body: body && this.serializeMethodBody(body),
148        };
149    }
150
151    private serializeMethodBody(body: ArkBody): object {
152        return {
153            locals: Array.from(body.getLocals().values()).map(local => this.serializeLocal(local)),
154            cfg: this.serializeCfg(body.getCfg()),
155        };
156    }
157
158    private serializeMethodParameter(parameter: MethodParameter): object {
159        return {
160            name: parameter.getName(),
161            type: this.serializeType(parameter.getType()),
162            isOptional: parameter.isOptional(),
163            isRest: parameter.hasDotDotDotToken(),
164        };
165    }
166
167    private serializeImportInfo(importInfo: ImportInfo): object {
168        return {
169            importClauseName: importInfo.getImportClauseName(),
170            importType: importInfo.getImportType(),
171            importFrom: importInfo.getFrom(),
172            nameBeforeAs: importInfo.getNameBeforeAs(),
173            modifiers: importInfo.getModifiers(),
174            originTsPosition: this.serializeLineColPosition(importInfo.getOriginTsPosition()),
175        };
176    }
177
178    private serializeExportInfo(exportInfo: ExportInfo): object {
179        return {
180            exportClauseName: exportInfo.getExportClauseName(),
181            exportClauseType: exportInfo.getExportClauseType(),
182            exportFrom: exportInfo.getFrom(),
183            nameBeforeAs: exportInfo.getNameBeforeAs(),
184            isDefault: exportInfo.isDefault(),
185            modifiers: exportInfo.getModifiers(),
186            originTsPosition: this.serializeLineColPosition(exportInfo.getOriginTsPosition()),
187        };
188    }
189
190    private serializeDecorator(decorator: Decorator): object {
191        return {
192            kind: decorator.getKind(),
193        };
194    }
195
196    private serializeLineColPosition(position: LineColPosition): object {
197        return {
198            line: position.getLineNo(),
199            col: position.getColNo(),
200        };
201    }
202
203    private serializeType(type: Type): object {
204        if (type === undefined) {
205            throw new Error('Type is undefined');
206        }
207
208        if (type instanceof AnyType) {
209            return {
210                _: 'AnyType',
211            };
212        } else if (type instanceof UnknownType) {
213            return {
214                _: 'UnknownType',
215            };
216        } else if (type instanceof VoidType) {
217            return {
218                _: 'VoidType',
219            };
220        } else if (type instanceof NeverType) {
221            return {
222                _: 'NeverType',
223            };
224        } else if (type instanceof UnionType) {
225            return {
226                _: 'UnionType',
227                types: type.getTypes().map(type => this.serializeType(type)),
228            };
229        } else if (type instanceof IntersectionType) {
230            return {
231                _: 'IntersectionType',
232                types: type.getTypes().map((type) => this.serializeType(type)),
233            };
234        } else if (type instanceof TupleType) {
235            return {
236                _: 'TupleType',
237                types: type.getTypes().map(type => this.serializeType(type)),
238            };
239        } else if (type instanceof BooleanType) {
240            return {
241                _: 'BooleanType',
242            };
243        } else if (type instanceof NumberType) {
244            return {
245                _: 'NumberType',
246            };
247        } else if (type instanceof BigIntType) {
248            return {
249                _: 'BigIntType',
250            };
251        } else if (type instanceof StringType) {
252            return {
253                _: 'StringType',
254            };
255        } else if (type instanceof NullType) {
256            return {
257                _: 'NullType',
258            };
259        } else if (type instanceof UndefinedType) {
260            return {
261                _: 'UndefinedType',
262            };
263        } else if (type instanceof LiteralType) {
264            return {
265                _: 'LiteralType',
266                literal: type.getLiteralName(),
267            };
268        } else if (type instanceof ClassType) {
269            return {
270                _: 'ClassType',
271                signature: this.serializeClassSignature(type.getClassSignature()),
272                typeParameters: type.getRealGenericTypes()?.map(type => this.serializeType(type)),
273            };
274        } else if (type instanceof FunctionType) {
275            return {
276                _: 'FunctionType',
277                signature: this.serializeMethodSignature(type.getMethodSignature()),
278                typeParameters: type.getRealGenericTypes()?.map(type => this.serializeType(type)),
279            };
280        } else if (type instanceof ArrayType) {
281            return {
282                _: 'ArrayType',
283                elementType: this.serializeType(type.getBaseType()),
284                dimensions: type.getDimension(),
285            };
286        } else if (type instanceof UnclearReferenceType) {
287            return {
288                _: 'UnclearReferenceType',
289                name: type.getName(),
290                typeParameters: type.getGenericTypes().map(type => this.serializeType(type)),
291            };
292        } else if (type instanceof GenericType) {
293            let constraint = type.getConstraint();
294            let defaultType = type.getDefaultType();
295            return {
296                _: 'GenericType',
297                name: type.getName(),
298                constraint: constraint && this.serializeType(constraint),
299                defaultType: defaultType && this.serializeType(defaultType),
300            };
301        } else if (type instanceof AliasType) {
302            return {
303                _: 'AliasType',
304                name: type.getName(),
305                originalType: this.serializeType(type.getOriginalType()),
306                signature: this.serializeAliasTypeSignature(type.getSignature()),
307            };
308        } else if (type instanceof AnnotationNamespaceType) {
309            return {
310                _: 'AnnotationNamespaceType',
311                originType: type.getOriginType(),
312                namespaceSignature: this.serializeNamespaceSignature(type.getNamespaceSignature()),
313            };
314        } else if (type instanceof AnnotationTypeQueryType) {
315            return {
316                _: 'AnnotationTypeQueryType',
317                originType: type.getOriginType(),
318            };
319        } else if (type instanceof EnumValueType) {
320            const c = type.getConstant();
321            return {
322                _: 'EnumValueType',
323                signature: this.serializeFieldSignature(type.getFieldSignature()),
324                constant: c && this.serializeValue(c),
325            };
326        } else {
327            console.warn(`Unhandled Type: ${type.constructor.name} (${type.toString()})`);
328            return {
329                _: type.constructor.name,
330                text: type.toString(),
331            };
332        }
333    }
334
335    private serializeFileSignature(file: FileSignature): object {
336        return {
337            projectName: file.getProjectName(),
338            fileName: file.getFileName(),
339        };
340    }
341
342    private serializeNamespaceSignature(namespace: NamespaceSignature): object {
343        let dns = namespace.getDeclaringNamespaceSignature();
344        return {
345            name: namespace.getNamespaceName(),
346            declaringFile: this.serializeFileSignature(namespace.getDeclaringFileSignature()),
347            declaringNamespace: dns && this.serializeNamespaceSignature(dns),
348        };
349    }
350
351    private serializeClassSignature(clazz: ClassSignature): object {
352        let dns = clazz.getDeclaringNamespaceSignature();
353        return {
354            name: clazz.getClassName(),
355            declaringFile: this.serializeFileSignature(clazz.getDeclaringFileSignature()),
356            declaringNamespace: dns && this.serializeNamespaceSignature(dns),
357        };
358    }
359
360    private serializeFieldSignature(field: FieldSignature): object {
361        let declaringSignature: ClassSignature | NamespaceSignature = field.getDeclaringSignature();
362        let declaringClass;
363        if (declaringSignature instanceof ClassSignature) {
364            declaringClass = this.serializeClassSignature(declaringSignature);
365        } else {
366            declaringClass = this.serializeNamespaceSignature(declaringSignature);
367        }
368        return {
369            declaringClass,
370            name: field.getFieldName(),
371            type: this.serializeType(field.getType()),
372        };
373    }
374
375    private serializeMethodSignature(method: MethodSignature): object {
376        return {
377            declaringClass: this.serializeClassSignature(method.getDeclaringClassSignature()),
378            name: method.getMethodSubSignature().getMethodName(),
379            parameters: method
380                .getMethodSubSignature()
381                .getParameters()
382                .map(param => this.serializeMethodParameter(param)),
383            returnType: this.serializeType(method.getType()),
384        };
385    }
386
387    private serializeAliasTypeSignature(signature: AliasTypeSignature): object {
388        return {
389            name: signature.getName(),
390            method: this.serializeMethodSignature(signature.getDeclaringMethodSignature()),
391        };
392    }
393
394    private serializeCfg(cfg: Cfg): object {
395        const visited = new Set<BasicBlock>();
396        const stack: BasicBlock[] = [];
397        const startingBlock = cfg.getStartingBlock();
398        if (startingBlock) {
399            stack.push(startingBlock);
400        }
401        let id = 0;
402        while (stack.length > 0) {
403            const block = stack.pop()!;
404            if (visited.has(block)) {
405                continue;
406            }
407            visited.add(block);
408            block.setId(id++);
409            stack.push(...block.getSuccessors());
410        }
411        return {
412            blocks: Array.from(visited).map(block => this.serializeBasicBlock(block)),
413        };
414    }
415
416    private serializeBasicBlock(block: BasicBlock): object {
417        const successors = block.getSuccessors().map(succ => succ.getId());
418        successors.sort((a, b) => a - b);
419        const predecessors = block.getPredecessors().map(pred => pred.getId());
420        predecessors.sort((a, b) => a - b);
421        return {
422            id: block.getId(),
423            successors,
424            predecessors,
425            stmts: block.getStmts().map(stmt => this.serializeStmt(stmt)),
426        };
427    }
428
429    private serializeLocal(local: Local): object {
430        return {
431            name: local.getName(),
432            type: this.serializeType(local.getType()),
433        };
434    }
435
436    private serializeConstant(constant: Constant): object {
437        return {
438            value: constant.getValue(),
439            type: this.serializeType(constant.getType()),
440        };
441    }
442
443    private serializeValue(value: Value): object {
444        if (value === undefined) {
445            throw new Error('Value is undefined');
446        }
447
448        if (value instanceof Local) {
449            return {
450                _: 'Local',
451                ...this.serializeLocal(value),
452            };
453        } else if (value instanceof Constant) {
454            return {
455                _: 'Constant',
456                ...this.serializeConstant(value),
457            };
458        } else if (value instanceof ArkNewExpr) {
459            return {
460                _: 'NewExpr',
461                classType: this.serializeType(value.getClassType()),
462            };
463        } else if (value instanceof ArkNewArrayExpr) {
464            return {
465                _: 'NewArrayExpr',
466                elementType: this.serializeType(value.getBaseType()),
467                size: this.serializeValue(value.getSize()),
468            };
469        } else if (value instanceof ArkDeleteExpr) {
470            return {
471                _: 'DeleteExpr',
472                arg: this.serializeValue(value.getField()),
473            };
474        } else if (value instanceof ArkAwaitExpr) {
475            return {
476                _: 'AwaitExpr',
477                arg: this.serializeValue(value.getPromise()),
478            };
479        } else if (value instanceof ArkYieldExpr) {
480            return {
481                _: 'YieldExpr',
482                arg: this.serializeValue(value.getYieldValue()),
483            };
484        } else if (value instanceof ArkTypeOfExpr) {
485            return {
486                _: 'TypeOfExpr',
487                arg: this.serializeValue(value.getOp()),
488            };
489        } else if (value instanceof ArkInstanceOfExpr) {
490            return {
491                _: 'InstanceOfExpr',
492                arg: this.serializeValue(value.getOp()),
493                checkType: this.serializeType(value.getCheckType()),
494            };
495        } else if (value instanceof ArkCastExpr) {
496            return {
497                _: 'CastExpr',
498                arg: this.serializeValue(value.getOp()),
499                type: this.serializeType(value.getType()),
500            };
501        } else if (value instanceof ArkPhiExpr) {
502            const args = value.getArgs();
503            const argToBlock = value.getArgToBlock();
504            return {
505                _: 'PhiExpr',
506                args: args.map(arg => this.serializeValue(arg)),
507                blocks: args.map(arg => argToBlock.get(arg)!.getId()),
508                type: this.serializeType(value.getType()),
509            };
510        } else if (value instanceof ArkConditionExpr) {
511            return {
512                _: 'ConditionExpr',
513                op: value.getOperator(),
514                left: this.serializeValue(value.getOp1()),
515                right: this.serializeValue(value.getOp2()),
516                type: this.serializeType(value.getType()),
517            };
518        } else if (value instanceof ArkNormalBinopExpr) {
519            return {
520                _: 'BinopExpr',
521                op: value.getOperator(),
522                left: this.serializeValue(value.getOp1()),
523                right: this.serializeValue(value.getOp2()),
524            };
525        } else if (value instanceof ArkUnopExpr) {
526            return {
527                _: 'UnopExpr',
528                op: value.getOperator(),
529                arg: this.serializeValue(value.getOp()),
530            };
531        } else if (value instanceof ArkInstanceInvokeExpr) {
532            return {
533                _: 'InstanceCallExpr',
534                instance: this.serializeValue(value.getBase()),
535                method: this.serializeMethodSignature(value.getMethodSignature()),
536                args: value.getArgs().map(arg => this.serializeValue(arg)),
537            };
538        } else if (value instanceof ArkStaticInvokeExpr) {
539            return {
540                _: 'StaticCallExpr',
541                method: this.serializeMethodSignature(value.getMethodSignature()),
542                args: value.getArgs().map(arg => this.serializeValue(arg)),
543            };
544        } else if (value instanceof ArkPtrInvokeExpr) {
545            return {
546                _: 'PtrCallExpr',
547                ptr: this.serializeValue(value.getFuncPtrLocal()),
548                method: this.serializeMethodSignature(value.getMethodSignature()),
549                args: value.getArgs().map(arg => this.serializeValue(arg)),
550            };
551        } else if (value instanceof ArkThisRef) {
552            return {
553                _: 'ThisRef',
554                type: this.serializeType(value.getType()),
555            };
556        } else if (value instanceof ArkParameterRef) {
557            return {
558                _: 'ParameterRef',
559                index: value.getIndex(),
560                type: this.serializeType(value.getType()),
561            };
562        } else if (value instanceof ArkArrayRef) {
563            return {
564                _: 'ArrayRef',
565                array: this.serializeValue(value.getBase()),
566                index: this.serializeValue(value.getIndex()),
567                type: this.serializeType(value.getType()),
568            };
569        } else if (value instanceof ArkCaughtExceptionRef) {
570            return {
571                _: 'CaughtExceptionRef',
572                type: this.serializeType(value.getType()),
573            };
574        } else if (value instanceof GlobalRef) {
575            let ref = value.getRef();
576            return {
577                _: 'GlobalRef',
578                name: value.getName(),
579                ref: ref ? this.serializeValue(ref) : null,
580            };
581        } else if (value instanceof ClosureFieldRef) {
582            return {
583                _: 'ClosureFieldRef',
584                base: this.serializeLocal(value.getBase()),
585                fieldName: value.getFieldName(),
586                type: this.serializeType(value.getType()),
587            };
588        } else if (value instanceof ArkInstanceFieldRef) {
589            return {
590                _: 'InstanceFieldRef',
591                instance: this.serializeValue(value.getBase()),
592                field: this.serializeFieldSignature(value.getFieldSignature()),
593            };
594        } else if (value instanceof ArkStaticFieldRef) {
595            return {
596                _: 'StaticFieldRef',
597                field: this.serializeFieldSignature(value.getFieldSignature()),
598            };
599        } else {
600            console.warn(`Unhandled Value: ${value.constructor.name} (${value.toString()})`);
601            return {
602                _: value.constructor.name,
603                text: value.toString(),
604                type: this.serializeType(value.getType()),
605            };
606        }
607    }
608
609    private serializeStmt(stmt: Stmt): object {
610        if (stmt instanceof ArkAssignStmt) {
611            return {
612                _: 'AssignStmt',
613                left: this.serializeValue(stmt.getLeftOp()),
614                right: this.serializeValue(stmt.getRightOp()),
615            };
616        } else if (stmt instanceof ArkInvokeStmt) {
617            return {
618                _: 'CallStmt',
619                expr: this.serializeValue(stmt.getInvokeExpr()),
620            };
621        } else if (stmt instanceof ArkIfStmt) {
622            return {
623                _: 'IfStmt',
624                condition: this.serializeValue(stmt.getConditionExpr()),
625            };
626        } else if (stmt instanceof ArkReturnVoidStmt) {
627            return {
628                _: 'ReturnVoidStmt',
629            };
630        } else if (stmt instanceof ArkReturnStmt) {
631            return {
632                _: 'ReturnStmt',
633                arg: this.serializeValue(stmt.getOp()),
634            };
635        } else if (stmt instanceof ArkThrowStmt) {
636            return {
637                _: 'ThrowStmt',
638                arg: this.serializeValue(stmt.getOp()),
639            };
640        } else {
641            console.warn(`Unhandled Stmt: ${stmt.constructor.name} (${stmt.toString()})`);
642            return {
643                _: stmt.constructor.name,
644                text: stmt.toString(),
645            };
646        }
647    }
648}
649