• 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, 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