/* * Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const { generateFunctionDirect } = require('./function_direct'); const { generateFunctionSync } = require('./function_sync'); const { generateFunctionAsync } = require('./function_async'); const { generateFunctionOnOff } = require('./function_onoff'); const { generateThreadsafeFunc } = require('./function_threadsafe'); const { FuncType, InterfaceList, getArrayType, getArrayTypeTwo, getMapType, EnumList, jsType2CType, isOnOffRegisterFunc, isCreateThreadsafeFunc } = require('../tools/common'); const { jsToC, getCType, paramGenerate } = require('./param_generate'); const { cToJs } = require('./return_generate'); const re = require('../tools/re'); const { NapiLog } = require('../tools/NapiLog'); const { addUniqFunc2List, addUniqObj2List, setOverrideFunc } = require('../tools/tool'); let middleHTmplete = ` class [className]_middle { public: struct constructor_value_struct {[contructorValueIn] }; static napi_value constructor(napi_env env, napi_callback_info info); static void release(DataPtr p); [static_funcs] }; `; let middleBodyTmplete = ` napi_value [className]_middle::constructor(napi_env env, napi_callback_info info) { XNapiTool *pxt = new XNapiTool(env, info); napi_status status; size_t argc; status = napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr); if (argc > 0) { napi_value args[argc]; status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); if (status != napi_ok) { return nullptr; } struct constructor_value_struct *vio = new constructor_value_struct(); [getConstructorParam] [className] *p = new [className]([constructorParam]); napi_value thisvar = pxt->WrapInstance(reinterpret_cast(p), [className]_middle::release); delete vio; return thisvar; } else { [className] *p = new [className](); napi_value thisvar = pxt->WrapInstance(reinterpret_cast(p), [className]_middle::release); return thisvar; } } void [className]_middle::release(DataPtr p) { void *dataPtr = p; [className] *p2 = static_cast<[className] *>(dataPtr); delete p2; } [static_funcs] `; function getHDefineOfVariable(name, type, variable, optional) { if (type.indexOf('|') >= 0) { unionTypeString(name, type, variable, optional); } else if (type === 'string') { variableTypeString(optional, variable, name); } else if (InterfaceList.getValue(type)) { variableTypeInterface(optional, variable, type, name); } else if (EnumList.getValue(type)) { // 如果是枚举string类型,需要将其转换为std::string类型 variableTypeEnum(type, variable, name); } else if (type.indexOf('Array<') === 0) { typeArrFunctionOne(type, variable, name, optional); } else if (type === 'boolean') { variableTypeBoolean(optional, variable, name); } else if (type.substring(type.length - 2) === '[]') { typeArrFunctionTwo(type, variable, name, optional); } else if (type.substring(0, 4) === 'Map<' || type.indexOf('{[key:') === 0) { variable.hDefine += mapTypeString(type, name, optional); // 支持可选参数? } else if (type === 'any') { variable.hDefine += anyTypeString(type, name); } else if (type.substring(0, 12) === 'NUMBER_TYPE_') { variableTypeNumber(optional, variable, type, name); } else if (type === 'Object' || type === 'object') { variable.hDefine += '\n std::map %s;'.format(name); } else { NapiLog.logError(` ---- generateVariable fail %s,%s ---- `.format(name, type)); } } function variableTypeNumber(optional, variable, type, name) { if (optional) { variable.hDefine += '\n std::optional<%s> %s;'.format(type, name); } else { variable.hDefine += '\n %s %s;'.format(type, name); } } function variableTypeBoolean(optional, variable, name) { if (optional) { variable.hDefine += '\n std::optional %s;'.format(name); } else { variable.hDefine += '\n bool %s;'.format(name); } } function variableTypeEnum(type, variable, name) { let enumBasicType = EnumList.getValue(type)[0].type; if (enumBasicType === 'string') { variable.hDefine += '\n %s %s;'.format('std::string', name); } else { variable.hDefine += '\n %s %s;'.format(type, name); } } function variableTypeInterface(optional, variable, type, name) { if (optional) { variable.hDefine += '\n std::optional<%s> %s;'.format(type, name); } else { variable.hDefine += '\n %s %s;'.format(type, name); } } function variableTypeString(optional, variable, name) { if (optional) { variable.hDefine += '\n std::optional %s;'.format(name); } else { variable.hDefine += '\n std::string %s;'.format(name); } } function typeArrFunctionTwo(type, variable, name, optional) { let arrayType = getArrayTypeTwo(type); if (arrayType === 'any') { variable.hDefine += '\n std::string %s_type;\n std::any %s;'.format(name, name); } else { let cType = jsType2CType(arrayType); if (optional) { variable.hDefine += '\n std::optional> %s;'.format(cType, name); } else { variable.hDefine += '\n std::vector<%s> %s;'.format(cType, name); } } } function typeArrFunctionOne(type, variable, name, optional) { let arrayType = getArrayType(type); if (arrayType === 'any') { variable.hDefine += '\n std::string %s_type; \n std::any %s;'.format(name, name); } else { let cType = jsType2CType(arrayType); if (optional) { variable.hDefine += '\n std::optional> %s;'.format(cType, name); } else { variable.hDefine += '\n std::vector<%s> %s;'.format(cType, name); } } } function generateVariable(value, variable, className) { let name = value.name; let type = value.type; let optional = value.optional; if (!value.isParentMember) { // 只有类/接口自己的成员属性需要在.h中定义, 父类/父接口不需要 getHDefineOfVariable(name, type, variable, optional); } if (optional && type.indexOf('|') < 0) { optionalParamGetSet(type, variable, name, className); } else { variable.middleH += ` static napi_value getvalue_%s(napi_env env, napi_callback_info info); static napi_value setvalue_%s(napi_env env, napi_callback_info info);\n`.format(name, name); variable.middleValue += ` napi_value %s::getvalue_%s(napi_env env, napi_callback_info info) { XNapiTool *pxt = std::make_unique(env, info).release(); void *instPtr = pxt->UnWarpInstance(); %s *p = static_cast<%s *>(instPtr); napi_value result = nullptr; `.format(className + '_middle', name, className, className) + cToJs('p->' + name, type, 'result', 1, optional) + ` delete pxt; return result; } napi_value %s::setvalue_%s(napi_env env, napi_callback_info info) { std::shared_ptr pxt = std::make_shared(env, info); void *instPtr = pxt->UnWarpInstance(); %s *p = static_cast<%s *>(instPtr); `.format(className + '_middle', name, className, className) + jsToC('p->' + name, 'pxt->GetArgv(XNapiTool::ZERO)', type, 0, optional) + ` return nullptr; }`; } } function optionalParamGetSet(type, variable, name, className) { let optType = getCType(type); variable.middleH += ` static napi_value getvalue_%s(napi_env env, napi_callback_info info); static napi_value setvalue_%s(napi_env env, napi_callback_info info);\n`.format(name, name); variable.middleValue += ` napi_value %s::getvalue_%s(napi_env env, napi_callback_info info) { XNapiTool *pxt = std::make_unique(env, info).release(); void *instPtr = pxt->UnWarpInstance(); %s *p = static_cast<%s *>(instPtr); napi_value result = nullptr; if(p->%s.has_value()) { `.format(className + '_middle', name, className, className, name) + cToJs('p->%s.value()'.format(name), type, 'result') + ` } delete pxt; return result; } napi_value %s::setvalue_%s(napi_env env, napi_callback_info info) { std::shared_ptr pxt = std::make_shared(env, info); void *instPtr = pxt->UnWarpInstance(); %s *p = static_cast<%s *>(instPtr); if (pxt->GetProperty(pxt->GetArgv(XNapiTool::ZERO), "%s")) { %s %s_tmp; `.format(className + '_middle', name, className, className, name, optType, name) + jsToC('%s_tmp'.format(name), 'pxt->GetArgv(XNapiTool::ZERO)', type) + ` p->%s.emplace(%s_tmp);`.format(name, name) + ` } return nullptr; }`; } function unionTypeString(name, type, variable, optional) { if (optional) { variable.hDefine += `\n std::optional %s_type; std::optional %s;`.format(name, name); } else { variable.hDefine += `\n std::string %s_type; std::any %s;`.format(name, name); } } function mapTypeString(type, name, optional) { let mapType = getMapType(type); let mapTypeString; if (mapType[1] !== undefined && mapType[1] !== null && mapType[2] === undefined) { if (mapType[1] === 'string') { mapTypeString = 'std::string, std::string'; } else if (mapType[1] === 'boolean') { mapTypeString = 'std::string, bool'; } else if (mapType[1].substring(0, 12) === 'NUMBER_TYPE_') { mapTypeString = 'std::string, %s'.format(mapType[1]); } else if (mapType[1].substring(0, 12) === 'any') { mapTypeString = `std::string, std::any`.format(mapType[1]); return `\n std::map<%s> %s; std::string %s_type;`.format(mapTypeString, name, name); } else if (InterfaceList.getValue(mapType[1])) { mapTypeString = 'std::string, %s'.format(mapType[1]); } } if (mapType[2] !== undefined) { if (mapType[2] === 'string') { mapTypeString = 'std::string, std::map'; } else if (mapType[2] === 'boolean') { mapTypeString = 'std::string, std::map'; } else if (mapType[2].substring(0, 12) === 'NUMBER_TYPE_') { mapTypeString = 'std::string, std::map'.format(mapType[2]); } } if (mapType[3] !== undefined) { if (mapType[3] === 'string') { mapTypeString = 'std::string, std::vector'; } else if (mapType[3] === 'boolean') { mapTypeString = 'std::string, std::vector'; } else if (mapType[3].substring(0, 12) === 'NUMBER_TYPE_') { mapTypeString = 'std::string, std::vector<%s>'.format(mapType[3]); } } if (optional) { return '\n std::optional> %s;'.format(mapTypeString, name); } else { return '\n std::map<%s> %s;'.format(mapTypeString, name); } } function anyTypeString(type, name) { let anyType = `\n std::string %s_type; std::any %s;`; return anyType.format(name, name); } function generateInterface(name, data, inNamespace) { let param = { valueIn: '', valueOut: '', valueCheckout: '', valueFill: '', valuePackage: '', valueDefine: '', optionalParamDestory: '' }; let getConParam = getConstructorFunc(data, param); let resultConnect = connectResult(data, inNamespace, name); let middleFunc = resultConnect[0]; let implH = resultConnect[1]; let implCpp = resultConnect[2]; let middleInit = resultConnect[3]; let middleH = resultConnect[4]; let selfNs = ''; if (inNamespace.length > 0) { let nsl = inNamespace.split('::'); nsl.pop(); if (nsl.length >= 2) { selfNs = ', ' + nsl[nsl.length - 1]; } } let toolNamespace = getToolNamespace(inNamespace); middleInit += `\n pxt->DefineClass("%s", %s%s%s_middle::constructor, valueList, funcList%s);\n}\n` .format(name, inNamespace, toolNamespace, name, selfNs); let extendsStr = (data.parentNameList && data.parentNameList.length > 0) ? ' : public %s'.format(data.parentNameList.join(', public ')) : ''; let result = { implH: ` class %s%s { public:%s\n };\n`.format(name, extendsStr, implH), implCpp: implCpp, middleBody: middleBodyTmplete.replaceAll('[className]', name).replaceAll('[static_funcs]', middleFunc) .replaceAll('[getConstructorParam]', getConParam) .replaceAll('[constructorParam]', param.valueFill), middleInit: middleInit, declarationH: ` class %s;\r`.format(name), middleH: middleHTmplete.replaceAll('[className]', name).replaceAll('[static_funcs]', middleH) .replaceAll('[contructorValueIn]', param.valueIn) }; return result; } function getConstructorFunc(data, param) { let funcValues = null; if (data.function !== null && data.function !== undefined) { for (let i = 0; i < data.function.length; i++) { if (data.function[i].name === 'constructor') { funcValues = data.function[i].value; } } } for (let j in funcValues) { paramGenerate(j, funcValues[j], param, data); } let tmpBody = param.valueCheckout.split(';\n'); let getConParam = ''; for (let i in tmpBody) { let flag = tmpBody[i].replaceAll('\n', '').replaceAll(' ', ''); if (flag !== '') { let indexBegin = tmpBody[i].indexOf('pxt->GetArgv('); if (indexBegin > 0 && tmpBody[i].indexOf('\n') < 0) { tmpBody[i] = tmpBody[i].replaceAll('pxt->GetArgv(', 'args['); let index = tmpBody[i].indexOf(')'); tmpBody[i] = tmpBody[i].substring(0, index) + ']' + tmpBody[i].substring(index + 1, tmpBody[i].length); } else if (indexBegin > 0 && tmpBody[i].indexOf('\n') >= 0) { tmpBody[i] = tmpBody[i].replaceAll('pxt->GetArgv(', 'args['); let index = tmpBody[i].indexOf('),'); tmpBody[i] = tmpBody[i].substring(0, index) + ']' + tmpBody[i].substring(index + 1, tmpBody[i].length); } getConParam += tmpBody[i] + ';\n'; } } let index = getConParam.lastIndexOf(';\n'); if (getConParam.substring(index - 1, index) === ' ') { getConParam = getConParam.substring(0, index - 1); } return getConParam; } // 递归获取接口及接口父类的所有成员属性和方法 function getAllPropties(interfaceBody, properties, isParentClass) { for (let i in interfaceBody.value) { interfaceBody.value[i].isParentMember = isParentClass; addUniqObj2List(interfaceBody.value[i], properties.values); } for (let i in interfaceBody.function) { interfaceBody.function[i].isParentMember = isParentClass; if (!addUniqFunc2List(interfaceBody.function[i], properties.functions)) { if (isParentClass) { // 没添加到列表,说明子类方法properties.functions重写了父类方法interfaceBody.function[i] // 找到该子类方法并将其设置为override (生成的重写函数如果没有override关键字会触发门禁告警) setOverrideFunc(interfaceBody.function[i], properties.functions); } } } if (!isParentClass && interfaceBody.parentNameList && interfaceBody.parentNameList.length > 0) { getAllPropties(interfaceBody.parentBody, properties, true); } } function addVirtualKeywords(data, implH, name) { if (data.childList && data.childList.length > 0) { // 如果该类是其它类的父类,增加虚析构函数使其具备泛型特征 (基类必须有虚函数才能正确使用dynamic_cast和typeinfo等方法) // 如果该类自己也有父类,虚析构函数需要加上override关键字(否则C++门禁会有告警) let ovrrideStr = (data.parentList && data.parentList.length > 0) ? ' override' : ''; // 如果虚析构函数已经有override关键字,就不需要再加virtual关键字了(否则C++门禁会有告警) let virtualStr = (data.parentList && data.parentList.length > 0) ? '' : 'virtual '; implH += '\n %s~%s()%s {};'.format(virtualStr, name, ovrrideStr); } return implH; } function connectResult(data, inNamespace, name) { let implH = ''; let implCpp = ''; let middleFunc = ''; let middleInit = ''; let middleH = ''; let variable = { hDefine: '', middleValue: '', middleH: '' }; middleInit = getMiddleInitFunc(middleInit, data, variable, name, inNamespace); implH += variable.hDefine; middleFunc += variable.middleValue; middleInit += `\n std::map funcList;`; middleH += variable.middleH; for (let i in data.allProperties.functions) { let func = data.allProperties.functions[i]; let tmp; if (isOnOffRegisterFunc(func.name)) { tmp = generateFunctionOnOff(func, data, name); } else if (isCreateThreadsafeFunc(func.name)) { tmp = generateThreadsafeFunc(func, data, name); } if (!tmp) { switch (func.type) { case FuncType.DIRECT: tmp = generateFunctionDirect(func, data, name, implH); break; case FuncType.SYNC: tmp = generateFunctionSync(func, data, name); break; case FuncType.ASYNC: case FuncType.PROMISE: tmp = generateFunctionAsync(func, data, name, implH); break; default: break; } } middleFunc += tmp[0]; implH += tmp[1]; implCpp += tmp[2]; middleH += tmp[3]; middleInit = generateMiddleInitFunc(func, inNamespace, middleInit, name); } implH = addVirtualKeywords(data, implH, name); return [middleFunc, implH, implCpp, middleInit, middleH]; } function generateMiddleInitFunc(func, inNamespace, middleInit, name) { if (func.name !== 'constructor') { let toolNamespace = getToolNamespace(inNamespace); middleInit += `\n funcList["%s"] = %s%s%s_middle::%s_middle;`.format(func.name, inNamespace, toolNamespace, name, func.name); } return middleInit; } function getMiddleInitFunc(middleInit, data, variable, name, inNamespace) { middleInit = `{\n std::map> valueList;`; data.allProperties = { values: [], functions: [] }; getAllPropties(data, data.allProperties, false); let toolNamespace = getToolNamespace(inNamespace); for (let i in data.allProperties.values) { let v = data.allProperties.values[i]; generateVariable(v, variable, name); middleInit += ` valueList["%s"]["getvalue"] = %s%s%s_middle::getvalue_%s; valueList["%s"]["setvalue"] = %s%s%s_middle::setvalue_%s;` .format(v.name, inNamespace, toolNamespace, name, v.name, v.name, inNamespace, toolNamespace, name, v.name); } return middleInit; } function getToolNamespace(inNamespace) { let index = inNamespace.lastIndexOf('::'); let toolNamespace; if (index > 0) { let bodyTmp = inNamespace.substring(0, index); let index2 = bodyTmp.lastIndexOf('::'); if (index2 > 0 && index2 < index) { toolNamespace = inNamespace.substring(index2 + 2, index) + '_interface::'; } else { toolNamespace = bodyTmp + '_interface::'; } } else { toolNamespace = inNamespace + '_interface::'; } return toolNamespace; } module.exports = { generateInterface, connectResult, generateVariable, mapTypeString, anyTypeString, getHDefineOfVariable };