1/* 2* Copyright (c) 2022 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*/ 15const { generateFunctionDirect } = require('./function_direct'); 16const { generateFunctionSync } = require('./function_sync'); 17const { generateFunctionAsync } = require('./function_async'); 18const { generateFunctionOnOff } = require('./function_onoff'); 19const { generateThreadsafeFunc } = require('./function_threadsafe'); 20const { FuncType, InterfaceList, getArrayType, getArrayTypeTwo, getMapType, EnumList, jsType2CType, 21 isOnOffRegisterFunc, isCreateThreadsafeFunc } = require('../tools/common'); 22const { jsToC, getCType, paramGenerate } = require('./param_generate'); 23const { cToJs } = require('./return_generate'); 24const re = require('../tools/re'); 25const { NapiLog } = require('../tools/NapiLog'); 26const { addUniqFunc2List, addUniqObj2List, setOverrideFunc } = require('../tools/tool'); 27 28let middleHTmplete = ` 29class [className]_middle { 30public: 31 struct constructor_value_struct {[contructorValueIn] 32 }; 33 static napi_value constructor(napi_env env, napi_callback_info info); 34 static void release(DataPtr p); 35 [static_funcs] 36}; 37`; 38 39let middleBodyTmplete = ` 40 napi_value [className]_middle::constructor(napi_env env, napi_callback_info info) 41 { 42 XNapiTool *pxt = new XNapiTool(env, info); 43 napi_status status; 44 size_t argc; 45 status = napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr); 46 if (argc > 0) { 47 napi_value args[argc]; 48 status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 49 if (status != napi_ok) { 50 return nullptr; 51 } 52 struct constructor_value_struct *vio = new constructor_value_struct(); 53 [getConstructorParam] 54 [className] *p = new [className]([constructorParam]); 55 napi_value thisvar = pxt->WrapInstance(reinterpret_cast<DataPtr>(p), [className]_middle::release); 56 delete vio; 57 return thisvar; 58 } else { 59 [className] *p = new [className](); 60 napi_value thisvar = pxt->WrapInstance(reinterpret_cast<DataPtr>(p), [className]_middle::release); 61 return thisvar; 62 } 63 } 64 void [className]_middle::release(DataPtr p) 65 { 66 void *dataPtr = p; 67 [className] *p2 = static_cast<[className] *>(dataPtr); 68 delete p2; 69 } 70 [static_funcs] 71`; 72 73function getHDefineOfVariable(name, type, variable, optional) { 74 if (type.indexOf('|') >= 0) { 75 unionTypeString(name, type, variable, optional); 76 } else if (type === 'string') { 77 variableTypeString(optional, variable, name); 78 } else if (InterfaceList.getValue(type)) { 79 variableTypeInterface(optional, variable, type, name); 80 } else if (EnumList.getValue(type)) { 81 // 如果是枚举string类型,需要将其转换为std::string类型 82 variableTypeEnum(type, variable, name); 83 } else if (type.indexOf('Array<') === 0) { 84 typeArrFunctionOne(type, variable, name, optional); 85 } else if (type === 'boolean') { 86 variableTypeBoolean(optional, variable, name); 87 } else if (type.substring(type.length - 2) === '[]') { 88 typeArrFunctionTwo(type, variable, name, optional); 89 } else if (type.substring(0, 4) === 'Map<' || type.indexOf('{[key:') === 0) { 90 variable.hDefine += mapTypeString(type, name, optional); // 支持可选参数? 91 } else if (type === 'any') { 92 variable.hDefine += anyTypeString(type, name); 93 } else if (type.substring(0, 12) === 'NUMBER_TYPE_') { 94 variableTypeNumber(optional, variable, type, name); 95 } else if (type === 'Object' || type === 'object') { 96 variable.hDefine += '\n std::map<std::string, std::any> %s;'.format(name); 97 } 98 else { 99 NapiLog.logError(` 100 ---- generateVariable fail %s,%s ---- 101 `.format(name, type)); 102 } 103} 104 105function variableTypeNumber(optional, variable, type, name) { 106 if (optional) { 107 variable.hDefine += '\n std::optional<%s> %s;'.format(type, name); 108 } else { 109 variable.hDefine += '\n %s %s;'.format(type, name); 110 } 111} 112 113function variableTypeBoolean(optional, variable, name) { 114 if (optional) { 115 variable.hDefine += '\n std::optional<bool> %s;'.format(name); 116 } else { 117 variable.hDefine += '\n bool %s;'.format(name); 118 } 119} 120 121function variableTypeEnum(type, variable, name) { 122 let enumBasicType = EnumList.getValue(type)[0].type; 123 if (enumBasicType === 'string') { 124 variable.hDefine += '\n %s %s;'.format('std::string', name); 125 } else { 126 variable.hDefine += '\n %s %s;'.format(type, name); 127 } 128} 129 130function variableTypeInterface(optional, variable, type, name) { 131 if (optional) { 132 variable.hDefine += '\n std::optional<%s> %s;'.format(type, name); 133 } else { 134 variable.hDefine += '\n %s %s;'.format(type, name); 135 } 136} 137 138function variableTypeString(optional, variable, name) { 139 if (optional) { 140 variable.hDefine += '\n std::optional<std::string> %s;'.format(name); 141 } else { 142 variable.hDefine += '\n std::string %s;'.format(name); 143 } 144} 145 146function typeArrFunctionTwo(type, variable, name, optional) { 147 let arrayType = getArrayTypeTwo(type); 148 if (arrayType === 'any') { 149 variable.hDefine += '\n std::string %s_type;\n std::any %s;'.format(name, name); 150 } else { 151 let cType = jsType2CType(arrayType); 152 if (optional) { 153 variable.hDefine += '\n std::optional<std::vector<%s>> %s;'.format(cType, name); 154 } else { 155 variable.hDefine += '\n std::vector<%s> %s;'.format(cType, name); 156 } 157 } 158} 159 160function typeArrFunctionOne(type, variable, name, optional) { 161 let arrayType = getArrayType(type); 162 if (arrayType === 'any') { 163 variable.hDefine += '\n std::string %s_type; \n std::any %s;'.format(name, name); 164 } else { 165 let cType = jsType2CType(arrayType); 166 if (optional) { 167 variable.hDefine += '\n std::optional<std::vector<%s>> %s;'.format(cType, name); 168 } else { 169 variable.hDefine += '\n std::vector<%s> %s;'.format(cType, name); 170 } 171 } 172} 173 174function generateVariable(value, variable, className) { 175 let name = value.name; 176 let type = value.type; 177 let optional = value.optional; 178 if (!value.isParentMember) { 179 // 只有类/接口自己的成员属性需要在.h中定义, 父类/父接口不需要 180 getHDefineOfVariable(name, type, variable, optional); 181 } 182 if (optional && type.indexOf('|') < 0) { 183 optionalParamGetSet(type, variable, name, className); 184 } else { 185 variable.middleH += ` 186 static napi_value getvalue_%s(napi_env env, napi_callback_info info); 187 static napi_value setvalue_%s(napi_env env, napi_callback_info info);\n`.format(name, name); 188 variable.middleValue += ` 189 napi_value %s::getvalue_%s(napi_env env, napi_callback_info info) 190 { 191 XNapiTool *pxt = std::make_unique<XNapiTool>(env, info).release(); 192 void *instPtr = pxt->UnWarpInstance(); 193 %s *p = static_cast<%s *>(instPtr); 194 napi_value result = nullptr; 195 `.format(className + '_middle', name, className, className) + 196 cToJs('p->' + name, type, 'result', 1, optional) + ` 197 delete pxt; 198 return result; 199 } 200 napi_value %s::setvalue_%s(napi_env env, napi_callback_info info) 201 { 202 std::shared_ptr<XNapiTool> pxt = std::make_shared<XNapiTool>(env, info); 203 void *instPtr = pxt->UnWarpInstance(); 204 %s *p = static_cast<%s *>(instPtr); 205 `.format(className + '_middle', name, className, className) + 206 jsToC('p->' + name, 'pxt->GetArgv(XNapiTool::ZERO)', 207 type, 0, optional) + ` 208 return nullptr; 209 }`; 210 } 211} 212 213function optionalParamGetSet(type, variable, name, className) { 214 let optType = getCType(type); 215 variable.middleH += ` 216 static napi_value getvalue_%s(napi_env env, napi_callback_info info); 217 static napi_value setvalue_%s(napi_env env, napi_callback_info info);\n`.format(name, name); 218 variable.middleValue += ` 219 napi_value %s::getvalue_%s(napi_env env, napi_callback_info info) 220 { 221 XNapiTool *pxt = std::make_unique<XNapiTool>(env, info).release(); 222 void *instPtr = pxt->UnWarpInstance(); 223 %s *p = static_cast<%s *>(instPtr); 224 napi_value result = nullptr; 225 if(p->%s.has_value()) { 226 `.format(className + '_middle', name, className, className, name) + 227 cToJs('p->%s.value()'.format(name), type, 'result') + ` 228 } 229 delete pxt; 230 return result; 231 } 232 napi_value %s::setvalue_%s(napi_env env, napi_callback_info info) 233 { 234 std::shared_ptr<XNapiTool> pxt = std::make_shared<XNapiTool>(env, info); 235 void *instPtr = pxt->UnWarpInstance(); 236 %s *p = static_cast<%s *>(instPtr); 237 if (pxt->GetProperty(pxt->GetArgv(XNapiTool::ZERO), "%s")) { 238 %s %s_tmp; 239 `.format(className + '_middle', name, className, className, name, optType, name) + 240 jsToC('%s_tmp'.format(name), 'pxt->GetArgv(XNapiTool::ZERO)', type) + ` 241 p->%s.emplace(%s_tmp);`.format(name, name) + ` 242 } 243 return nullptr; 244 }`; 245} 246 247function unionTypeString(name, type, variable, optional) { 248 if (optional) { 249 variable.hDefine += `\n std::optional<std::string> %s_type; 250 std::optional<std::any> %s;`.format(name, name); 251 } else { 252 variable.hDefine += `\n std::string %s_type; 253 std::any %s;`.format(name, name); 254 } 255} 256 257function mapTypeString(type, name, optional) { 258 let mapType = getMapType(type); 259 let mapTypeString; 260 if (mapType[1] !== undefined && mapType[1] !== null && mapType[2] === undefined) { 261 if (mapType[1] === 'string') { 262 mapTypeString = 'std::string, std::string'; 263 } else if (mapType[1] === 'boolean') { 264 mapTypeString = 'std::string, bool'; 265 } else if (mapType[1].substring(0, 12) === 'NUMBER_TYPE_') { 266 mapTypeString = 'std::string, %s'.format(mapType[1]); 267 } else if (mapType[1].substring(0, 12) === 'any') { 268 mapTypeString = `std::string, std::any`.format(mapType[1]); 269 return `\n std::map<%s> %s; 270 std::string %s_type;`.format(mapTypeString, name, name); 271 } else if (InterfaceList.getValue(mapType[1])) { 272 mapTypeString = 'std::string, %s'.format(mapType[1]); 273 } 274 } 275 if (mapType[2] !== undefined) { 276 if (mapType[2] === 'string') { 277 mapTypeString = 'std::string, std::map<std::string, std::string>'; 278 } else if (mapType[2] === 'boolean') { 279 mapTypeString = 'std::string, std::map<std::string, bool>'; 280 } else if (mapType[2].substring(0, 12) === 'NUMBER_TYPE_') { 281 mapTypeString = 'std::string, std::map<std::string, %s>'.format(mapType[2]); 282 } 283 } 284 if (mapType[3] !== undefined) { 285 if (mapType[3] === 'string') { 286 mapTypeString = 'std::string, std::vector<std::string>'; 287 } else if (mapType[3] === 'boolean') { 288 mapTypeString = 'std::string, std::vector<bool>'; 289 } else if (mapType[3].substring(0, 12) === 'NUMBER_TYPE_') { 290 mapTypeString = 'std::string, std::vector<%s>'.format(mapType[3]); 291 } 292 } 293 if (optional) { 294 return '\n std::optional<std::map<%s>> %s;'.format(mapTypeString, name); 295 } else { 296 return '\n std::map<%s> %s;'.format(mapTypeString, name); 297 } 298} 299 300function anyTypeString(type, name) { 301 let anyType = `\n std::string %s_type; 302 std::any %s;`; 303 return anyType.format(name, name); 304} 305 306function generateInterface(name, data, inNamespace) { 307 let param = { valueIn: '', valueOut: '', valueCheckout: '', valueFill: '', 308 valuePackage: '', valueDefine: '', optionalParamDestory: '' }; 309 let getConParam = getConstructorFunc(data, param); 310 let resultConnect = connectResult(data, inNamespace, name); 311 let middleFunc = resultConnect[0]; 312 let implH = resultConnect[1]; 313 let implCpp = resultConnect[2]; 314 let middleInit = resultConnect[3]; 315 let middleH = resultConnect[4]; 316 let selfNs = ''; 317 if (inNamespace.length > 0) { 318 let nsl = inNamespace.split('::'); 319 nsl.pop(); 320 if (nsl.length >= 2) { 321 selfNs = ', ' + nsl[nsl.length - 1]; 322 } 323 } 324 let toolNamespace = getToolNamespace(inNamespace); 325 middleInit += `\n pxt->DefineClass("%s", %s%s%s_middle::constructor, 326 valueList, funcList%s);\n}\n` 327 .format(name, inNamespace, toolNamespace, name, selfNs); 328 let extendsStr = (data.parentNameList && data.parentNameList.length > 0) ? 329 ' : public %s'.format(data.parentNameList.join(', public ')) : ''; 330 let result = { 331 implH: ` 332class %s%s { 333public:%s\n 334};\n`.format(name, extendsStr, implH), 335 implCpp: implCpp, 336 middleBody: middleBodyTmplete.replaceAll('[className]', name).replaceAll('[static_funcs]', middleFunc) 337 .replaceAll('[getConstructorParam]', getConParam) 338 .replaceAll('[constructorParam]', param.valueFill), 339 middleInit: middleInit, 340 declarationH: ` 341class %s;\r`.format(name), 342 middleH: middleHTmplete.replaceAll('[className]', name).replaceAll('[static_funcs]', middleH) 343 .replaceAll('[contructorValueIn]', param.valueIn) 344 }; 345 return result; 346} 347 348function getConstructorFunc(data, param) { 349 let funcValues = null; 350 if (data.function !== null && data.function !== undefined) { 351 for (let i = 0; i < data.function.length; i++) { 352 if (data.function[i].name === 'constructor') { 353 funcValues = data.function[i].value; 354 } 355 } 356 } 357 for (let j in funcValues) { 358 paramGenerate(j, funcValues[j], param, data); 359 } 360 let tmpBody = param.valueCheckout.split(';\n'); 361 let getConParam = ''; 362 for (let i in tmpBody) { 363 let flag = tmpBody[i].replaceAll('\n', '').replaceAll(' ', ''); 364 if (flag !== '') { 365 let indexBegin = tmpBody[i].indexOf('pxt->GetArgv('); 366 if (indexBegin > 0 && tmpBody[i].indexOf('\n') < 0) { 367 tmpBody[i] = tmpBody[i].replaceAll('pxt->GetArgv(', 'args['); 368 let index = tmpBody[i].indexOf(')'); 369 tmpBody[i] = tmpBody[i].substring(0, index) + ']' + tmpBody[i].substring(index + 1, tmpBody[i].length); 370 } else if (indexBegin > 0 && tmpBody[i].indexOf('\n') >= 0) { 371 tmpBody[i] = tmpBody[i].replaceAll('pxt->GetArgv(', 'args['); 372 let index = tmpBody[i].indexOf('),'); 373 tmpBody[i] = tmpBody[i].substring(0, index) + ']' + tmpBody[i].substring(index + 1, tmpBody[i].length); 374 } 375 getConParam += tmpBody[i] + ';\n'; 376 } 377 } 378 let index = getConParam.lastIndexOf(';\n'); 379 if (getConParam.substring(index - 1, index) === ' ') { 380 getConParam = getConParam.substring(0, index - 1); 381 } 382 return getConParam; 383} 384 385// 递归获取接口及接口父类的所有成员属性和方法 386function getAllPropties(interfaceBody, properties, isParentClass) { 387 for (let i in interfaceBody.value) { 388 interfaceBody.value[i].isParentMember = isParentClass; 389 addUniqObj2List(interfaceBody.value[i], properties.values); 390 } 391 for (let i in interfaceBody.function) { 392 interfaceBody.function[i].isParentMember = isParentClass; 393 if (!addUniqFunc2List(interfaceBody.function[i], properties.functions)) { 394 if (isParentClass) { 395 // 没添加到列表,说明子类方法properties.functions重写了父类方法interfaceBody.function[i] 396 // 找到该子类方法并将其设置为override (生成的重写函数如果没有override关键字会触发门禁告警) 397 setOverrideFunc(interfaceBody.function[i], properties.functions); 398 } 399 } 400 } 401 if (!isParentClass && interfaceBody.parentNameList && interfaceBody.parentNameList.length > 0) { 402 getAllPropties(interfaceBody.parentBody, properties, true); 403 } 404} 405 406function addVirtualKeywords(data, implH, name) { 407 if (data.childList && data.childList.length > 0) { 408 // 如果该类是其它类的父类,增加虚析构函数使其具备泛型特征 (基类必须有虚函数才能正确使用dynamic_cast和typeinfo等方法) 409 // 如果该类自己也有父类,虚析构函数需要加上override关键字(否则C++门禁会有告警) 410 let ovrrideStr = (data.parentList && data.parentList.length > 0) ? ' override' : ''; 411 // 如果虚析构函数已经有override关键字,就不需要再加virtual关键字了(否则C++门禁会有告警) 412 let virtualStr = (data.parentList && data.parentList.length > 0) ? '' : 'virtual '; 413 implH += '\n %s~%s()%s {};'.format(virtualStr, name, ovrrideStr); 414 } 415 return implH; 416} 417 418function connectResult(data, inNamespace, name) { 419 let implH = ''; 420 let implCpp = ''; 421 let middleFunc = ''; 422 let middleInit = ''; 423 let middleH = ''; 424 let variable = { 425 hDefine: '', 426 middleValue: '', 427 middleH: '' 428 }; 429 middleInit = getMiddleInitFunc(middleInit, data, variable, name, inNamespace); 430 implH += variable.hDefine; 431 middleFunc += variable.middleValue; 432 middleInit += `\n std::map<const char *, napi_callback> funcList;`; 433 middleH += variable.middleH; 434 for (let i in data.allProperties.functions) { 435 let func = data.allProperties.functions[i]; 436 let tmp; 437 if (isOnOffRegisterFunc(func.name)) { 438 tmp = generateFunctionOnOff(func, data, name); 439 } else if (isCreateThreadsafeFunc(func.name)) { 440 tmp = generateThreadsafeFunc(func, data, name); 441 } 442 if (!tmp) { 443 switch (func.type) { 444 case FuncType.DIRECT: 445 tmp = generateFunctionDirect(func, data, name, implH); 446 break; 447 case FuncType.SYNC: 448 tmp = generateFunctionSync(func, data, name); 449 break; 450 case FuncType.ASYNC: 451 case FuncType.PROMISE: 452 tmp = generateFunctionAsync(func, data, name, implH); 453 break; 454 default: 455 break; 456 } 457 } 458 middleFunc += tmp[0]; 459 implH += tmp[1]; 460 implCpp += tmp[2]; 461 middleH += tmp[3]; 462 middleInit = generateMiddleInitFunc(func, inNamespace, middleInit, name); 463 } 464 implH = addVirtualKeywords(data, implH, name); 465 return [middleFunc, implH, implCpp, middleInit, middleH]; 466} 467 468function generateMiddleInitFunc(func, inNamespace, middleInit, name) { 469 if (func.name !== 'constructor') { 470 let toolNamespace = getToolNamespace(inNamespace); 471 middleInit += `\n funcList["%s"] = %s%s%s_middle::%s_middle;`.format(func.name, 472 inNamespace, toolNamespace, name, func.name); 473 } 474 return middleInit; 475} 476 477function getMiddleInitFunc(middleInit, data, variable, name, inNamespace) { 478 middleInit = `{\n std::map<const char *, std::map<const char *, napi_callback>> valueList;`; 479 data.allProperties = { values: [], functions: [] }; 480 getAllPropties(data, data.allProperties, false); 481 let toolNamespace = getToolNamespace(inNamespace); 482 for (let i in data.allProperties.values) { 483 let v = data.allProperties.values[i]; 484 generateVariable(v, variable, name); 485 middleInit += ` 486 valueList["%s"]["getvalue"] = %s%s%s_middle::getvalue_%s; 487 valueList["%s"]["setvalue"] = %s%s%s_middle::setvalue_%s;` 488 .format(v.name, inNamespace, toolNamespace, name, v.name, v.name, inNamespace, toolNamespace, name, v.name); 489 } 490 return middleInit; 491} 492 493function getToolNamespace(inNamespace) { 494 let index = inNamespace.lastIndexOf('::'); 495 let toolNamespace; 496 if (index > 0) { 497 let bodyTmp = inNamespace.substring(0, index); 498 let index2 = bodyTmp.lastIndexOf('::'); 499 if (index2 > 0 && index2 < index) { 500 toolNamespace = inNamespace.substring(index2 + 2, index) + '_interface::'; 501 } else { 502 toolNamespace = bodyTmp + '_interface::'; 503 } 504 } else { 505 toolNamespace = inNamespace + '_interface::'; 506 } 507 return toolNamespace; 508} 509 510module.exports = { 511 generateInterface, 512 connectResult, 513 generateVariable, 514 mapTypeString, 515 anyTypeString, 516 getHDefineOfVariable 517}; 518