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