diff --git a/src/vscode_plugin/src/gen/gencpp.ts b/src/vscode_plugin/src/gen/gencpp.ts index 52328ed708c0d120425308ca7c8e0d910f0e2ced..2bafbfb9a2d6e4faa12f15ab6b42c323afc59ebb 100644 --- a/src/vscode_plugin/src/gen/gencpp.ts +++ b/src/vscode_plugin/src/gen/gencpp.ts @@ -18,11 +18,11 @@ import { getInterfaceBody, getTypeBody } from "./gendts"; import { boolIn, boolRet, doubleIn, doubleRet, funcGetParamTemplate, int32tIn, int32tRet, int64tIn, int64tRet, napiFuncCppTemplate, napiFuncHTemplate, napiFuncInitTemplate, napiFuncRetTemplate, objectRet, objectTosetRet, - paramGenTemplate, stringIn, stringRet, + paramGenTemplate, promiseRet, stringIn, stringRet, uint32tIn, uint32tRet } from "../template/func_template"; import { replaceAll } from "../common/tool"; -import { cppout, dts2cpp_cppdir } from "../template/dtscpp/dtscppdir"; +import { cppdir } from "../template/dtscpp/dtscppdir"; import * as path from 'path'; import * as fs from 'fs'; import { h2NapiInKey, h2NapiOutKey } from "../template/dtscpp/dts2cpp_key"; @@ -237,6 +237,13 @@ const fileHandlers: { [key: string]: Function } = { // 通过类型值映射模板,比如:uint32_t返回值 -> uint32tRet -> napi_create_uint32 export function transCkey2NapiOutkey(key: string) { + // 如果是ts传递的Promise<>类型,并且transTs2C时未转换,那么就返回promiseRet + let tsPromiseReg = /Promise<([^>]+)>/g; + const tsPromiseMatch = tsPromiseReg.exec(key); + if (tsPromiseMatch) { + return promiseRet; + } + // 数组 map set iterator tuple pair 等都当作objectOut处理 for (const keyItem of h2NapiOutKey) { for (const str of keyItem.keys) { @@ -399,7 +406,7 @@ export function getCppParamGen(funcInfo: FuncObj): string { let getParamInTemplate = transCkey2NapiInkey(funcInfo.parameters[i].type); // 如果getParamInTemplate是空,则默认是对象输入,不做任何处理 if (getParamInTemplate === '') { - paramGenResult = '// Todo: handle object input'; + paramGenResult += '// Todo: handle object input.\n\n'; continue; } let getParam = replaceAll(getParamInTemplate, '[param_index_replace]', @@ -428,7 +435,7 @@ export function genCppReturnGen(funcInfo: FuncObj): string { if (funcInfo.returns === 'void') { return ' return NULL;\n'; } - let returnName = funcInfo.name + 'Out'; + let returnName = funcInfo.name; let funcReturnReplace = replaceAll(napiFuncRetTemplate, '[return_name]', returnName); let retGenResult = transCkey2NapiOutkey(funcInfo.returns); @@ -472,7 +479,7 @@ export function genHCppFile(rootInfo: GenInfo, out: string) { if (out === undefined || out === null || out.trim() === '') { out = path.dirname(rootInfo.rawFilePath); } - genDir(dts2cpp_cppdir, rootInfo, out); + genDir(cppdir, rootInfo, out); } diff --git a/src/vscode_plugin/src/gen/gendts.ts b/src/vscode_plugin/src/gen/gendts.ts index 618bb545ec29cdd52819161346cb35aa086b0c97..d623f80567ac243bec5993ac48dc81adbd9d8358 100644 --- a/src/vscode_plugin/src/gen/gendts.ts +++ b/src/vscode_plugin/src/gen/gendts.ts @@ -22,7 +22,7 @@ import { generateRandomInteger, removeComments, removeTab, replaceAll } from '.. import util = require('util'); import re = require('../common/re'); import { dtsFuncTemplate } from '../template/func_template'; -const DTS = '.d.ts'; +const dtsFileExt = '.d.ts'; export function genTsFunction(func: FuncInfo, rawFileName: string) { let funcParams = ''; @@ -58,7 +58,8 @@ export function getInterFuncParams(str: string, paramObj: ParamObj[]) { let paramObject: ParamObj = { name: '', type: '', - arraySize: 0 + arraySize: 0, + arraySizeList: [] } let paramArr = replaceAll(str, '*', '').split(','); for (let i = 0; i < paramArr.length; i++) { @@ -102,13 +103,15 @@ export function createParam(parseParamInfo: ParamObj) { let tsParam: ParamObj = { name: '', type: '', - arraySize: 0 + arraySize: 0, + arraySizeList: [] }; let cppParam: ParamObj = { name: '', type: '', - arraySize: 0 + arraySize: 0, + arraySizeList: [] }; tsParam.name = replaceAll(parseParamInfo.name, '*', ''); cppParam.name = tsParam.name; @@ -250,7 +253,9 @@ export function genDtsInterface(path: string, typeList: TypeList[], interfaceLis let paramObj: ParamObj = { name: variableName, type: replaceAll(variabletype, 'struct', '').trim(), - arraySize: 0 + arraySize: 0, + arraySizeList: [], + } paramsContent.push(paramObj); } @@ -397,7 +402,7 @@ export function transTskey2Ckey(key: string): string { if (matchDate) { return 'Date'; } - for(const keyItem of dts2cpp_key) { + for(const keyItem of cpp2DtsKey) { for(const str of keyItem.keys) { if (key.includes(str)) { return keyItem.value; @@ -540,7 +545,7 @@ export function genDtsFile(rootInfo: GenInfo, out: string) { // gen union fileContent += getDtsUnions(rootInfo); - let dtsFileName = rootInfo.fileName + DTS; + let dtsFileName = rootInfo.fileName + dtsFileExt; let outPath = '' if (out === undefined || out === null || out.trim() === '') { let dirPath = path.dirname(rootInfo.rawFilePath); diff --git a/src/vscode_plugin/src/gen/gendtscpp.ts b/src/vscode_plugin/src/gen/gendtscpp.ts index a642f6f18d29211d9a0059a1e57f5915f3268564..4df3462235ff67d8af2984ebde6ac4c629b27336 100644 --- a/src/vscode_plugin/src/gen/gendtscpp.ts +++ b/src/vscode_plugin/src/gen/gendtscpp.ts @@ -13,7 +13,9 @@ * limitations under the License. */ -import { DirTemp, DtscppRootInfo, FuncInfo, InterfaceList, TypeList, ParamObj, ParseObj, ClassObj, FuncObj, GenInfo } from "./datatype"; +import { DirTemp, DtscppRootInfo, FuncInfo, InterfaceList, TypeList, +ParamObj, ParseObj, ClassObj, FuncObj, GenInfo, +UnionObj, StructObj, TypeObj } from "./datatype"; import { replaceAll } from "../common/tool"; import fs = require('fs'); import path = require("path"); @@ -24,6 +26,7 @@ import { generateDirectFunction, genHCppFile } from "./gencpp"; import { genAbilitytestFile, generateFuncTestCase } from "./gentest"; import { Logger } from "../common/log"; import { tsTransferType } from "../template/functypemap_template"; +import { dts2CppKey } from "../template/dtscpp/dts2cpp_key"; interface GenResult { // dts文件中的内容 @@ -186,7 +189,8 @@ export function getInterfaces(parseObj: ParseObj) { variables.map(variable => ({ name: variable.name, type: getCTypeFromJS(variable.type), - arraySize: variable.arraySize + arraySize: variable.arraySize, + arraySizeList: [] })); const getFunctions = (functions: FuncObj[]) => @@ -253,7 +257,8 @@ export function createFuncParam(params: ParamObj) { let cppParam: ParamObj = { name: '', type: '', - arraySize: 0 + arraySize: 0, + arraySizeList: [], }; cppParam.name = params.name; cppParam.type = getCTypeFromJS(params.type); @@ -267,23 +272,184 @@ export function createDir(path: string) { } } export function genDtscppFromH(rootInfo: GenInfo) { - // 生成dts文件: 这里将文件生成在 cpp/types目录下,该路径是ndk工程中的dts文件的默 - // 认路径 - let outDir = path.dirname(rootInfo.rawFilePath); - let dtsOutPath = path.join(outDir, 'cpp'); - createDir(dtsOutPath); - dtsOutPath = path.join(dtsOutPath, 'types'); + let rootDir = path.dirname(rootInfo.rawFilePath); + let cppOutPath = path.join(rootDir, 'cpp'); + createDir(cppOutPath); + let dtsOutPath = path.join(cppOutPath, 'types'); createDir(dtsOutPath); + // 生成dts文件: 这里将文件生成在cpp/types目录下,该路径是ndk工程中的dts文件的默 + // 认路径 genDtsFile(rootInfo, dtsOutPath); - // 生成.cpp和.h文件 - genHCppFile(rootInfo, outDir); - // 生成Ability.test.ets文件:这里将文件生成在 test/ets目录下,该路径是ndk工程中 - // 的test文件的默认路径 - let testOutPath = path.join(outDir, 'test'); + // 生成.cpp和.h文件:这里将文件生成在cpp目录下,该路径是ndk工程中的cpp文件的默 + // 认路径 + genHCppFile(rootInfo, cppOutPath); + let testOutPath = path.join(rootDir, 'test'); createDir(testOutPath); testOutPath = path.join(testOutPath, 'ets'); createDir(testOutPath); + // 生成Ability.test.ets文件:这里将文件生成在test/ets目录下,该路径是ndk工程中 + // 的test文件的默认路径 genAbilitytestFile(rootInfo, testOutPath); - console.log('h2dtscpp success!') + Logger.getInstance().info('generate success!') } -// -------------------dts2cpp------------------------todo + +// -------------------dts2cpp------------------------ +// 将dts类型转换为c++类型 +export function transCkey2Dtskey(key: string): string { + // 箭头函数类型: (a:number,b:string)=>void -> std::function + const arrowFuncReg = /\(([\w\:\<\>\,\s*]*)\)\s*=>([\w\s\:<\>\,\s*]+)/; + const arrowFuncMatch = key.match(arrowFuncReg); + if (arrowFuncMatch) { + const paramsStr = arrowFuncMatch[1] ? arrowFuncMatch[1].trim() : ''; + let paramreg = /([\w\s\:\*]+<[^>]*>|[\*\w\s\:]+)/g; + let pmatch; + let paramList = []; + while ((pmatch = paramreg.exec(paramsStr)) !== null) { + paramList.push(pmatch[0]); + } + let str = ''; + for (let i = 0; i < paramList.length; ++i) { + const [paramName, paramType] = paramList[i].split(':').map((item) => item.trim()); + str += paramType === '' ? '' : transCkey2Dtskey(paramType); + if (i != paramList.length - 1) { + str += ', '; + } + } + return `std::function<${transCkey2Dtskey(arrowFuncMatch[2].trim())}(${str})>`; + } + + // Callback -> std::function + const callbackReg = /Callback\s*<([^>]+)>/g; + const callbackMatch = callbackReg.exec(key); + if (callbackMatch) { + return `std::function`; + } + + // 基本类型:number/string/boolean/Map/Set/Array + for (const keyItem of dts2CppKey) { + for (const str of keyItem.keys) { + if (key.trim().replace(' ', '') === str) { + return keyItem.value; + } + } + } + // 如果是object类型,直接返回类型名如:a: AType -> AType + return key; +} + +export function transParseObj(parseObj: ParseObj) { + // trans enums + let enums = parseObj.enums; + // trans unions + let unions: UnionObj[] = []; + for (let union of parseObj.unions) { + unions.push({ + name: union.name, + alias: union.alias, + members: transParameters(union.members), + }); + } + // trans structs + let structs: StructObj[] = []; + for (let struct of parseObj.structs) { + structs.push({ + name: struct.name, + alias: struct.alias, + members: transParameters(struct.members), + functions: transFunctions(struct.functions) + }); + } + // trans classes + let classes: ClassObj[] = []; + for (let classObj of parseObj.classes) { + classes.push({ + name: classObj.name, + alias: classObj.alias, + variableList: transParameters(classObj.variableList), + functionList: transFunctions(classObj.functionList) + }); + } + // trans funcs + let funcs: FuncObj[] = transFunctions(parseObj.funcs); + + // trans types : 首先判断types是否存在 + let types: TypeObj[] = []; + if (parseObj.types && parseObj.types.length > 0) { + for (let type of parseObj.types) { + let memberType: string[] = type.types; // 这个types是啥?里面的成员是不是需要转换啊 + for (let member of memberType) { + let cType = transCkey2Dtskey(member); + memberType.push(cType); + } + types.push({ + name: type.name, + alias: type.alias, + members: transParameters(type.members), + types: memberType, // for test. 这里面是啥还不知道 + functions: transFunctions(type.functions) + }); + } + } + + let transParseObj: ParseObj = { + enums: enums, + unions: unions, + structs: structs, + classes: classes, + funcs: funcs, + types: types + }; + + return transParseObj; +} + +// 将FuncObj[]中的ts type全转换成cpp type +function transFunctions(tranFuncs: FuncObj[]) { + let funcs: FuncObj[] = []; + for (let func of tranFuncs) { + funcs.push({ + name: func.name, + type: func.type, + returns: transCkey2Dtskey(func.returns), + parameters: transParameters(func.parameters), + }); + } + return funcs; +} + +// 将ParamObj[]中的ts type全转换成cpp type +export function transParameters(transMembers: ParamObj[]) { + let members: ParamObj[] = []; + for (let member of transMembers) { + members.push({ + type: transCkey2Dtskey(member.type), + name: member.name, + arraySize: member.arraySize, + arraySizeList: member.arraySizeList + }); + } + return members; +} + +export function genCppFromDts(rootInfo: GenInfo) { + // 要将rootInfo中的type信息转换为c++类型,然后写入cpp文件中 + let hRootInfo: GenInfo = { + parseObj: transParseObj(rootInfo.parseObj), + rawFilePath: rootInfo.rawFilePath, + fileName: rootInfo.fileName + } + // 生成napi框架(.h文件和.cpp文件):这里将文件生成在cpp目录下,该路径是ndk工程中 + // 的cpp文件的默认路径 + let rootDir = path.dirname(hRootInfo.rawFilePath); + let cppOutPath = path.join(rootDir, 'cpp'); + createDir(cppOutPath); + genHCppFile(hRootInfo, cppOutPath); + // 生成Ability.test.ets文件: 这里将文件生成在test/ets目录下,该路径是ndk工程中 + // 的test文件的默认路径 + let testOutPath = path.join(rootDir, 'test'); + createDir(testOutPath); + testOutPath = path.join(testOutPath, 'ets'); + createDir(testOutPath); + genAbilitytestFile(hRootInfo, testOutPath); +} + diff --git a/src/vscode_plugin/src/gen/gentest.ts b/src/vscode_plugin/src/gen/gentest.ts index 075bbf058fdf0530329d573a38bd167bd1debe4b..e4e38d43843f356b76a6198aad8c5634773ce671 100644 --- a/src/vscode_plugin/src/gen/gentest.ts +++ b/src/vscode_plugin/src/gen/gentest.ts @@ -36,7 +36,7 @@ export function generateFuncTestCase(funcInfo: FuncInfo, rawFileName: string, t // 调用函数 Logger.getInstance().info("test funcInfo:" + JSON.stringify(funcInfo)); if (getJsType(funcInfo.retType) !== 'void') { - callFunc = util.format('let result: %s = testNapi.%s(%s)\n ', getJsType(funcInfo.retType), funcInfo.genName, funcParamUse); + callFunc = util.format('let result: %s = testNapi.%s(%s)\n ', getJsType(funcInfo.retType), funcInfo.name, funcParamUse); // 加 hilog 打印 hilogContent = util.format('hilog.info(0x0000, "testTag", "Test NAPI %s: ", JSON.stringify(result));\n ', funcInfo.name); hilogContent += util.format('Logger.getInstance().info("testTag", "Test NAPI %s: ", JSON.stringify(result));\n ', funcInfo.name); @@ -191,7 +191,7 @@ export function getTestType(type: string) { export function getJsType(type: string) { type = replaceAll(type,'const', ''); type = replaceAll(type, '*', '').trim(); - for(const keyItem of dts2cpp_key) { + for(const keyItem of cpp2DtsKey) { for(const str of keyItem.keys) { if (type.includes(str)) { return transTskey2Ckey(type); @@ -228,7 +228,7 @@ export function genAbilitytestFile(rootInfo: GenInfo, out: string) { // 参数定义并初始化 const param = funcInfo.parameters[i]; let paramType = transTskey2Ckey(param.type); - let testValue = '\'Please give an any value.\''; // any类型咋赋值? + let testValue = 'undefined; // Please give an any value.'; // any类型咋赋值? dts2TestValue.forEach(item => { if (item.key === paramType) { testValue = item.value; @@ -242,8 +242,10 @@ export function genAbilitytestFile(rootInfo: GenInfo, out: string) { funcParamUse = funcParamUse.slice(0, -2); // 去掉最后一个逗号和空格 } } - // 返回值 - let returnType = transTskey2Ckey(funcInfo.returns); + // 返回值,如果本来就是ts类型就不用替换了:如Promise,就不用替换了 + let tsPromiseReg = /Promise<([^>]+)>/g; + let returnType = tsPromiseReg.exec(funcInfo.returns)? funcInfo.returns: + transTskey2Ckey(funcInfo.returns); if (returnType === 'void') { callFunc = util.format('testNapi.%s(%s)\n ', funcInfo.name, funcParamUse); diff --git a/src/vscode_plugin/src/model/dts2cppmod.ts b/src/vscode_plugin/src/model/dts2cppmod.ts index c2ba0df12e453b87a712a7d3e56cc648470a727c..5fb1878514e375aa5b4e4cf30c5ec35acdbea69e 100644 --- a/src/vscode_plugin/src/model/dts2cppmod.ts +++ b/src/vscode_plugin/src/model/dts2cppmod.ts @@ -27,7 +27,7 @@ import { EVENT_WARNING } from '../common/eventtype'; import { parseTsFile } from '../parse/parsets'; -import { genCppFile } from '../gen/gendtscpp'; +import { genCppFile, genCppFromDts } from '../gen/gendtscpp'; import { Logger } from '../common/log'; export class Dts2cppMod extends IModel { name: string; @@ -62,7 +62,13 @@ export class Dts2cppMod extends IModel { this.emmitEventForKey(EVENT_PROGRESS, 50, PARSE_COMPLETE); // generator let out = path.dirname(this.uri.fsPath); - genCppFile(res, this.uri.fsPath, out); + // genCppFile(res, this.uri.fsPath, out); + let rootInfo: GenInfo = { + parseObj: res, + rawFilePath: this.uri.fsPath, // e://xxx.d.ts + fileName: path.basename(this.uri.fsPath, '.d.ts') // xxx + }; + genCppFromDts(rootInfo); // progress.report({ increment: 100, message: GEN_COMPLETE + out }); this.emmitEventForKey(EVENT_PROGRESS, 100, PARSE_COMPLETE + out); diff --git a/src/vscode_plugin/src/template/dtscpp/dts2cpp_key.ts b/src/vscode_plugin/src/template/dtscpp/dts2cpp_key.ts index 677c9e880371dd6fbb9c6dacc316dcf97c16ab7b..d16d841cc8f1cdd809e30252d27c5539b58253fd 100644 --- a/src/vscode_plugin/src/template/dtscpp/dts2cpp_key.ts +++ b/src/vscode_plugin/src/template/dtscpp/dts2cpp_key.ts @@ -91,6 +91,76 @@ export let h2NapiInKey = [ } ] +export let dts2CppKey = [ + { + keys: ['number'], + value: 'double' + }, + { + keys: ['string'], + value: 'std::string' + }, + { + keys: ['boolean'], + value: 'bool' + }, + { + keys: ['void'], + value: 'void' + }, + { + keys: ['Array', 'number[]'], + value: 'std::vector' + }, + { + keys: ['Array', 'string[]'], + value: 'std::vector' + }, + { + keys: ['Array', 'boolean[]'], + value: 'std::vector' + }, + { + keys: ['Map'], + value: 'std::map' + }, + { + keys: ['Map'], + value: 'std::map' + }, + { + keys: ['Map'], + value: 'std::map' + }, + { + keys: ['Map'], + value: 'std::map' + }, + { + keys: ['Map'], + value: 'std::map' + }, + { + keys: ['Map'], + value: 'std::map' + }, + { + keys: ['Set'], + value: 'std::set' + }, + { + keys: ['Set'], + value: 'std::set' + }, + { + keys: ['Set'], + value: 'std::set' + }, + { + keys: ['any', 'object'], + value: 'std::any' + } +] export let h2NapiOutKey = [ { diff --git a/src/vscode_plugin/src/template/dtscpp/dtscppdir.ts b/src/vscode_plugin/src/template/dtscpp/dtscppdir.ts index 2214228460785af3b069842f52c0bb0b61e743df..329ea4454eba03a61f90203237c2e6e4c3ee9173 100644 --- a/src/vscode_plugin/src/template/dtscpp/dtscppdir.ts +++ b/src/vscode_plugin/src/template/dtscpp/dtscppdir.ts @@ -64,4 +64,12 @@ export let cppout: DirTemp = { name: 'testdts2cpp', files: [], dirs: [dts2cpp_cppdir, dtscpp_etsdir] +} + +// --------------- +export let cppdir: DirTemp = { + name: '', + files: [napiCommonHTemplate, napiCommonCppTemplate, napiHTemplate, + napiInitTemplate, napiCppTemplate, dts2hTemplate], + dirs: [] } \ No newline at end of file diff --git a/src/vscode_plugin/src/template/func_template.ts b/src/vscode_plugin/src/template/func_template.ts index be082ac13981abe0c2ebff6506c01409ddcd9c30..b2c5c6934fe635c25613779e0b6ee9757df32390 100644 --- a/src/vscode_plugin/src/template/func_template.ts +++ b/src/vscode_plugin/src/template/func_template.ts @@ -239,6 +239,23 @@ if (status != napi_ok) { } `; +export let promiseRet = `napi_value [return_name_replace]Out; +napi_deferred [return_name_replace]DeferedOut; +/* [NAPI_GEN]: 创建一个空的promise对象 + * env: N-API环境的句柄,表示当前的上下文 + * promise: 用于接收创建的promise对象的指针 + * deferred: 用于接收deferred对象的指针 +*/ +status = napi_create_promise(env, &[return_name_replace]Out, &[return_name_replace]DeferedOut); +if (status != napi_ok) { + /* [NAPI_GEN]: 错误处理*/ + getErrMessage(status, env, extended_error_info, "napi_create_promise", tag); + return nullptr; +} +// Todo: create async work here. +`; + + // napi func的paramGen template export let paramGenTemplate = `napi_valuetype valuetype[param_name_replace]; /* [NAPI_GEN]: 获取入参类型,第[param_index_replace]个入参 @@ -395,18 +412,19 @@ if (status != napi_ok) { `; export let callbackIn = ` - napi_value argv; - // Todo:创建要传递给回调函数的参数 - napi_value result; - /** - * env: napi_env 类型的环境变量 - * this_arg: 指向 JavaScript 对象的指针,表示回调函数中的 this 值。如果不需要传递 this 值,则可以设置为 nullptr。 - * func: 指向 JavaScript 回调函数的指针。 - * argc: 传递给回调函数的参数数量。 - * argv: 一个 napi_value 类型的数组,用于存储要传递给回调函数的参数。 - * result: 指向返回值的指针。如果回调函数没有返回值,则可以设置为 nullptr。 - */ - napi_call_function(env, nullptr, args[[param_index_replace]], 1, &argv, &result); +napi_value argv; +// Todo: 创建要传递给回调函数的参数 +napi_value result; +/** +* env: napi_env 类型的环境变量 +* this_arg: 指向 JavaScript 对象的指针,表示回调函数中的 this 值。如果不需要递 this 值,则可以设置为 nullptr。 +* func: 指向 JavaScript 回调函数的指针。 +* argc: 传递给回调函数的参数数量。示例中传递了一个参数。 +* argv: 一个 napi_value 类型的数组,用于存储要传递给回调函数的参数。示例中传递了一个参数。 + +* result: 指向返回值的指针。如果回调函数没有返回值,则可以设置为 nullptr。 +*/ +napi_call_function(env, nullptr, args[[param_index_replace]], 1, &argv, result); `; // napi testAbility需要生成的方法模板