1/* 2* Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development 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 { DirTemp, DtscppRootInfo, FuncInfo, InterfaceList, TypeList, 17ParamObj, ParseObj, ClassObj, FuncObj, GenInfo, 18UnionObj, StructObj, TypeObj } from "./datatype"; 19import { replaceAll } from "../common/tool"; 20import fs = require('fs'); 21import path = require("path"); 22import { napiFuncHTemplate, napiFuncInitTemplate } from "../template/func_template"; 23import { cppout, dtscppout } from "../template/dtscpp/dtscppdir"; 24import { analyzeRootFunction, genDtsFile, genDtsInterface, genTsFunction } from "./gendts"; 25import { generateDirectFunction, genHCppFile } from "./gencpp"; 26import { genAbilitytestFile, generateFuncTestCase } from "./gentest"; 27import { Logger } from "../common/log"; 28import { tsTransferType } from "../template/functypemap_template"; 29import { dts2CppKey } from "../template/dtscpp/dts2cpp_key"; 30 31interface GenResult { 32 // dts文件中的内容 33 dtsContent: string; 34 // abilitytest文件中的内容 35 testContet: string; 36 // h文件中的内容 37 napiHContent: string; 38 napiInitContent: string, 39 napiCppContent: string 40} 41 42export function genHFunction(func: FuncInfo, rawFileName: string) { 43 let funcParams = ''; 44 for (let i = 0; i < func.params.length; ++i) { 45 funcParams += i > 0 ? ', ' : ''; 46 funcParams += func.params[i].name + ': ' + func.params[i].type; 47 } 48 let hContent = replaceAll(napiFuncHTemplate, '[file_introduce_replace]', rawFileName); 49 hContent = replaceAll(hContent, '[func_introduce_replace]', func.name); 50 hContent = replaceAll(hContent, '[input_introduce_replace]', funcParams === '' ? 'void' : funcParams); 51 hContent = replaceAll(hContent, '[func_name_replace]', func.name); 52 hContent = replaceAll(hContent, '[func_param_replace]', funcParams); 53 hContent = replaceAll(hContent, '[func_return_replace]', func.retType === ''? 'void': func.retType); 54 55 return hContent; 56} 57 58export function replaceContent(fileContent: string, funcContent: GenResult, rootInfo: DtscppRootInfo) { 59 let upperFileName = rootInfo.fileName.toLocaleUpperCase(); 60 61 fileContent = replaceAll(fileContent, '[fileName]', rootInfo.fileName); 62 fileContent = replaceAll(fileContent, '[upper_filename]', upperFileName); 63 fileContent = replaceAll(fileContent, '[dts_content_template]', funcContent.dtsContent); 64 fileContent = replaceAll(fileContent, '[init_replace]', funcContent.napiInitContent); 65 fileContent = replaceAll(fileContent, '[func_declare_replace]', funcContent.napiHContent); 66 fileContent = replaceAll(fileContent, '[func_content_replace]', funcContent.napiCppContent); 67 fileContent = replaceAll(fileContent, '[testAbilityFunctions]', funcContent.testContet); 68 69 return fileContent; 70} 71 72export function genDir(dirItem: DirTemp, funcContent: GenResult, rootInfo: DtscppRootInfo, out: string) 73{ 74 let dirPath = path.join(out, dirItem.name); 75 let lowerFileName = rootInfo.fileName.toLocaleLowerCase(); 76 77 // 创建目录 78 if (!fs.existsSync(dirPath)) { 79 fs.mkdirSync(dirPath, { recursive: true }); 80 } 81 82 // 遍历文件 83 dirItem.files.forEach(file => { 84 let fileName = file.name.replace('[fileName]', lowerFileName); 85 let filePath = path.join(dirPath, fileName); 86 // 将content写入文件, 这里的content是模板,需要replace里面的部分内容 87 if (!fs.existsSync(filePath)) { 88 // replace file content 89 // 这里的替换是替换模板公共的东西,方法的替换在哪里生成呢? 90 let fileContent = replaceContent(file.content, funcContent, rootInfo); 91 fs.writeFileSync(filePath, fileContent); 92 } 93 }) 94 95 // 递归遍历子目录 96 dirItem.dirs.forEach(subDir => { 97 genDir(subDir, funcContent, rootInfo, dirPath); 98 }) 99} 100 101export function generateFuncCode(rootInfo: DtscppRootInfo) { 102 let genResult: GenResult = { 103 dtsContent: '', 104 testContet: '', 105 napiHContent: '', 106 napiInitContent: '', 107 napiCppContent: '', 108 } 109 110 let typeList: TypeList[] = [] 111 let interfaceList: InterfaceList[] = [] 112 113 // 分析的时候拿到typeList和interfaceList 114 let interDef = genDtsInterface(rootInfo.rawFilePath, typeList, interfaceList); 115 let tsFuncContent = ''; 116 // analyze 117 let tsfunctions: FuncInfo[] = []; 118 let cppfunctions: FuncInfo[] = []; 119 analyzeRootFunction(tsfunctions, cppfunctions, rootInfo.funcs); 120 let rawFileName = path.basename(rootInfo.rawFilePath); 121 // gen 122 for (let i = 0; i < rootInfo.funcs.length; i++) { 123 // gen dts function 124 tsFuncContent += genTsFunction(tsfunctions[i], rawFileName); 125 // 每个napi方法的init 126 genResult.napiInitContent += replaceAll(napiFuncInitTemplate, '[func_name_replace]', tsfunctions[i].name); 127 // 每个napi方法的h声明 128 genResult.napiHContent += genHFunction(cppfunctions[i], rawFileName); 129 // 每个Napi方法的cpp说明 130 genResult.napiCppContent += generateDirectFunction(cppfunctions[i], rawFileName, typeList, interfaceList); 131 // gen test function 132 genResult.testContet += generateFuncTestCase(cppfunctions[i], rawFileName, typeList, interfaceList); 133 134 } 135 genResult.dtsContent = interDef + tsFuncContent; 136 return genResult; 137} 138 139// h2dtscpp 140export function genDtsCppFile(rootInfo: DtscppRootInfo, out: string) { 141 let res: GenResult = generateFuncCode(rootInfo); 142 genDir(dtscppout, res, rootInfo, out); 143 Logger.getInstance().info('generate success!') 144} 145 146// dts2cpp 147export function genCppFile(parseObj: ParseObj, tsFilePath: string, out: string) { 148 let rootInfo: DtscppRootInfo = { 149 funcs: parseObj.funcs, 150 rawFilePath: tsFilePath, 151 fileName: path.basename(tsFilePath, '.d.ts')// xxx 152 }; 153 let genResult: GenResult = generateFunctions(parseObj, tsFilePath); 154 genDir(cppout, genResult, rootInfo, out); 155 Logger.getInstance().info('generate success!') 156} 157 158export function generateFunctions(parseObj: ParseObj, tsFilePath: string) { 159 let cppfunctions: FuncInfo[] = getFunctions(parseObj); 160 let typeList: TypeList[] = getTypes(parseObj); 161 let interfaceList: InterfaceList[] = getInterfaces(parseObj); 162 163 let genResult: GenResult = { 164 dtsContent: '', 165 testContet: '', 166 napiHContent: '', 167 napiInitContent: '', 168 napiCppContent: '', 169 }; 170 let rawFileName = path.basename(tsFilePath); 171 for (let i = 0; i < cppfunctions.length; i++) { 172 // 每个napi方法的init 173 genResult.napiInitContent += replaceAll(napiFuncInitTemplate, '[func_name_replace]', cppfunctions[i].name); 174 // 每个napi方法的h声明 175 genResult.napiHContent += genHFunction(cppfunctions[i], rawFileName); 176 // 每个Napi方法的cpp说明 177 genResult.napiCppContent += generateDirectFunction(cppfunctions[i], rawFileName, typeList, interfaceList); 178 // gen test function 179 genResult.testContet += generateFuncTestCase(cppfunctions[i], rawFileName, typeList, interfaceList); 180 181 } 182 return genResult; 183} 184 185// 将interface列表中的js type全部转换为c type 186export function getInterfaces(parseObj: ParseObj) { 187 return parseObj.classes.map(cls => { 188 const getParams = (variables: ParamObj[]) => 189 variables.map(variable => ({ 190 name: variable.name, 191 type: getCTypeFromJS(variable.type), 192 arraySize: variable.arraySize, 193 arraySizeList: [] 194 })); 195 196 const getFunctions = (functions: FuncObj[]) => 197 functions.map(func => ({ 198 type: func.type, 199 name: func.name, 200 returns: getCTypeFromJS(func.returns), 201 parameters: getParams(func.parameters) 202 })); 203 204 return { 205 interfaceName: cls.name, 206 interfaceBody: { 207 params: getParams(cls.variableList), 208 funcs: getFunctions(cls.functionList) 209 } 210 }; 211 }); 212} 213 214export function getTypes(parseObj: ParseObj) { 215 let typeList: TypeList[] = []; 216 for (let i = 0; i < parseObj.types!.length; i++) { 217 let typeObj: TypeList = { 218 typeName: parseObj.types![i].name, 219 typeBody: getCTypeFromJS(parseObj.types![i].alias), 220 }; 221 typeList.push(typeObj); 222 } 223 return typeList; 224} 225 226export function getFunctions(parseObj: ParseObj) { 227 let cppfunctions: FuncInfo[] = []; 228 for (let i = 0; i < parseObj.funcs.length; i++) { 229 let cppFuncInfo: FuncInfo = { 230 name: '', 231 params: [], 232 retType: '', 233 }; 234 cppFuncInfo.name = parseObj.funcs[i].name; 235 let parseParams = parseObj.funcs[i].parameters; 236 for (let i = 0; i < parseParams.length; ++i) { 237 let paramsRes = createFuncParam(parseParams[i]); 238 cppFuncInfo.params.push(paramsRes); 239 } 240 cppFuncInfo.retType = getCTypeFromJS(parseObj.funcs[i].returns); 241 cppfunctions.push(cppFuncInfo); 242 } 243 return cppfunctions; 244} 245 246export function getCTypeFromJS(type: string) { 247 let cType = type; 248 for (let index = 0; index < tsTransferType.length; index++) { 249 if (type === tsTransferType[index].fromType) { 250 cType = tsTransferType[index].tranferContent[0]; 251 } 252 } 253 return cType; 254} 255 256export function createFuncParam(params: ParamObj) { 257 let cppParam: ParamObj = { 258 name: '', 259 type: '', 260 arraySize: 0, 261 arraySizeList: [] 262 }; 263 cppParam.name = params.name; 264 cppParam.type = getCTypeFromJS(params.type); 265 return cppParam; 266} 267 268// -----------------------h2dtscpp------------------------ 269export function createDir(path: string) { 270 if (!fs.existsSync(path)) { 271 fs.mkdirSync(path); 272 } 273} 274export function genDtscppFromH(rootInfo: GenInfo) { 275 let rootDir = path.dirname(rootInfo.rawFilePath); 276 let cppOutPath = path.join(rootDir, 'cpp'); 277 createDir(cppOutPath); 278 let dtsOutPath = path.join(cppOutPath, 'types'); 279 createDir(dtsOutPath); 280 // 生成dts文件: 这里将文件生成在cpp/types目录下,该路径是ndk工程中的dts文件的默 281 // 认路径 282 genDtsFile(rootInfo, dtsOutPath); 283 // 生成.cpp和.h文件:这里将文件生成在cpp目录下,该路径是ndk工程中的cpp文件的默 284 // 认路径 285 genHCppFile(rootInfo, cppOutPath); 286 let testOutPath = path.join(rootDir, 'test'); 287 createDir(testOutPath); 288 testOutPath = path.join(testOutPath, 'ets'); 289 createDir(testOutPath); 290 // 生成Ability.test.ets文件:这里将文件生成在test/ets目录下,该路径是ndk工程中 291 // 的test文件的默认路径 292 genAbilitytestFile(rootInfo, testOutPath); 293 Logger.getInstance().info('generate success!') 294} 295 296// -------------------dts2cpp------------------------ 297// 将dts类型转换为c++类型 298export function transCkey2Dtskey(key: string): string { 299 // 箭头函数类型: (a:number,b:string)=>void -> std::function<void(double, string)> 300 const arrowFuncReg = /\(([\w\:\<\>\,\s*]*)\)\s*=>([\w\s\:<\>\,\s*]+)/; 301 const arrowFuncMatch = key.match(arrowFuncReg); 302 if (arrowFuncMatch) { 303 const paramsStr = arrowFuncMatch[1] ? arrowFuncMatch[1].trim() : ''; 304 let paramreg = /([\w\s\:\*]+<[^>]*>|[\*\w\s\:]+)/g; 305 let pmatch; 306 let paramList = []; 307 while ((pmatch = paramreg.exec(paramsStr)) !== null) { 308 paramList.push(pmatch[0]); 309 } 310 let str = ''; 311 for (let i = 0; i < paramList.length; ++i) { 312 const [paramName, paramType] = paramList[i].split(':').map((item) => item.trim()); 313 str += paramType === '' ? '' : transCkey2Dtskey(paramType); 314 if (i != paramList.length - 1) { 315 str += ', '; 316 } 317 } 318 return `std::function<${transCkey2Dtskey(arrowFuncMatch[2].trim())}(${str})>`; 319 } 320 321 // Callback<boolean> -> std::function<void(bool)> 322 const callbackReg = /Callback\s*<([^>]+)>/g; 323 const callbackMatch = callbackReg.exec(key); 324 if (callbackMatch) { 325 return `std::function<void(${transCkey2Dtskey(callbackMatch[1].trim())})>`; 326 } 327 328 // 基本类型:number/string/boolean/Map/Set/Array 329 for (const keyItem of dts2CppKey) { 330 for (const str of keyItem.keys) { 331 if (key.trim().replace(' ', '') === str) { 332 return keyItem.value; 333 } 334 } 335 } 336 // 如果是object类型,直接返回类型名如:a: AType -> AType 337 return key; 338} 339 340export function transParseObj(parseObj: ParseObj) { 341 // trans enums 342 let enums = parseObj.enums; 343 // trans unions 344 let unions: UnionObj[] = []; 345 for (let union of parseObj.unions) { 346 unions.push({ 347 name: union.name, 348 alias: union.alias, 349 members: transParameters(union.members), 350 }); 351 } 352 // trans structs 353 let structs: StructObj[] = []; 354 for (let struct of parseObj.structs) { 355 structs.push({ 356 name: struct.name, 357 alias: struct.alias, 358 members: transParameters(struct.members), 359 functions: transFunctions(struct.functions) 360 }); 361 } 362 // trans classes 363 let classes: ClassObj[] = []; 364 for (let classObj of parseObj.classes) { 365 classes.push({ 366 name: classObj.name, 367 alias: classObj.alias, 368 variableList: transParameters(classObj.variableList), 369 functionList: transFunctions(classObj.functionList) 370 }); 371 } 372 // trans funcs 373 let funcs: FuncObj[] = transFunctions(parseObj.funcs); 374 375 // trans types : 首先判断types是否存在 376 let types: TypeObj[] = []; 377 if (parseObj.types && parseObj.types.length > 0) { 378 for (let type of parseObj.types) { 379 let memberType: string[] = type.types; // 这个types是啥?里面的成员是不是需要转换啊 380 for (let member of memberType) { 381 let cType = transCkey2Dtskey(member); 382 memberType.push(cType); 383 } 384 types.push({ 385 name: type.name, 386 alias: type.alias, 387 members: transParameters(type.members), 388 types: memberType, // for test. 这里面是啥还不知道 389 functions: transFunctions(type.functions) 390 }); 391 } 392 } 393 394 let transParseObj: ParseObj = { 395 enums: enums, 396 unions: unions, 397 structs: structs, 398 classes: classes, 399 funcs: funcs, 400 types: types 401 }; 402 403 return transParseObj; 404} 405 406// 将FuncObj[]中的ts type全转换成cpp type 407function transFunctions(tranFuncs: FuncObj[]) { 408 let funcs: FuncObj[] = []; 409 for (let func of tranFuncs) { 410 funcs.push({ 411 name: func.name, 412 type: func.type, 413 returns: transCkey2Dtskey(func.returns), 414 parameters: transParameters(func.parameters), 415 }); 416 } 417 return funcs; 418} 419 420// 将ParamObj[]中的ts type全转换成cpp type 421export function transParameters(transMembers: ParamObj[]) { 422 let members: ParamObj[] = []; 423 for (let member of transMembers) { 424 members.push({ 425 type: transCkey2Dtskey(member.type), 426 name: member.name, 427 arraySize: member.arraySize, 428 arraySizeList: member.arraySizeList 429 }); 430 } 431 return members; 432} 433 434export function genCppFromDts(rootInfo: GenInfo) { 435 // 要将rootInfo中的type信息转换为c++类型,然后写入cpp文件中 436 let hRootInfo: GenInfo = { 437 parseObj: transParseObj(rootInfo.parseObj), 438 rawFilePath: rootInfo.rawFilePath, 439 fileName: rootInfo.fileName 440 } 441 // 生成napi框架(.h文件和.cpp文件):这里将文件生成在cpp目录下,该路径是ndk工程中 442 // 的cpp文件的默认路径 443 let rootDir = path.dirname(hRootInfo.rawFilePath); 444 let cppOutPath = path.join(rootDir, 'cpp'); 445 createDir(cppOutPath); 446 genHCppFile(hRootInfo, cppOutPath); 447 // 生成Ability.test.ets文件: 这里将文件生成在test/ets目录下,该路径是ndk工程中 448 // 的test文件的默认路径 449 let testOutPath = path.join(rootDir, 'test'); 450 createDir(testOutPath); 451 testOutPath = path.join(testOutPath, 'ets'); 452 createDir(testOutPath); 453 genAbilitytestFile(hRootInfo, testOutPath); 454} 455 456