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, FuncInfo, FuncObj, GenInfo, InterfaceList, TypeList } from "./datatype"; 17import { getInterfaceBody, getTypeBody } from "./gendts"; 18import { 19 boolIn, boolRet, doubleIn, doubleRet, funcGetParamTemplate, int32tIn, int32tRet, int64tIn, int64tRet, 20 napiFuncCppTemplate, napiFuncHTemplate, napiFuncInitTemplate, napiFuncRetTemplate, objectRet, objectTosetRet, 21 paramGenTemplate, promiseRet, stringIn, stringRet, 22 uint32tIn, uint32tRet 23} from "../template/func_template"; 24import { replaceAll } from "../common/tool"; 25import { cppdir } from "../template/dtscpp/dtscppdir"; 26import * as path from 'path'; 27import * as fs from 'fs'; 28import { h2NapiInKey, h2NapiOutKey } from "../template/dtscpp/dts2cpp_key"; 29import { napiCppTemplate } from "../template/dtscpp/dtscpp_napicpp_template"; 30 31interface RetObjInfo { 32 objName: string; 33 flag: boolean; 34} 35 36export function generateDirectFunction(funcInfo: FuncInfo, rawFileName: string, typeList: TypeList[], interfaceList: InterfaceList[]) { 37 38 // 生成 39 let paramGenResult = genParamInfo(funcInfo, typeList); 40 41 // 返回值处理 对于对象要使用循环处理 42 let retGenResult = ''; 43 let retObjInfo: RetObjInfo = { 44 objName: '', 45 flag: false 46 }; 47 48 let returnType = replaceAll(funcInfo.retType, '*', '').trim(); 49 retGenResult = returnTypeC2Js(funcInfo.name, returnType, retGenResult, retObjInfo, typeList, interfaceList); 50 51 let bodyReplace = getReplaceInfo(funcInfo, rawFileName); 52 53 let genParamReplace = getGenParamReplace(funcInfo, paramGenResult); 54 bodyReplace = getBodyReplace2(funcInfo, bodyReplace, genParamReplace); 55 if (funcInfo.retType.replace('*', '').trim() !== 'void') { 56 let returnType = funcInfo.retType === 'std::string' ? 'const char *' : funcInfo.retType; 57 returnType = returnType === 'size_t' ? 'int64_t' : returnType; 58 let funcReturnReplace = replaceAll(napiFuncRetTemplate, '[return_name]', retObjInfo.objName); 59 funcReturnReplace = replaceAll(funcReturnReplace, '[func_name_replace]', funcInfo.name); 60 funcReturnReplace = replaceAll(funcReturnReplace, '[return_replace]', retGenResult); 61 bodyReplace = replaceAll(bodyReplace, '[func_return_replace]', funcReturnReplace); 62 } else { 63 bodyReplace = replaceAll(bodyReplace, '[func_return_replace]', ' return NULL;\n'); 64 } 65 bodyReplace = replaceAll(bodyReplace, '[return_replace]', retGenResult); 66 67 68 return bodyReplace; 69} 70 71export function getReplaceInfo(funcInfo: FuncInfo, hFileName: string) { 72 let funcInfoParams = genFuncInfoParams(funcInfo); 73 let bodyReplace = replaceAll(napiFuncCppTemplate, '[func_name_replace]', funcInfo.name); 74 bodyReplace = replaceAll(bodyReplace, '[get_error_msg_tag]', funcInfo.name); 75 bodyReplace = replaceAll(bodyReplace, '[file_introduce_replace]', hFileName); 76 bodyReplace = replaceAll(bodyReplace, '[func_introduce_replace]', funcInfo.name); 77 bodyReplace = replaceAll(bodyReplace, '[input_introduce_replace]', funcInfoParams === '' ? 'void' : funcInfoParams); 78 bodyReplace = replaceAll(bodyReplace, '[output_introduce_replace]', funcInfo.retType); 79 return bodyReplace; 80} 81 82export function getBodyReplace2(funcInfo: FuncInfo, bodyReplace: string, genParamReplace: string) { 83 if (funcInfo.params.length !== 0) { 84 bodyReplace = replaceAll(bodyReplace, '[func_getParam_replace]', genParamReplace); 85 } else { 86 bodyReplace = replaceAll(bodyReplace, '[func_getParam_replace]', ''); 87 } 88 return bodyReplace; 89} 90 91export function getGenParamReplace(funcInfo: FuncInfo, paramGenResult: string) { 92 let genParamReplace = replaceAll(funcGetParamTemplate, '[param_length]', 'PARAMS' + funcInfo.params.length); 93 genParamReplace = replaceAll(genParamReplace, '[func_name_replace]', funcInfo.name); 94 genParamReplace = replaceAll(genParamReplace, '[getAllParam_replace]', paramGenResult); 95 return genParamReplace; 96} 97 98export function genFuncInfoParams(funcInfo: FuncInfo) { 99 let funcInfoParams = ''; 100 let funcInfoParamTemp = '[paramName]: [paramType]; '; 101 for (let i = 0; i < funcInfo.params.length; i++) { 102 let funcInfoParamReplace = replaceAll(funcInfoParamTemp, '[paramName]', funcInfo.params[i].name); 103 funcInfoParamReplace = replaceAll(funcInfoParamReplace, '[paramType]', funcInfo.params[i].type); 104 funcInfoParams += funcInfoParamReplace; 105 } 106 return funcInfoParams; 107} 108 109 110export function genParamInfo(funcInfo: FuncInfo, typeList: TypeList[]) { 111 let paramGenResult = ''; 112 // napi 获取参数 113 for (let i = 0; i < funcInfo.params.length; i++) { 114 paramGenResult = getParamJs2C(funcInfo, i, paramGenResult, typeList); 115 } 116 return paramGenResult; 117} 118 119 120export function getParamJs2C(funcInfo: FuncInfo, i: number, paramGenResult: string, typeList: TypeList[]) { 121 let paramType = funcInfo.params[i].type === 'size_t' ? 'int64_t' : funcInfo.params[i].type; 122 // 去除const 和 * 123 paramType = paramType.replace('const', '').replace('*', '').trim(); 124 let paramName = funcInfo.params[i].name; 125 let paramGen = replaceAll(paramGenTemplate, '[param_index_replace]', 'PARAMS' + i); 126 paramGen = replaceAll(paramGen, '[param_name_replace]', paramName); 127 if (paramType === 'double') { 128 paramGen = getParamGenCon(doubleIn, i, paramName, paramGen); 129 paramGenResult += paramGen; 130 } else if (paramType === 'uint32_t') { 131 paramGen = getParamGenCon(uint32tIn, i, paramName, paramGen); 132 paramGenResult += paramGen; 133 } else if (paramType === 'int32_t' || paramType === 'int') { 134 paramGen = getParamGenCon(int32tIn, i, paramName, paramGen); 135 paramGenResult += paramGen; 136 } else if (paramType === 'int64_t' || paramType === 'size_t') { 137 paramGen = getParamGenCon(int64tIn, i, paramName, paramGen); 138 paramGenResult += paramGen; 139 } else if (paramType === 'bool') { 140 paramGen = getParamGenCon(boolIn, i, paramName, paramGen); 141 paramGenResult += paramGen; 142 } else if (paramType === 'std::string' || paramType.indexOf('char') >= 0) { 143 paramGen = getParamGenCon(stringIn, i, paramName, paramGen); 144 paramGenResult += paramGen; 145 } else if (getTypeBody(paramType, typeList)) { 146 // typedefs 147 funcInfo.params[i].type = getTypeBody(paramType, typeList) as string; 148 paramGenResult = getParamJs2C(funcInfo, i, paramGenResult, typeList); 149 } 150 // 其他情况,处理成对象 napi_get_cb_info之后不做任何处理 151 return paramGenResult; 152} 153 154export function getParamGenCon(getParamContent: string, i: number, paramName: string, paramGen: string) { 155 let getParam = replaceAll(getParamContent, '[param_index_replace]', 'PARAMS' + i); 156 getParam = replaceAll(getParam, '[param_name_replace]', paramName); 157 paramGen = replaceAll(paramGen, '[getParam_replace]', getParam); 158 return paramGen; 159} 160 161export function returnTypeC2Js(returnName: string, retType: string, retGenResult: string, retObjInfo: RetObjInfo, typeList: TypeList[], interfaceList: InterfaceList[]) { 162 if (!retObjInfo.flag) { 163 retObjInfo.objName = returnName; 164 } 165 if (retType === 'uint32_t') { 166 retGenResult = getRetTypeContent(uint32tRet, returnName, retGenResult, retObjInfo, objectTosetRet); 167 } else if (retType === 'double') { 168 retGenResult = getRetTypeContent(doubleRet, returnName, retGenResult, retObjInfo, objectTosetRet); 169 } else if (retType === 'int32_t' || retType === 'int') { 170 retGenResult = getRetTypeContent(int32tRet, returnName, retGenResult, retObjInfo, objectTosetRet); 171 } else if (retType === 'int64_t' || retType === 'size_t') { 172 retGenResult = getRetTypeContent(int64tRet, returnName, retGenResult, retObjInfo, objectTosetRet); 173 } else if (retType === 'bool') { 174 retGenResult = getRetTypeContent(boolRet, returnName, retGenResult, retObjInfo, objectTosetRet); 175 } else if (retType === 'std::string' || retType.substring(0, 10) === 'const char' || 176 retType === 'char') { 177 retGenResult = getRetTypeContent(stringRet, returnName, retGenResult, retObjInfo, objectTosetRet); 178 } else if (getInterfaceBody(retType, interfaceList)) { 179 // 返回值是对象 180 if (!retObjInfo.flag) { 181 retGenResult += replaceAll(objectRet, '[return_name_replace]', returnName); 182 retObjInfo.flag = true; 183 let objectProperty = getInterfaceBody(retType, interfaceList) 184 // 遍历属性 185 for (let i = 0; i < objectProperty!.params.length; i++) { 186 let name = objectProperty!.params[i].name; 187 let type = objectProperty!.params[i].type; 188 let testRes = returnTypeC2Js(name, type, retGenResult, retObjInfo, typeList, interfaceList); 189 retGenResult = testRes; 190 } 191 } else { 192 retGenResult = getObjRetGenResult(retObjInfo, retGenResult, returnName); 193 } 194 } else if (getTypeBody(retType, typeList)) { 195 // typedefs 196 let funcRetType = getTypeBody(retType, typeList) as string; 197 retGenResult = returnTypeC2Js(returnName, funcRetType, retGenResult, retObjInfo,typeList, interfaceList); 198 } 199 return retGenResult; 200} 201 202export function getObjRetGenResult(retObjInfo: RetObjInfo, retGenResult: string, returnName: string) { 203 if (retObjInfo.objName !== '') { 204 retGenResult += replaceAll(objectRet, '[return_name_replace]', returnName); 205 let setRetPropertyObj = replaceAll(objectTosetRet, '[set_objname_replace]', retObjInfo.objName); 206 setRetPropertyObj = replaceAll(setRetPropertyObj, '[set_propname_replace]', returnName); 207 setRetPropertyObj = replaceAll(setRetPropertyObj, '[set_propvalue_replace]', returnName); 208 retGenResult += setRetPropertyObj; 209 } 210 return retGenResult; 211} 212 213export function getRetTypeContent(retTypeTemplate: string, returnName: string, retGenResult: string, 214 retObjInfo: RetObjInfo, setRetProperty: string) { 215 let funcReturnType = replaceAll(retTypeTemplate, '[return_name_replace]', returnName); 216 retGenResult += funcReturnType; 217 if (retObjInfo.flag) { 218 setRetProperty = replaceAll(setRetProperty, '[set_objname_replace]', retObjInfo.objName); 219 setRetProperty = replaceAll(setRetProperty, '[set_propname_replace]', returnName); 220 setRetProperty = replaceAll(setRetProperty, '[set_propvalue_replace]', returnName); 221 retGenResult += setRetProperty; 222 } 223 return retGenResult; 224} 225 226 227// ------------------------------ gencpp ----------------------------- 228const fileHandlers: { [key: string]: Function } = { 229 '[fileName]init.cpp': genInitCppFile, 230 '[fileName]napi.h': genNapiHFile, 231 '[fileName]napi.cpp': genNapiCppFile, 232 '[fileName]common.h': genCommonHFile, 233 '[fileName]common.cpp': genCommonCppFile, 234 '[fileName].h': genCommonFile, 235 'readme.md': genCommonFile 236}; 237 238// 通过类型值映射模板,比如:uint32_t返回值 -> uint32tRet -> napi_create_uint32 239export function transCkey2NapiOutkey(key: string) { 240 // 如果是ts传递的Promise<>类型,并且transTs2C时未转换,那么就返回promiseRet 241 let tsPromiseReg = /Promise<([^>]+)>/g; 242 const tsPromiseMatch = tsPromiseReg.exec(key); 243 if (tsPromiseMatch) { 244 return promiseRet; 245 } 246 247 // 数组 map set iterator tuple pair 等都当作objectOut处理 248 for (const keyItem of h2NapiOutKey) { 249 for (const str of keyItem.keys) { 250 if (key.includes(str)) { 251 return keyItem.value; 252 } 253 } 254 } 255 let replaceKeyList = ['enum', 'struct', 'union']; 256 for (const rkey of replaceKeyList) { 257 key = key.replace(rkey, '').trim(); 258 } 259 // 其他的全部当作object处理, 如typeDef/enum/struct/union/class等,当作objectOut处理,返回objectRet 260 return objectRet; 261} 262 263// 通过类型值映射模板,比如:uint32_t输入 -> uint32tIn -> napi_get_value_uint32 264export function transCkey2NapiInkey(key: string) { 265 for (const keyItem of h2NapiInKey) { 266 for (const str of keyItem.keys) { 267 if (key.includes(str)) { 268 return keyItem.value; 269 } 270 } 271 } 272 let replaceKeyList = ['enum', 'struct', 'union']; 273 for (const rkey of replaceKeyList) { 274 key = key.replace(rkey, '').trim(); 275 } 276 // 其他的全部当作object处理, 如typeDef/enum/struct/union/class等, 此时不需要做任何处理,因此返回空 277 return ''; 278 279} 280 281// 把这些东西分成一个个文件,根据文件内容来生成 282export function genCommonFile(rootInfo: GenInfo, filePath: string, 283 fileContent: string) { 284 fs.writeFileSync(filePath, fileContent); 285} 286// 生成Init的文件 287 288export function genInitCppFile(rootInfo: GenInfo, filePath: string, 289 fileContent: string) { 290 let napiInitContent = ''; 291 if (rootInfo.parseObj && rootInfo.parseObj.funcs) { 292 rootInfo.parseObj.funcs.forEach(func => { 293 let funcName = func.name; 294 napiInitContent += replaceAll(napiFuncInitTemplate, 295 '[func_name_replace]', funcName); 296 }); 297 } 298 // 写文件 299 fileContent = replaceAll(fileContent, '[fileName]', rootInfo.fileName); 300 fileContent = replaceAll(fileContent, '[init_replace]', napiInitContent); 301 fs.writeFileSync(filePath, fileContent); 302} 303 304// 生成common.h文件,这个读模板直接生成 305export function genCommonHFile(rootInfo: GenInfo, filePath: string, 306 fileContent: string) { 307 let upperFileName = rootInfo.fileName.toLocaleUpperCase(); 308 fileContent = replaceAll(fileContent, '[fileName]', rootInfo.fileName); 309 fileContent = replaceAll(fileContent, '[upper_filename]', upperFileName); 310 fs.writeFileSync(filePath, fileContent); 311} 312// 生成common.cpp文件,读模板直接生成 313export function genCommonCppFile(rootInfo: GenInfo, filePath: string, fileContent: string) { 314 fileContent = replaceAll(fileContent, '[fileName]', rootInfo.fileName); 315 fs.writeFileSync(filePath, fileContent); 316} 317 318// 生成napi.h文件 319export function genNapiHFile(rootInfo: GenInfo, filePath: string, 320 fileContent: string) { 321 let napiHContent = ''; 322 if (rootInfo.parseObj && rootInfo.parseObj.funcs) { 323 rootInfo.parseObj.funcs.forEach(func => { 324 let funcParams = ''; 325 for (let i = 0; i < func.parameters.length; ++i) { 326 funcParams += i > 0 ? ', ' : ''; 327 funcParams += func.parameters[i].name + ': ' + func.parameters[i].type; 328 } 329 let rawFileName = path.basename(rootInfo.rawFilePath); 330 let hContent = replaceAll(napiFuncHTemplate, '[file_introduce_replace]', rawFileName); 331 hContent = replaceAll(hContent, '[input_introduce_replace]', funcParams === '' ? 'void' : funcParams); 332 hContent = replaceAll(hContent, '[func_name_replace]', func.name); 333 hContent = replaceAll(hContent, '[func_param_replace]', funcParams); 334 hContent = replaceAll(hContent, '[func_return_replace]', 335 func.returns === '' ? 'void' : func.returns); 336 napiHContent += hContent; 337 }); 338 } 339 let upperFileName = rootInfo.fileName.toLocaleUpperCase(); 340 fileContent = replaceAll(fileContent, '[fileName]', rootInfo.fileName); 341 fileContent = replaceAll(fileContent, '[upper_filename]', upperFileName); 342 fileContent = replaceAll(fileContent, '[func_declare_replace]', napiHContent); 343 fs.writeFileSync(filePath, fileContent); 344} 345 346// 生成napi.cpp文件 347export function genNapiCppFile(rootInfo: GenInfo, filePath: string, 348 fileContent: string) { 349 let napiCppContent = ''; 350 if (rootInfo.parseObj && rootInfo.parseObj.funcs) { 351 rootInfo.parseObj.funcs.forEach(funcInfo => { 352 // 替换每个方法主体 353 let hFileName = path.basename(rootInfo.rawFilePath); 354 let bodyReplace = replaceAll(napiFuncCppTemplate, '[func_name_replace]', 355 funcInfo.name); 356 bodyReplace = replaceAll(bodyReplace, '[get_error_msg_tag]', 357 funcInfo.name); 358 bodyReplace = replaceAll(bodyReplace, '[file_introduce_replace]', 359 hFileName); 360 // 生成方法注释 361 let funcInfoParams = funcInfo.parameters.length > 0 ? '' : 'void'; 362 let funcInfoParamTemp = '[paramName]: [paramType]; '; 363 for (let i = 0; i < funcInfo.parameters.length; i++) { 364 let funcInfoParamReplace = replaceAll(funcInfoParamTemp, '[paramName]', 365 funcInfo.parameters[i].name); 366 funcInfoParamReplace = replaceAll(funcInfoParamReplace, '[paramType]', 367 funcInfo.parameters[i].type); 368 funcInfoParams += funcInfoParamReplace; 369 } 370 bodyReplace = replaceAll(bodyReplace, '[input_introduce_replace]', 371 funcInfoParams === '' ? 'void' : funcInfoParams); 372 bodyReplace = replaceAll(bodyReplace, '[output_introduce_replace]', 373 funcInfo.returns); 374 // 方法参数的处理,解析参数类型,生成napi的参数处理代码 375 let paramGenResult = getCppParamGen(funcInfo); 376 bodyReplace = replaceAll(bodyReplace, '[func_getParam_replace]', 377 paramGenResult); 378 // 方法返回值的处理,解析返回值类型,生成napi的返回值处理代码 379 let returnGenResult = genCppReturnGen(funcInfo); 380 bodyReplace = replaceAll(bodyReplace, '[func_return_replace]', 381 returnGenResult); 382 // 组合一个个方法 383 napiCppContent += bodyReplace; 384 }); 385 386 // 生成xxxNapi.cpp文件 387 fileContent = replaceAll(fileContent, '[fileName]', rootInfo.fileName); 388 fileContent = replaceAll(fileContent, '[func_content_replace]', napiCppContent); 389 fs.writeFileSync(filePath, fileContent); 390 } 391 392 fileContent = replaceAll(fileContent, '[fileName]', rootInfo.fileName); 393 fileContent = replaceAll(fileContent, '[func_content_replace]', napiCppContent); 394 fs.writeFileSync(filePath, fileContent); 395} 396 397// 方法输入参数的处理,只处理基本类型,像数组/map/set/class/struct等都全部当作 398// object,且不做处理 399export function getCppParamGen(funcInfo: FuncObj): string { 400 // 处理输入的参数,生成napi的参数处理代码 401 if (funcInfo.parameters.length === 0) { 402 return '// no input params'; 403 } 404 let paramGenResult = ''; 405 for (let i = 0; i < funcInfo.parameters.length; ++i) { 406 let getParamInTemplate = transCkey2NapiInkey(funcInfo.parameters[i].type); 407 // 如果getParamInTemplate是空,则默认是对象输入,不做任何处理 408 if (getParamInTemplate === '') { 409 paramGenResult += '// Todo: handle object input.\n\n'; 410 continue; 411 } 412 let getParam = replaceAll(getParamInTemplate, '[param_index_replace]', 413 'PARAMS' + i); 414 getParam = replaceAll(getParam, '[param_name_replace]', 415 funcInfo.parameters[i].name); 416 let paramGen = replaceAll(paramGenTemplate, '[param_index_replace]', 417 'PARAMS' + i); 418 paramGen = replaceAll(paramGen, '[param_name_replace]', 419 funcInfo.parameters[i].name); 420 paramGen = replaceAll(paramGen, '[getParam_replace]', getParam); 421 paramGenResult += paramGen; 422 } 423 let genParamReplace = replaceAll(funcGetParamTemplate, '[param_length]', 424 'PARAMS' + funcInfo.parameters.length); 425 genParamReplace = replaceAll(genParamReplace, '[func_name_replace]', 426 funcInfo.name); 427 genParamReplace = replaceAll(genParamReplace, '[getAllParam_replace]', 428 paramGenResult); 429 return genParamReplace 430} 431 432// 方法返回值的处理 433export function genCppReturnGen(funcInfo: FuncObj): string { 434 // 如果函数返回值是空,直接返回NULL 435 if (funcInfo.returns === 'void') { 436 return ' return NULL;\n'; 437 } 438 let returnName = funcInfo.name; 439 let funcReturnReplace = replaceAll(napiFuncRetTemplate, '[return_name]', 440 returnName); 441 let retGenResult = transCkey2NapiOutkey(funcInfo.returns); 442 retGenResult = replaceAll(retGenResult, '[return_name_replace]', returnName); 443 funcReturnReplace = replaceAll(funcReturnReplace, '[func_name_replace]', 444 funcInfo.name); 445 funcReturnReplace = replaceAll(funcReturnReplace, '[return_replace]', 446 retGenResult); 447 return funcReturnReplace; 448} 449 450export function genDir(dirItem: DirTemp, rootInfo: GenInfo, out: string) { 451 let dirPath = path.join(out, dirItem.name); 452 let lowerFileName = rootInfo.fileName.toLocaleLowerCase(); 453 // 创建目录 454 if (!fs.existsSync(dirPath)) { 455 fs.mkdirSync(dirPath, { recursive: true }); 456 } 457 // 遍历生成当前目录文件 458 dirItem.files.forEach(file => { 459 let fileName = file.name.replace('[fileName]', lowerFileName); 460 let filePath = path.join(dirPath, fileName); 461 // 将content写入文件, 这里的content是模板,需要replace里面的部分内容 462 if (!fs.existsSync(filePath)) { 463 // 拿到每个文件并且根据文件生成内容并写入 464 const handler = fileHandlers[file.name]; 465 if (handler) { 466 // 调用对应的生成文件方法 467 handler(rootInfo, filePath, file.content); 468 } 469 } 470 }) 471 // 遍历子目录,生成子目录的文件 472 dirItem.dirs.forEach(subDir => { 473 genDir(subDir, rootInfo, dirPath); 474 }) 475} 476 477// gen h and cpp file. 478export function genHCppFile(rootInfo: GenInfo, out: string) { 479 if (out === undefined || out === null || out.trim() === '') { 480 out = path.dirname(rootInfo.rawFilePath); 481 } 482 genDir(cppdir, rootInfo, out); 483} 484 485 486