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 { NapiLog } = require('../tools/napi_log'); 16const fs = require('fs'); 17const os = require('os'); 18const { AllParseFileList } = require('../tools/common'); 19const path = require('path'); 20 21function parseFileAll(hFilePath) { 22 let execSync = require('child_process').execSync; 23 let cmd = ''; 24 if (fs.existsSync('./header_parser.py')) { 25 // call python file (for debug test) 26 cmd = 'python ./header_parser.py ' + hFilePath; 27 } else { 28 // call exe file (for real runtime) 29 let sysInfo = os.platform(); 30 let execPath = path.dirname(process.execPath); 31 let exeFile = sysInfo === 'win32' ? path.join(execPath, 'header_parser.exe') : 32 path.join(execPath, 'header_parser'); 33 cmd = exeFile + ' ' + hFilePath; 34 } 35 36 let parseResult = null; 37 let stdout = execSync(cmd); 38 parseResult = JSON.parse(stdout.toString()).result; 39 parseResult.rawContent = fs.readFileSync(hFilePath, 'UTF-8'); 40 return parseResult; 41} 42 43function analyzeNameSpace(rootInfo, parseResult) { 44 if (parseResult.namespaces.length === 0) { 45 return; 46 } 47 let lastNameSpace = parseResult.namespaces[parseResult.namespaces.length - 1]; 48 rootInfo.nameSpace = lastNameSpace.split('::'); 49} 50 51function createParam(parseParamInfo) { 52 let param = {}; 53 param.name = parseParamInfo.name; 54 param.type = parseParamInfo.reference ? parseParamInfo.type.replace('&', '').trim() : parseParamInfo.type; 55 param.rawType = parseParamInfo.raw_type; 56 param.isPointer = (parseParamInfo.pointer === 1); 57 param.isReference = (parseParamInfo.reference === 1); 58 param.isArray = (parseParamInfo.array === 1); 59 param.isConstant = (parseParamInfo.constant === 1); 60 return param; 61} 62 63function createFuncInfo(parseFuncInfo) { 64 let funcInfo = { 65 'name': '', // 方法名 66 'params': [], // 参数列表 67 'retType': '', // 返回值 68 'rawStr': '' // 方法原始代码 69 }; 70 funcInfo.name = parseFuncInfo.name; 71 72 let parseParams = parseFuncInfo.parameters; 73 for (let i = 0; i < parseParams.length; ++i) { 74 let param = createParam(parseParams[i]); 75 funcInfo.params.push(param); 76 } 77 78 funcInfo.retType = parseFuncInfo.returns === '' ? parseFuncInfo.rtnType : parseFuncInfo.returns; 79 funcInfo.rawStr = parseFuncInfo.debug; 80 return funcInfo; 81} 82 83function createClassFunctions(parseFuncs) { 84 let funcList = []; 85 for (let i = 0; i < parseFuncs.length; ++i) { 86 if (!(parseFuncs[i].constructor || parseFuncs[i].destructor)) { // 构造和析构方法不需要生成remote接口代码 87 let funcInfo = createFuncInfo(parseFuncs[i]); 88 funcList.push(funcInfo); 89 } 90 } 91 return funcList; 92} 93 94function createClassInfo(parseClassInfo) { 95 let classInfo = { 96 'name': '', 97 'namespace': [], 98 'properties': [], 99 'functions': [], 100 'extends': [] 101 }; 102 classInfo.name = parseClassInfo.name; 103 classInfo.namespace = parseClassInfo.namespace.split('::'); 104 classInfo.functions = createClassFunctions(parseClassInfo.methods.public); 105 106 return classInfo; 107} 108 109function analyzeClasses(rootInfo, parseClasses) { 110 if (parseClasses.length === 0) { 111 NapiLog.logError('Can not find any class.'); 112 return; 113 } 114 115 let firstClassName = null; // JSON集合中第一个class名称 116 let serviceClassName = null;// JSON集合中带“@ServiceClass”注解的class名称 117 let i = 0; 118 for (let className in parseClasses) { 119 if (++i === 1) { 120 firstClassName = className; 121 } 122 123 let doxygen = parseClasses[className].doxygen; 124 if (doxygen && doxygen.includes('@ServiceClass')) { 125 serviceClassName = className; 126 break; 127 } 128 } 129 130 if (parseClasses.length === 1) { 131 // h文件中只有唯一的一个类,基于该类的接口定义生成service 132 rootInfo.serviceName = firstClassName; 133 let classInfo = createClassInfo(parseClasses[firstClassName]); 134 rootInfo.class.push(classInfo); 135 } else { 136 // h文件中有多个类,基于带@ServiceClass注解的类生成service 137 if (serviceClassName === null || serviceClassName === undefined) { 138 NapiLog.logError('There must be one class that contains @ServiceClass annotations.'); 139 return; 140 } 141 rootInfo.serviceName = serviceClassName; 142 let classInfo = createClassInfo(parseClasses[serviceClassName]); 143 rootInfo.class.push(classInfo); 144 } 145} 146 147function doAnalyze(hFilePath, cmdParam) { 148 let parseResult = parseFileAll(hFilePath); 149 parseResult.isInclude = false; 150 AllParseFileList.push(parseResult); 151 let rootInfo = { 152 'serviceName': '', 153 'nameSpace': [], 154 'class': [], 155 'includes': [], 156 'using': [], 157 'serviceId': (cmdParam.serviceId === null || cmdParam.serviceId === undefined) ? 158 '9002' : cmdParam.serviceId, 159 'versionTag': (cmdParam.versionTag === null || cmdParam.versionTag === undefined) ? 160 '3.2' : cmdParam.versionTag, 161 'rawContent': parseResult.rawContent 162 }; 163 164 analyzeNameSpace(rootInfo, parseResult); 165 analyzeClasses(rootInfo, parseResult.classes); 166 rootInfo.includes = parseResult.includes; 167 rootInfo.using = parseResult.using; 168 return rootInfo; 169} 170 171module.exports = { 172 doAnalyze 173};