• 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 { LineColPosition } from '../../base/Position';
17import { buildDefaultArkClassFromArkNamespace, buildNormalArkClassFromArkNamespace } from './ArkClassBuilder';
18import { ArkFile } from '../ArkFile';
19import { buildArkMethodFromArkClass } from './ArkMethodBuilder';
20import ts from 'ohos-typescript';
21import { ArkNamespace } from '../ArkNamespace';
22import { buildDecorators, buildModifiers } from './builderUtils';
23import Logger, { LOG_MODULE_TYPE } from '../../../utils/logger';
24import { buildExportAssignment, buildExportDeclaration, buildExportInfo, buildExportVariableStatement, isExported } from './ArkExportBuilder';
25import { ArkClass } from '../ArkClass';
26import { ArkMethod } from '../ArkMethod';
27import { NamespaceSignature } from '../ArkSignature';
28import { IRUtils } from '../../common/IRUtils';
29
30const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'ArkNamespaceBuilder');
31
32export function buildArkNamespace(node: ts.ModuleDeclaration, declaringInstance: ArkFile | ArkNamespace, ns: ArkNamespace, sourceFile: ts.SourceFile): void {
33    // modifiers
34    if (node.modifiers) {
35        ns.setModifiers(buildModifiers(node));
36        ns.setDecorators(buildDecorators(node, sourceFile));
37    }
38
39    if (declaringInstance instanceof ArkFile) {
40        ns.setDeclaringArkFile(declaringInstance);
41    } else {
42        ns.setDeclaringArkNamespace(declaringInstance);
43        ns.setDeclaringArkFile(declaringInstance.getDeclaringArkFile());
44    }
45    ns.setDeclaringInstance(declaringInstance);
46    const namespaceName = node.name.text;
47    const namespaceSignature = new NamespaceSignature(
48        namespaceName,
49        ns.getDeclaringArkFile().getFileSignature(),
50        ns.getDeclaringArkNamespace()?.getSignature() || null
51    );
52    ns.setSignature(namespaceSignature);
53
54    // TODO: whether needed?
55    ns.setCode(node.getText(sourceFile));
56
57    // set line and column
58    const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
59    ns.setLine(line + 1);
60    ns.setColumn(character + 1);
61
62    genDefaultArkClass(ns, node, sourceFile);
63
64    // build ns member
65    if (node.body) {
66        if (ts.isModuleBlock(node.body)) {
67            buildNamespaceMembers(node.body, ns, sourceFile);
68        }
69        // NamespaceDeclaration extends ModuleDeclaration
70        //TODO: Check
71        else if (ts.isModuleDeclaration(node.body)) {
72            logger.trace('This ModuleBody is an NamespaceDeclaration.');
73            let childNs: ArkNamespace = new ArkNamespace();
74            buildArkNamespace(node.body, ns, childNs, sourceFile);
75            ns.addNamespace(childNs);
76        } else if (ts.isIdentifier(node.body)) {
77            logger.warn('ModuleBody is Identifier');
78        } else {
79            logger.warn('JSDocNamespaceDeclaration found.');
80        }
81    } else {
82        logger.warn('JSDocNamespaceDeclaration found.');
83    }
84    IRUtils.setComments(ns, node, sourceFile, ns.getDeclaringArkFile().getScene().getOptions());
85}
86
87// TODO: check and update
88function buildNamespaceMembers(node: ts.ModuleBlock, namespace: ArkNamespace, sourceFile: ts.SourceFile): void {
89    const statements = node.statements;
90    const nestedNamespaces: ArkNamespace[] = [];
91    statements.forEach(child => {
92        if (ts.isModuleDeclaration(child)) {
93            let childNs: ArkNamespace = new ArkNamespace();
94            childNs.setDeclaringArkNamespace(namespace);
95            childNs.setDeclaringArkFile(namespace.getDeclaringArkFile());
96
97            buildArkNamespace(child, namespace, childNs, sourceFile);
98            nestedNamespaces.push(childNs);
99        } else if (ts.isClassDeclaration(child) || ts.isInterfaceDeclaration(child) || ts.isEnumDeclaration(child) || ts.isStructDeclaration(child)) {
100            let cls: ArkClass = new ArkClass();
101
102            buildNormalArkClassFromArkNamespace(child, namespace, cls, sourceFile);
103            namespace.addArkClass(cls);
104
105            if (cls.isExported()) {
106                namespace.addExportInfo(buildExportInfo(cls, namespace.getDeclaringArkFile(), LineColPosition.buildFromNode(child, sourceFile)));
107            }
108        }
109        // TODO: Check
110        else if (ts.isMethodDeclaration(child)) {
111            logger.trace('This is a MethodDeclaration in ArkNamespace.');
112            let mthd: ArkMethod = new ArkMethod();
113
114            buildArkMethodFromArkClass(child, namespace.getDefaultClass(), mthd, sourceFile);
115
116            if (mthd.isExported()) {
117                namespace.addExportInfo(buildExportInfo(mthd, namespace.getDeclaringArkFile(), LineColPosition.buildFromNode(child, sourceFile)));
118            }
119        } else if (ts.isFunctionDeclaration(child)) {
120            let mthd: ArkMethod = new ArkMethod();
121
122            buildArkMethodFromArkClass(child, namespace.getDefaultClass(), mthd, sourceFile);
123
124            if (mthd.isExported()) {
125                namespace.addExportInfo(buildExportInfo(mthd, namespace.getDeclaringArkFile(), LineColPosition.buildFromNode(child, sourceFile)));
126            }
127        } else if (ts.isExportDeclaration(child)) {
128            buildExportDeclaration(child, sourceFile, namespace.getDeclaringArkFile()).forEach(item => namespace.addExportInfo(item));
129        } else if (ts.isExportAssignment(child)) {
130            buildExportAssignment(child, sourceFile, namespace.getDeclaringArkFile()).forEach(item => namespace.addExportInfo(item));
131        } else if (ts.isVariableStatement(child) && isExported(child.modifiers)) {
132            buildExportVariableStatement(child, sourceFile, namespace.getDeclaringArkFile(), namespace).forEach(item => namespace.addExportInfo(item));
133        } else {
134            logger.trace('Child joined default method of arkFile: ', ts.SyntaxKind[child.kind]);
135            // join default method
136        }
137    });
138
139    const nestedMergedNameSpaces = mergeNameSpaces(nestedNamespaces);
140    nestedMergedNameSpaces.forEach(nestedNameSpace => {
141        namespace.addNamespace(nestedNameSpace);
142        if (nestedNameSpace.isExport()) {
143            const linCol = new LineColPosition(nestedNameSpace.getLine(), nestedNameSpace.getColumn());
144            namespace.addExportInfo(buildExportInfo(nestedNameSpace, namespace.getDeclaringArkFile(), linCol));
145        }
146    });
147}
148
149function genDefaultArkClass(ns: ArkNamespace, node: ts.ModuleDeclaration, sourceFile: ts.SourceFile): void {
150    let defaultClass = new ArkClass();
151
152    buildDefaultArkClassFromArkNamespace(ns, defaultClass, node, sourceFile);
153    ns.setDefaultClass(defaultClass);
154    ns.addArkClass(defaultClass);
155}
156
157export function mergeNameSpaces(arkNamespaces: ArkNamespace[]): ArkNamespace[] {
158    const namespaceMap = new Map<string, ArkNamespace>();
159    for (let i = 0; i < arkNamespaces.length; i++) {
160        const currNamespace = arkNamespaces[i];
161        const currName = currNamespace.getName();
162        if (namespaceMap.has(currName)) {
163            const prevNamespace = namespaceMap.get(currName)!;
164            const nestedPrevNamespaces = prevNamespace.getNamespaces();
165            const nestedCurrNamespaces = currNamespace.getNamespaces();
166            const nestedMergedNameSpaces = mergeNameSpaces([...nestedPrevNamespaces, ...nestedCurrNamespaces]);
167            nestedMergedNameSpaces.forEach(nestedNameSpace => {
168                prevNamespace.addNamespace(nestedNameSpace);
169            });
170            const classes = currNamespace.getClasses();
171            classes.forEach(cls => {
172                prevNamespace.addArkClass(cls);
173            });
174            const preSourceCodes = prevNamespace.getCodes();
175            const currSourceCodes = currNamespace.getCodes();
176            prevNamespace.setCodes([...preSourceCodes, ...currSourceCodes]);
177            const prevLineColPairs = prevNamespace.getLineColPairs();
178            const currLineColPairs = currNamespace.getLineColPairs();
179            prevNamespace.setLineCols([...prevLineColPairs, ...currLineColPairs]);
180        } else {
181            namespaceMap.set(currName, currNamespace);
182        }
183    }
184    return [...namespaceMap.values()];
185}
186