• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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