From 2423a24296e9e6b274c0877a9e002214e15bcf3c Mon Sep 17 00:00:00 2001 From: zhaojunxia2020 Date: Thu, 29 Sep 2022 14:48:56 +0800 Subject: [PATCH] service gen tool Signed-off-by: zhaojunxia2020 --- service-gen/package.json | 19 ++ service-gen/src/gen/analyze.js | 172 ++++++++++ service-gen/src/gen/fileTemplate.js | 468 +++++++++++++++++++++++++++ service-gen/src/gen/generate.js | 375 +++++++++++++++++++++ service-gen/src/gen/header_parser.py | 24 ++ service-gen/src/gen/main.js | 102 ++++++ service-gen/src/tools/FileRW.js | 149 +++++++++ service-gen/src/tools/NapiLog.js | 125 +++++++ service-gen/src/tools/common.js | 49 +++ service-gen/src/tools/re.js | 80 +++++ service-gen/src/tools/tool.js | 68 ++++ 11 files changed, 1631 insertions(+) create mode 100644 service-gen/package.json create mode 100644 service-gen/src/gen/analyze.js create mode 100644 service-gen/src/gen/fileTemplate.js create mode 100644 service-gen/src/gen/generate.js create mode 100644 service-gen/src/gen/header_parser.py create mode 100644 service-gen/src/gen/main.js create mode 100644 service-gen/src/tools/FileRW.js create mode 100644 service-gen/src/tools/NapiLog.js create mode 100644 service-gen/src/tools/common.js create mode 100644 service-gen/src/tools/re.js create mode 100644 service-gen/src/tools/tool.js diff --git a/service-gen/package.json b/service-gen/package.json new file mode 100644 index 00000000..c040319c --- /dev/null +++ b/service-gen/package.json @@ -0,0 +1,19 @@ +{ + "name": "service-gen", + "version": "1.0.0", + "description": "", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "stdio": "^2.1.1", + "typescript": "^4.8.3" + }, + "bin": "./src/gen/main.js", + "pkg": { + "assets": [] + } +} diff --git a/service-gen/src/gen/analyze.js b/service-gen/src/gen/analyze.js new file mode 100644 index 00000000..5acf4b5b --- /dev/null +++ b/service-gen/src/gen/analyze.js @@ -0,0 +1,172 @@ +/* +* 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 fs = require("fs"); +const os = require("os"); +const { NapiLog } = require("../tools/NapiLog"); +const { writeFile, createFolder } = require("../tools/FileRW"); +const re = require("../tools/re"); +const gen = require("./generate"); + +function parseFileAll(hFilePath) { + let execSync = require("child_process").execSync; + let cmd = ""; + if(fs.existsSync("./service-gen/src/gen/header_parser.py")) { + // call python file (for debug test) + cmd = "python ./service-gen/src/gen/header_parser.py " + hFilePath; + } else { + // call exe file (for real runtime) + let sysInfo = os.platform(); + let exeFile = sysInfo === 'win32' ? "header_parser.exe" : "./header_parser"; + cmd = exeFile + " " + hFilePath; + } + + let parseResult = null; + let stdout = execSync(cmd); + parseResult = JSON.parse(stdout.toString()).result; + return parseResult; +} + +function analyzeNameSpace(rootInfo, parseResult) { + if (parseResult.namespaces.length == 0) { + return; + } + let lastNameSpace = parseResult.namespaces[parseResult.namespaces.length - 1]; + rootInfo.nameSpace = lastNameSpace.split('::'); +} + +function createParam(parseParamInfo) { + let param = {}; + param.name = parseParamInfo.name; + param.type = parseParamInfo.type; + param.rawType = parseParamInfo.raw_type; + param.isPointer = (parseParamInfo.pointer == 1); + param.isReference = (parseParamInfo.reference == 1); + param.isArray = (parseParamInfo.array == 1); + param.isConstant = (parseParamInfo.constant == 1); + return param; +} + +function createFuncInfo(parseFuncInfo) { + let funcInfo = { + "name": "", // 方法名 + "params": [], // 参数列表 + "retType": "", // 返回值 + "rawStr": "" // 方法原始代码 + } + funcInfo.name = parseFuncInfo.name; + + let parseParams = parseFuncInfo.parameters; + for(var i = 0; i < parseParams.length; ++i) { + let param = createParam(parseParams[i]); + funcInfo.params.push(param); + } + + funcInfo.retType = parseFuncInfo.returns === '' ? parseFuncInfo.rtnType : parseFuncInfo.returns; + funcInfo.rawStr = parseFuncInfo.debug; + return funcInfo; +} + +function createClassFunctions(parseFuncs) { + let funcList = []; + for(var i = 0; i < parseFuncs.length; ++i) { + if (!(parseFuncs[i].constructor || parseFuncs[i].destructor)) { // 构造和析构方法不需要生成remote接口代码 + let funcInfo = createFuncInfo(parseFuncs[i]); + funcList.push(funcInfo); + } + } + return funcList; +} + +function createClassInfo(parseClassInfo) { + let classInfo = { + "name": "", + "namespace": [], + "properties": [], + "functions": [], + "extends":[] + } + classInfo.name = parseClassInfo.name; + classInfo.namespace = parseClassInfo.namespace.split('::'); + classInfo.functions = createClassFunctions(parseClassInfo.methods.public); + + return classInfo; +} + +function analyzeClasses(rootInfo, parseClasses) { + if (parseClasses.length == 0) { + return; + } + + for(var className in parseClasses) { + rootInfo.serviceName = className; + let classInfo = createClassInfo(parseClasses[className]); + rootInfo.class.push(classInfo); + break; // 只取首个class(每个接口文件中应该只包含一个service class) + } +} + +function wirte2Disk(fileInfo, destDir) { + let filePath = re.pathJoin(destDir, fileInfo.name); + writeFile(filePath, fileInfo.content); +} + +function doAnalyze(hFilePath, cmdParam) { + let destDir = cmdParam.out; + let parseResult = parseFileAll(hFilePath); + let rootInfo = { + "serviceName": "", + "nameSpace": [], + "class": [], + "includes": [], + "serviceId": cmdParam.serviceId == null ? "9002" : cmdParam.serviceId + } + // 1. h文件解析保存为结构体 + analyzeNameSpace(rootInfo, parseResult); + analyzeClasses(rootInfo, parseResult.classes); + rootInfo.includes = parseResult.includes; + + // 2. 根据结构体生成代码 + let fileContent = gen.doGenerate(rootInfo); + + // 3. 创建service工程目录 + let serviceFolder = rootInfo.serviceName.toLowerCase() + "service"; + let outputPath = destDir + "/" + serviceFolder; + createFolder(re.pathJoin(destDir, serviceFolder)); + createFolder(re.pathJoin(outputPath, "include")); + createFolder(re.pathJoin(outputPath, "interface")); + createFolder(re.pathJoin(outputPath, "sa_profile")); + createFolder(re.pathJoin(outputPath, "etc")); + createFolder(re.pathJoin(outputPath, "src")); + + // 4. 生成代码保存为文件 + wirte2Disk(fileContent.iServiceHFile, outputPath + "/interface"); + wirte2Disk(fileContent.proxyHFile, outputPath + "/include"); + wirte2Disk(fileContent.stubHFile, outputPath + "/include"); + wirte2Disk(fileContent.serviceHFile, outputPath + "/include"); + wirte2Disk(fileContent.proxyCppFile, outputPath + "/src"); + wirte2Disk(fileContent.stubCppFile, outputPath + "/src"); + wirte2Disk(fileContent.serviceCppFile, outputPath + "/src"); + wirte2Disk(fileContent.clientCppFile, outputPath + "/src"); + wirte2Disk(fileContent.buildGnFile, outputPath); + wirte2Disk(fileContent.bundleJsonFile, outputPath); + wirte2Disk(fileContent.profileGnFile, outputPath + "/sa_profile"); + wirte2Disk(fileContent.profileXmlFile, outputPath + "/sa_profile"); + wirte2Disk(fileContent.serviceCfgFile, outputPath + "/etc"); + wirte2Disk(fileContent.serviceCfgGnFile, outputPath + "/etc"); +} + +module.exports = { + doAnalyze +} diff --git a/service-gen/src/gen/fileTemplate.js b/service-gen/src/gen/fileTemplate.js new file mode 100644 index 00000000..186f4581 --- /dev/null +++ b/service-gen/src/gen/fileTemplate.js @@ -0,0 +1,468 @@ +/* +* 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. +*/ + +let iServiceHTemplate = `#ifndef I_[marcoName]_SERVICE_H +#define I_[marcoName]_SERVICE_H + +[includes] +#include "iremote_broker.h" +#include "iremote_proxy.h" + +namespace OHOS { +namespace [serviceName] { +class I[className]Service : public OHOS::IRemoteBroker { +public: + enum { + [funcEnum] + }; + + DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.[serviceName].I[className]Service"); +public: + [functions] +}; +} // namespace [serviceName] +} // namespace OHOS +#endif // I_[marcoName]_SERVICE_H +`; +let proxyHTemplate = `#ifndef [marcoName]_PROXY_H +#define [marcoName]_PROXY_H +#include "message_parcel.h" +#include "parcel.h" +#include "iremote_broker.h" +#include "iremote_proxy.h" +#include "[iServiceHInclude]" + +namespace OHOS { +namespace [serviceName] { +class [className]Proxy : public IRemoteProxy { +public: + explicit [className]Proxy(const sptr &impl); + ~[className]Proxy() = default; + [functions] +private: + static inline BrokerDelegator<[className]Proxy> delegator_; +}; + +class [className]DeathRecipient : public IRemoteObject::DeathRecipient { +public: + virtual void OnRemoteDied(const wptr &remote) override; + [className]DeathRecipient(); + virtual ~[className]DeathRecipient(); +}; +} // namespace [serviceName] +} // namespace OHOS +#endif // [marcoName]_PROXY_H +`; + +let stubHTemplate = `#ifndef [marcoName]_STUB_H +#define [marcoName]_STUB_H +#include "iremote_stub.h" +#include "message_parcel.h" +#include "parcel.h" +#include "[iServiceHInclude]" + +namespace OHOS { +namespace [serviceName] { +class [className]Stub : public IRemoteStub { +public: + [className]Stub(); + virtual ~[className]Stub(); + int OnRemoteRequest(uint32_t code, MessageParcel& data, MessageParcel& reply, + MessageOption &option) override; +private: + using [className]InnerFunc = ErrCode ([className]Stub::*)(MessageParcel &data, MessageParcel &reply); + [innerFuncDef] + std::unordered_map innerFuncs_; +}; +} // namespace [serviceName] +} // namespace OHOS +#endif // [marcoName]_STUB_H`; + +let serviceHTemplate = `#ifndef [marcoName]_SERVICE_H +#define [marcoName]_SERVICE_H +#include "ipc_skeleton.h" +#include "system_ability.h" +#include "[stubHInclude]" + +namespace OHOS { +namespace [serviceName] { +// Business implementation +class [className]Service : public SystemAbility, public [className]Stub { +public: + DECLARE_SYSTEM_ABILITY([className]Service); + DISALLOW_COPY_AND_MOVE([className]Service); + explicit [className]Service(int32_t systemAbilityId, bool runOnCreate = true); + ~[className]Service() override; + + // Business implementation + [functions] + + // implement SystemAbility methods + void OnStart() override; + void OnStop() override; +}; +} +} +#endif // [marcoName]_SERVICE_H`; + +let proxyCppTemplate = `#include "[proxyHInclude]" +using namespace std; + +namespace OHOS { +namespace [serviceName] { +[className]Proxy::[className]Proxy(const sptr &impl) : IRemoteProxy(impl){} + +[remoteFuncImpl] +/** + * @brief Notify that a remote object died. + * It's called when the linked remote object died. + * + * @param remote The died IRemoteObject handler of the remote object + */ +void [className]DeathRecipient::OnRemoteDied(const wptr &remote) +{ + +} + +[className]DeathRecipient::[className]DeathRecipient() +{ +} + +[className]DeathRecipient::~[className]DeathRecipient() +{ +} + +} // namespace [serviceName] +} // namespace OHOS +`; +let proxyFuncTemplate = `[retType] [className]Proxy::[funcName]([params]) +{ + int retCode; + MessageParcel data, reply; + MessageOption option; + data.WriteInterfaceToken(GetDescriptor()); + [writeData] + retCode = Remote()->SendRequest([funcEnum], data, reply, option); + retCode = reply.ReadInt32(); + if (retCode != ERR_OK) { + + } + + [readReply] +}\n\n`; + +let stubCppTemplate = `#include "[stubHInclude]" +using namespace std; + +namespace OHOS { +namespace [serviceName] { + +[className]Stub::[className]Stub() +{ + [innerFuncMap] +} + +[className]Stub::~[className]Stub() +{ + innerFuncs_.clear(); +} + +int [className]Stub::OnRemoteRequest(uint32_t code, MessageParcel& data, MessageParcel& reply, + MessageOption &option) +{ + std::u16string descriptor = [className]Stub::GetDescriptor(); + std::u16string remoteDescriptor = data.ReadInterfaceToken(); + if (descriptor != remoteDescriptor) { + return OBJECT_NULL; + } + auto itFunc = innerFuncs_.find(code); + if (itFunc != innerFuncs_.end()) { + auto memberFunc = itFunc->second; + if (memberFunc != nullptr) { + return (this->*memberFunc)(data, reply); + } + } + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); +} + +[innerFuncImpl] + +} // namespace [serviceName] +} // namespace OHOS +`; + +let stubInnerFuncTemplate = `ErrCode [className]Stub::[funcName]Inner(MessageParcel &data, MessageParcel &reply) +{ + int retCode = ERR_OK; + [readData] + [writeReply] + return retCode; +} +`; + +let serviceCppTemplate = `#include "[serviceHInclude]" +#include "system_ability_definition.h" +using namespace std; + +namespace OHOS { +namespace [serviceName] { +// [marcoName]_SERVICE_ID should be defined in system_ability_definition.h +REGISTER_SYSTEM_ABILITY_BY_ID([className]Service, [marcoName]_SERVICE_ID, true) + +[className]Service::[className]Service(int32_t systemAbilityId, bool runOnCreate) + :SystemAbility(systemAbilityId, runOnCreate) +{ + +} + +[className]Service::~[className]Service(){ + +} + +void [className]Service::OnStart() +{ + // Publish(): Register service by method ISystemAbilityManager->AddSystemAbility() + bool isPublished = Publish(this); + if (!isPublished) { + // Log: Failed to publish the service + } + return; +} + +void [className]Service::OnStop() +{ + +} + +[serviceFuncImpl] +} +}`; + +let serviceFuncImplTemplate = `[retType] [className]Service::[funcName]([params]) +{ + [retType] ret; + // TODO: Invoke the business implementation + return ret; +} +`; +let clientCppTemplate = `#include "[proxyHInclude]" +#include "ipc_skeleton.h" +#include "system_ability_definition.h" +#include "iservice_registry.h" + +using namespace std; +using namespace OHOS; +using namespace OHOS::[serviceName]; + +sptr getRemoteProxy() +{ + auto saMgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (saMgr == nullptr) { + return nullptr; + } + + // [marcoName]_SERVICE_ID should be defined in system_ability_definition.h + sptr object = saMgr->GetSystemAbility([marcoName]_SERVICE_ID); + sptr proxy = nullptr; + if (object != nullptr) { + sptr death(new [className]DeathRecipient()); + object->AddDeathRecipient(death.GetRefPtr()); + proxy = iface_cast(object); + } + + if (proxy == nullptr) { + return nullptr; + } + + return proxy; +} + +int main(int argc, char *argv[]) +{ + auto proxy = getRemoteProxy(); + // TODO: Invoke remote method by proxy + [clientFuncInvoke] + + IPCSkeleton::JoinWorkThread(); + return 0; +}`; + +let buildGnTemplate = `import("//build/ohos.gni") + +ohos_shared_library("[lowServiceName]service") { + sources = [ + "//[lowServiceName]service/src/[stubCppFile]", + "//[lowServiceName]service/src/[serviceCppFile]" + ] + include_dirs = [ + "//[lowServiceName]service/include", + "//[lowServiceName]service/interface", + "//utils/native/base/include" + ] + + deps = [ + "//base/startup/syspara_lite/interfaces/innerkits/native/syspara:syspara", + "//utils/native/base:utils", + ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "safwk:system_ability_fwk", + "samgr_standard:samgr_proxy", + "startup_l2:syspara", + ] + + part_name = "[lowServiceName]service_part" + subsystem_name = "[lowServiceName]service" +} + +ohos_executable("[lowServiceName]client") { + sources = [ + "//[lowServiceName]service/src/[proxyCppFile]", + "//[lowServiceName]service/src/[clientCppFile]" + ] + + include_dirs = [ + "//[lowServiceName]service/include", + "//[lowServiceName]service/interface", + "//utils/native/base/include" + ] + + deps = [ + "//utils/native/base:utils", + ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog", + "ipc:ipc_core", + "samgr_standard:samgr_proxy", + ] + + part_name = "[lowServiceName]service_part" + subsystem_name = "[lowServiceName]service" +} +`; + +let bundleJsonTemplate = `{ + "name": "@ohos/[lowServiceName]service", + "description": "system ability framework test", + "homePage": "https://gitee.com/", + "version": "3.1", + "license": "Apache License 2.0", + "repository": "", + "publishAs": "code-segment", + "segment": { + "destPath": "[lowServiceName]service" + }, + "dirs": {}, + "scripts": {}, + "component": { + "name": "[lowServiceName]service_part", + "subsystem": "[lowServiceName]service", + "adapted_system_type": [ + "standard" + ], + "rom": "2048KB", + "ram": "~4096KB", + "deps": { + "components": [ + "hiviewdfx_hilog_native", + "ipc", + "samgr_standard", + "utils_base", + "safwk", + "startup_l2" + ], + "third_party": [ "libxml2" ] + }, + "build": { + "sub_component": [ + "//[lowServiceName]service:[lowServiceName]service", + "//[lowServiceName]service/sa_profile:[lowServiceName]service_sa_profile", + "//[lowServiceName]service:[lowServiceName]client", + "//[lowServiceName]service/etc:[lowServiceName]_service_init" + ], + "inner_kits": [ + ], + "test": [ + ] + } + } +}`; + +let profileGnTemplate = `import("//build/ohos.gni") +import("//build/ohos/sa_profile/sa_profile.gni") + +ohos_sa_profile("[lowServiceName]service_sa_profile") { + sources = [ "[serviceId].xml" ] + + part_name = "[lowServiceName]service_part" +} +`; + +let profileXmlTemplate = ` + + [lowServiceName]service_sa + + [serviceId] + lib[lowServiceName]service.z.so + true + false + 1 + + +`; + +let serviceCfgTemplate = `{ + "services" : [{ + "name" : "[lowServiceName]service", + "path" : ["/system/bin/sa_main", "/system/profile/[lowServiceName]service_sa.xml"], + "uid" : "system", + "gid" : ["system", "shell"] + } + ] +} +`; + +let serviceCfgGnTemplate = `import("//build/ohos.gni") + +ohos_prebuilt_etc("[lowServiceName]_service_init") { + source = "[lowServiceName]_service.cfg" + relative_install_dir = "init" + part_name = "[lowServiceName]service_part" + subsystem_name = "[lowServiceName]service" +} +`; + +module.exports = { + iServiceHTemplate, + proxyHTemplate, + stubHTemplate, + serviceHTemplate, + proxyCppTemplate, + proxyFuncTemplate, + stubCppTemplate, + stubInnerFuncTemplate, + serviceCppTemplate, + serviceFuncImplTemplate, + clientCppTemplate, + buildGnTemplate, + bundleJsonTemplate, + profileGnTemplate, + profileXmlTemplate, + serviceCfgTemplate, + serviceCfgGnTemplate +} \ No newline at end of file diff --git a/service-gen/src/gen/generate.js b/service-gen/src/gen/generate.js new file mode 100644 index 00000000..41dfddab --- /dev/null +++ b/service-gen/src/gen/generate.js @@ -0,0 +1,375 @@ + +/* +* 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 { NapiLog } = require("../tools/NapiLog"); +const { replaceAll, getTab } = require("../tools/tool"); +const re = require("../tools/re"); +const { iServiceHTemplate, proxyHTemplate, stubHTemplate, serviceHTemplate, proxyCppTemplate, + proxyFuncTemplate, stubCppTemplate, stubInnerFuncTemplate, serviceCppTemplate, serviceFuncImplTemplate, + clientCppTemplate, buildGnTemplate, bundleJsonTemplate, profileGnTemplate, profileXmlTemplate, serviceCfgTemplate, + serviceCfgGnTemplate } = require("./fileTemplate"); +const { DATA_W_MAP, DATA_R_MAP, getParcelType } = require("../tools/common"); + +let fileContent = { + "iServiceHFile": {}, + "proxyHFile": {}, + "stubHFile": {}, + "serviceHFile": {}, + "proxyCppFile": {}, + "stubCppFile": {}, + "serviceCppFile": {}, + "clientCppFile": {}, + "buildGnFile": {}, + "bundleJsonFile": {}, + "profileGnFile": {}, + "profileXmlFile": {}, + "serviceCfgFile": {}, + "serviceCfgGnFile": {} +}; + +function getIncludeStr(includeList) { + let includeStr = ""; + for (let i = 0; i < includeList.length; ++i) { + includeStr += "#include " + includeList[i] + "\n"; + } + return includeStr; +} + +function getFuncParamStr(params) { + let paramStr = ""; + for (let i = 0; i < params.length; ++i) { + paramStr += (i == 0) ? "" : ", "; + paramStr += params[i].type + " " + params[i].name; + } + return paramStr; +} + +/** + * 生成c参数写入remote消息buffer(parcel data)的代码段 + * @param {} srcName 待写入的c变量名 + * @param {*} destName 写入目标(parcel data)变量的名称 + * @param {*} vType c变量类型 + * @returns 生成的代码段 + */ +function genWriteString(srcName, destName, vType) { + let matchs = re.match("(std::)?vector<([\x21-\x7e]+)>", vType); + if (matchs) { // write data of std::vector + let rowType = re.getReg(vType, matchs.regs[2]); + let parcelType = getParcelType(rowType); + let wFunc = DATA_W_MAP.get(parcelType); + if (!wFunc) { + NapiLog.logError("Unsupport writing with type: " + vType); + return ""; + } + // use function Parcel::WriteVector(const std::vector &val, bool (Parcel::*Write)(T2)) + return "%s.WriteVector(%s, &(%s.%s));".format(destName, srcName, destName, wFunc); + } + + let parcelType = getParcelType(vType); + let wFunc = DATA_W_MAP.get(parcelType); + if (!wFunc) { + NapiLog.logError("Unsupport writing with type: " + vType); + return ""; + } + return "%s.%s(%s);".format(destName, wFunc, srcName); +} + +/** + * 生成从remote消息buffer(parcel data)读取c参数的代码段 + * @param {*} srcName 待读取的parcel data变量名称 + * @param {*} destName 读取出的内容写入的c变量名称 + * @param {*} vType c变量类型 + * @returns 生成的代码段 + */ +function genReadString(srcName, destName, vType) { + let matchs = re.match("(std::)?vector<([\x21-\x7e]+)>", vType); + if (matchs) { // write data of std::vector + let rowType = re.getReg(vType, matchs.regs[2]); + let parcelType = getParcelType(rowType); + let rFunc = DATA_R_MAP.get(parcelType); + if (!rFunc) { + NapiLog.logError("Unsupport reading with type: " + vType); + return ""; + } + // use function Parcel::ReadVector(std::vector *val, bool (Parcel::*Read)(T &)) + return "%s.ReadVector(&(%s), &(%s.%s));".format(srcName, destName, srcName, rFunc); + } + + let parcelType = getParcelType(vType); + let rFunc = DATA_R_MAP.get(parcelType); + if (!rFunc) { + NapiLog.logError("Unsupport reading with type: " + vType); + return ""; + } + return "%s = %s.%s();".format(destName, srcName, rFunc); +} + +function genProxyFunc(funcInfo, className, paramStr) { + let proxyFunc = replaceAll(proxyFuncTemplate, "[className]", className); + proxyFunc = replaceAll(proxyFunc, "[funcName]", funcInfo.name); + proxyFunc = replaceAll(proxyFunc, "[params]", paramStr); + proxyFunc = replaceAll(proxyFunc, "[retType]", funcInfo.retType); + proxyFunc = replaceAll(proxyFunc, "[funcEnum]", funcInfo.funcEnum); + + // 入参处理 + let writeDataStr = ""; + let tab = getTab(1); + for (let i = 0; i < funcInfo.params.length; ++i) { + let param = funcInfo.params[i]; + writeDataStr += (i == 0) ? "" : "\n" + tab; + writeDataStr += genWriteString(param.name, "data", param.type); + } + proxyFunc = replaceAll(proxyFunc, "[writeData]", writeDataStr); + + // 返回值处理 + let readReplyStr = ""; + if (funcInfo.retType != "void") { + readReplyStr = "%s result;".format(funcInfo.retType); + readReplyStr += "\n" + tab + genReadString("reply", "result", funcInfo.retType); + readReplyStr += "\n" + tab + "return result;"; + } + proxyFunc = replaceAll(proxyFunc, "[readReply]", readReplyStr); + + return proxyFunc; +} + +function genStubInnerFunc(funcInfo, className) { + let innerFunc = replaceAll(stubInnerFuncTemplate, "[className]", className); + innerFunc = replaceAll(innerFunc, "[funcName]", funcInfo.name); + + // 入参处理 + let readDataStr = ""; // 生成服务端读取客户端传参的代码段 + let tab = getTab(1); + let innerParamStr = ""; // 调用业务方法时传入的入参列表 + for (let i = 0; i < funcInfo.params.length; ++i) { + let param = funcInfo.params[i]; + let innerParamName = param.name + "Val"; + if (i > 0) { + readDataStr += "\n" + tab; + innerParamStr += " ,"; + } + + //将remote请求中的参数值读取到内部参数变量中 + readDataStr += "%s %s;".format(param.type, innerParamName); // 定义内部参数变量 + readDataStr += "\n" + tab + genReadString("data", param.name + "Val", param.type); + innerParamStr += innerParamName; + } + innerFunc = replaceAll(innerFunc, "[readData]", readDataStr); + + // 调用service的实际业务逻辑实现方法 + let writeReplyStr = ""; // 生成调用服务端实现并返回结果的代码段 + if (funcInfo.retType === "void") { + writeReplyStr += "%s(%s); // call business implementation".format(funcInfo.name, innerParamStr); + writeReplyStr += "\n" + tab + "reply.WriteInt32(retCode);"; + } else { + writeReplyStr += "%s retVal = %s(%s); // call business implementation".format( + funcInfo.retType, funcInfo.name, innerParamStr); + writeReplyStr += "\n" + tab + "reply.WriteInt32(retCode);"; + writeReplyStr += "\n" + tab + genWriteString("retVal", "reply", funcInfo.retType); + } + innerFunc = replaceAll(innerFunc, "[writeReply]", writeReplyStr); + return innerFunc; +} + +function genServiceFunc(funcInfo, className, paramStr) { + let serviceFunc = replaceAll(serviceFuncImplTemplate, "[retType]", funcInfo.retType); + serviceFunc = replaceAll(serviceFunc, "[className]", className); + serviceFunc = replaceAll(serviceFunc, "[funcName]", funcInfo.name); + serviceFunc = replaceAll(serviceFunc, "[params]", paramStr); + return serviceFunc; +} + +function genFunctions(classInfo, files) { + let res = genFunctionCode(classInfo); + files.iServiceH = replaceAll(files.iServiceH, "[funcEnum]", res.funcEnumStr); + files.iServiceH = replaceAll(files.iServiceH, "[functions]", res.iServiceFuncH); + files.proxyH = replaceAll(files.proxyH, "[functions]", res.proxyFuncH); + files.stubH = replaceAll(files.stubH, "[innerFuncDef]", res.stubInnerFuncH); + files.serviceH = replaceAll(files.serviceH, "[functions]", res.proxyFuncH); + files.proxyCpp = replaceAll(files.proxyCpp, "[remoteFuncImpl]", res.proxyFuncCpp); + files.stubCpp = replaceAll(files.stubCpp, "[innerFuncMap]", res.stubInnerFuncMap); + files.stubCpp = replaceAll(files.stubCpp, "[innerFuncImpl]", res.stubInnerFuncCpp); + files.serviceCpp = replaceAll(files.serviceCpp, "[serviceFuncImpl]", res.serviceFuncCpp); + files.clientCpp = replaceAll(files.clientCpp, "[clientFuncInvoke]", res.clientFuncCpp); +} + +function genFilesByTemplate(upperServiceName, lowServiceName, rootInfo) { + let files = {}; + // 按模板生成.h和.cpp文件内容框架 + files.iServiceH = replaceAll(iServiceHTemplate, "[marcoName]", upperServiceName); + files.proxyH = replaceAll(proxyHTemplate, "[marcoName]", upperServiceName); + files.stubH = replaceAll(stubHTemplate, "[marcoName]", upperServiceName); + files.serviceH = replaceAll(serviceHTemplate, "[marcoName]", upperServiceName); + files.proxyCpp = proxyCppTemplate; + files.stubCpp = stubCppTemplate; + files.serviceCpp = replaceAll(serviceCppTemplate, "[marcoName]", upperServiceName); + files.clientCpp = replaceAll(clientCppTemplate, "[marcoName]", upperServiceName); + + // 按模板生成资源配置文件内容框架 + files.buildGn = replaceAll(buildGnTemplate, "[lowServiceName]", lowServiceName); + files.buildGn = replaceAll(files.buildGn, "[stubCppFile]", fileContent.stubCppFile.name); + files.buildGn = replaceAll(files.buildGn, "[serviceCppFile]", fileContent.serviceCppFile.name); + files.buildGn = replaceAll(files.buildGn, "[proxyCppFile]", fileContent.proxyCppFile.name); + files.buildGn = replaceAll(files.buildGn, "[clientCppFile]", fileContent.clientCppFile.name); + files.bundleJson = replaceAll(bundleJsonTemplate, "[lowServiceName]", lowServiceName); + files.profileGn = replaceAll(profileGnTemplate, "[lowServiceName]", lowServiceName); + files.profileGn = replaceAll(files.profileGn, "[serviceId]", rootInfo.serviceId); + files.profileXml = replaceAll(profileXmlTemplate, "[lowServiceName]", lowServiceName); + files.profileXml = replaceAll(files.profileXml, "[serviceId]", rootInfo.serviceId); + files.serviceCfg = replaceAll(serviceCfgTemplate, "[lowServiceName]", lowServiceName); + files.serviceGnCfg = replaceAll(serviceCfgGnTemplate, "[lowServiceName]", lowServiceName); + return files; +} + +function replaceClassName(files, classInfo) { + files.iServiceH = replaceAll(files.iServiceH, "[className]", classInfo.name); + files.proxyH = replaceAll(files.proxyH, "[className]", classInfo.name); + files.stubH = replaceAll(files.stubH, "[className]", classInfo.name); + files.serviceH = replaceAll(files.serviceH, "[className]", classInfo.name); + files.proxyCpp = replaceAll(files.proxyCpp, "[className]", classInfo.name); + files.stubCpp = replaceAll(files.stubCpp, "[className]", classInfo.name); + files.serviceCpp = replaceAll(files.serviceCpp, "[className]", classInfo.name); + files.clientCpp = replaceAll(files.clientCpp, "[className]", classInfo.name); +} + +function replaceServiceName(files, rootInfo) { + files.iServiceH = replaceAll(files.iServiceH, "[serviceName]", rootInfo.serviceName); + files.proxyH = replaceAll(files.proxyH, "[serviceName]", rootInfo.serviceName); + files.stubH = replaceAll(files.stubH, "[serviceName]", rootInfo.serviceName); + files.serviceH = replaceAll(files.serviceH, "[serviceName]", rootInfo.serviceName); + files.proxyCpp = replaceAll(files.proxyCpp, "[serviceName]", rootInfo.serviceName); + files.stubCpp = replaceAll(files.stubCpp, "[serviceName]", rootInfo.serviceName); + files.serviceCpp = replaceAll(files.serviceCpp, "[serviceName]", rootInfo.serviceName); + files.clientCpp = replaceAll(files.clientCpp, "[serviceName]", rootInfo.serviceName); +} + +function replaceIncludes(files, rootInfo) { + files.iServiceH = replaceAll(files.iServiceH, "[includes]", getIncludeStr(rootInfo.includes)); + files.proxyH = replaceAll(files.proxyH, "[iServiceHInclude]", fileContent.iServiceHFile.name); + files.stubH = replaceAll(files.stubH, "[iServiceHInclude]", fileContent.iServiceHFile.name); + files.serviceH = replaceAll(files.serviceH, "[stubHInclude]", fileContent.stubHFile.name); + files.proxyCpp = replaceAll(files.proxyCpp, "[proxyHInclude]", fileContent.proxyHFile.name); + files.stubCpp = replaceAll(files.stubCpp, "[stubHInclude]", fileContent.stubHFile.name); + files.serviceCpp = replaceAll(files.serviceCpp, "[serviceHInclude]", fileContent.serviceHFile.name); + files.clientCpp = replaceAll(files.clientCpp, "[proxyHInclude]", fileContent.proxyHFile.name); +} + +function genFileNames(lowServiceName, rootInfo) { + fileContent.iServiceHFile.name = "i_%s_service.h".format(lowServiceName); + fileContent.proxyHFile.name = "%s_service_proxy.h".format(lowServiceName); + fileContent.stubHFile.name = "%s_service_stub.h".format(lowServiceName); + fileContent.serviceHFile.name = "%s_service.h".format(lowServiceName); + fileContent.proxyCppFile.name = "%s_service_proxy.cpp".format(lowServiceName); + fileContent.stubCppFile.name = "%s_service_stub.cpp".format(lowServiceName); + fileContent.serviceCppFile.name = "%s_service.cpp".format(lowServiceName); + fileContent.clientCppFile.name = "%s_client.cpp".format(lowServiceName); + fileContent.buildGnFile.name = "BUILD.gn"; + fileContent.bundleJsonFile.name = "bundle.json"; + fileContent.profileGnFile.name = "BUILD.gn"; + fileContent.profileXmlFile.name = rootInfo.serviceId + ".xml"; + fileContent.serviceCfgFile.name = "%s_service.cfg".format(lowServiceName); + fileContent.serviceCfgGnFile.name = "BUILD.gn"; +} + +function genFunctionCode(classInfo) { + let funcList = classInfo.functions; + let genResult = {} + genResult.funcEnumStr = ""; + genResult.iServiceFuncH = ""; //i_service.h 方法定义 + genResult.proxyFuncH = ""; //proxy.h 方法定义 + genResult.stubInnerFuncH = ""; // stub.h 的inner方法定义 + genResult.proxyFuncCpp = ""; //proxy.cpp 方法实现 + genResult.stubInnerFuncMap = ""; // stub.cpp 的inner方法映射表 + genResult.stubInnerFuncCpp = ""; // stub.cpp 的inner方法实现 + genResult.serviceFuncCpp = ""; // service.cpp的方法实现 + genResult.clientFuncCpp = ""; // client.cpp 的inner方法定义 + + let enumTab = getTab(2); + let funcTab = getTab(1); + for (var i = 0; i < funcList.length; ++i) { + funcList[i].funcEnum = funcList[i].name.toUpperCase(); // remote方法的枚举值 + genResult.funcEnumStr += (i == 0) ? "" : ",\n" + enumTab; + genResult.funcEnumStr += funcList[i].funcEnum; + + let paramStr = getFuncParamStr(funcList[i].params); + genResult.iServiceFuncH += (i == 0) ? "" : "\n" + funcTab; + genResult.iServiceFuncH += "virtual %s %s(%s) = 0;".format(funcList[i].retType, funcList[i].name, paramStr); + + genResult.proxyFuncH += (i == 0) ? "" : "\n" + funcTab; + genResult.proxyFuncH += "%s %s(%s) override;".format(funcList[i].retType, funcList[i].name, paramStr); + + genResult.stubInnerFuncH += (i == 0) ? "" : "\n" + funcTab; + genResult.stubInnerFuncH += + "ErrCode %sInner(MessageParcel &data, MessageParcel &reply);".format(funcList[i].name); + + genResult.proxyFuncCpp += genProxyFunc(funcList[i], classInfo.name, paramStr); + + genResult.stubInnerFuncMap += (i == 0) ? "" : "\n" + funcTab; + genResult.stubInnerFuncMap += "innerFuncs_[%s] = &%sStub::%sInner;".format( + funcList[i].funcEnum, classInfo.name, funcList[i].name); + + genResult.stubInnerFuncCpp += genStubInnerFunc(funcList[i], classInfo.name); + genResult.serviceFuncCpp += genServiceFunc(funcList[i], classInfo.name, paramStr); + + genResult.clientFuncCpp += (i == 0) ? "" : "\n" + funcTab; + genResult.clientFuncCpp += "// proxy->%s(%s);".format(funcList[i].name, paramStr); + } + return genResult; +} + +function doGenerate(rootInfo) { + let lowServiceName = rootInfo.serviceName.toLowerCase(); + let upperServiceName = rootInfo.serviceName.toUpperCase(); + + // 生成文件名 + genFileNames(lowServiceName, rootInfo); + + // 按模板生成.h和.cpp文件内容框架 + let files = genFilesByTemplate(upperServiceName, lowServiceName, rootInfo); + + // 替换文件includes + replaceIncludes(files, rootInfo); + + // 替换namespace + replaceServiceName(files, rootInfo); + + // 替换类名 + let classInfo = rootInfo.class[0] + replaceClassName(files, classInfo); + + // 生成函数定义与实现 + genFunctions(classInfo, files); + + // 文件内容汇总 + fileContent.iServiceHFile.content = files.iServiceH; + fileContent.proxyHFile.content = files.proxyH; + fileContent.stubHFile.content = files.stubH; + fileContent.serviceHFile.content = files.serviceH; + fileContent.proxyCppFile.content = files.proxyCpp; + fileContent.stubCppFile.content = files.stubCpp; + fileContent.serviceCppFile.content = files.serviceCpp; + fileContent.clientCppFile.content = files.clientCpp; + fileContent.buildGnFile.content = files.buildGn; + fileContent.bundleJsonFile.content = files.bundleJson; + fileContent.profileGnFile.content = files.profileGn; + fileContent.profileXmlFile.content = files.profileXml; + fileContent.serviceCfgFile.content = files.serviceCfg; + fileContent.serviceCfgGnFile.content = files.serviceGnCfg; + return fileContent; +} + +module.exports = { + doGenerate +} diff --git a/service-gen/src/gen/header_parser.py b/service-gen/src/gen/header_parser.py new file mode 100644 index 00000000..274c51de --- /dev/null +++ b/service-gen/src/gen/header_parser.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd. +# +# HDF is dual licensed: you can use it either under the terms of +# the GPL, or the BSD license, at your option. +# See the LICENSE file in the root of this repository for complete details. + +import json +import sys +import CppHeaderParser + +if __name__ == "__main__": + fileName = sys.argv[1]; + try: + hjson = json.loads(CppHeaderParser.CppHeader(fileName).toJSON()) + print(json.dumps({ + "result": hjson + })) + except CppHeaderParser.CppParseError: + print(CppHeaderParser.CppParseError) + finally: + pass \ No newline at end of file diff --git a/service-gen/src/gen/main.js b/service-gen/src/gen/main.js new file mode 100644 index 00000000..1286825c --- /dev/null +++ b/service-gen/src/gen/main.js @@ -0,0 +1,102 @@ +/* +* 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 path = require("path"); +const stdio = require("stdio"); +const fs = require('fs'); + +const { NapiLog } = require("../tools/NapiLog"); +const { print } = require("../tools/tool"); +const analyze = require("./analyze"); + +let ops = stdio.getopt({ + 'filename': { key: 'f', args: 1, description: ".d.ts file", default: "" }, + 'directory': { key: 'd', args: 1, description: ".d.ts directory", default: "" }, + 'out': { key: 'o', args: 1, description: "output directory", default: "." }, + 'loglevel': { key: 'l', args: 1, description: "Log Level: 0~3", default: "1" }, + 'serviceId': { key: 's', args: 1, description: "service register id: 9000~16777214", default: "9000" } +}); + +NapiLog.init(ops.loglevel, path.join("" + ops.out, "napi_gen.log")); + +let fileNames = ops.filename; +var pathDir = ops.directory; +if (fileNames == null && pathDir == null) { + NapiLog.logInfo("fileNames and pathDir both cannot be empty at the same time"); +} else if (pathDir != '') { + readDirFiles(); +} else if (fileNames != '') { + readFiles(); +} + +function readFiles() { + fileNames = fileNames.replace(/(^\s*)|(\s*$)/g, ''); // trim before and after espace + let regex = ','; + let filenameArray = fileNames.toString().split(regex); + + let n = filenameArray.length; + for (let i = 0; i < n; i++) { + let fileName = filenameArray[i]; + if (fileName !== ' ') { + fileName = fileName.replace(/(^\s*)|(\s*$)/g, ''); + checkGenerate(fileName); + } + } +} + +function readDirFiles() { + fs.readdir(pathDir + '', function (err, files) { + if (err) { + NapiLog.logError('readdir file error' + err); + return; + } + (function iterator(i) { + if (i === files.length) { + return; + } + fs.stat(path.join(pathDir + '', files[i]), function (err, data) { + if (err) { + NapiLog.logError('read file error' + err); + return; + } + if (data.isFile()) { + let fileName = files[i]; + checkGenerate(pathDir + '/' + fileName); + } + iterator(i + 1); + }); + })(0); + }); +} + +function checkGenerate(fileName) { + NapiLog.logInfo("check file []".format(fileName)); + let suffix = fileName.split('.').pop().toLowerCase(); + if (suffix === 'h') { + NapiLog.logInfo("Generating service code from file " + fileName); + analyze.doAnalyze(fileName, ops); + } else { + NapiLog.logError('Only .h file is supported.'); + } +} + +let ret = NapiLog.getResult(); +if (ret[0]) { + print('success'); + NapiLog.logInfo('success'); +} +else { + print('Finish with error: ' + ret[1]); + NapiLog.logInfo('Finish with error: ' + ret[1]); +} diff --git a/service-gen/src/tools/FileRW.js b/service-gen/src/tools/FileRW.js new file mode 100644 index 00000000..18acb588 --- /dev/null +++ b/service-gen/src/tools/FileRW.js @@ -0,0 +1,149 @@ +/* +* 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 fs = require('fs'); + +function utf8ArrayToStr(array) { + var out, i, len, c; + var char2, char3; + + out = ""; + len = array.length; + i = 0; + while (i < len) { + c = array[i++]; + switch (c >> 4) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + // 0xxxxxxx + out += String.fromCharCode(c); + break; + case 12: case 13: + // 110x xxxx 10xx xxxx + char2 = array[i++]; + out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = array[i++]; + char3 = array[i++]; + out += String.fromCharCode(((c & 0x0F) << 12) | + ((char2 & 0x3F) << 6) | + ((char3 & 0x3F) << 0)); + break; + } + } + + return out; +} + +function stringToUint8Array(string, options = { stream: false }) { + if (options.stream) { + throw new Error(`Failed to encode: the 'stream' option is unsupported.`); + } + let pos = 0; + const len = string.length; + let at = 0; // output position + let tlen = Math.max(32, len + (len >> 1) + 7); // 1.5x size + let target = new Uint8Array((tlen >> 3) << 3); // ... but at 8 byte offset + + while (pos < len) { + let value = string.charCodeAt(pos++); + let isContinue = false; + if (value >= 0xd800 && value <= 0xdbff) { + if (pos < len) {// high surrogate + const extra = string.charCodeAt(pos); + if ((extra & 0xfc00) === 0xdc00) { + ++pos; + value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000; + } + } + if (value >= 0xd800 && value <= 0xdbff) { + isContinue = true; // drop lone surrogate + } + } + + if (!isContinue) { + // expand the buffer if we couldn't write 4 bytes + if (at + 4 > target.length) { + tlen += 8; // minimum extra + tlen *= (1.0 + (pos / string.length) * 2); // take 2x the remaining + tlen = (tlen >> 3) << 3; // 8 byte offset + + target = uint8Array(tlen, target); + } + + let calculateResult = calculate(value, target, at) + isContinue = calculateResult[0] + target = calculateResult[1] + at = calculateResult[2] + } + } + return target.slice(0, at); +} + +function calculate(value, target, at) { + let isContinue = false + if ((value & 0xffffff80) === 0) { // 1-byte + target[at++] = value; // ASCII + isContinue = true; + } else if ((value & 0xfffff800) === 0) { // 2-byte + target[at++] = ((value >> 6) & 0x1f) | 0xc0; + } else if ((value & 0xffff0000) === 0) { // 3-byte + target[at++] = ((value >> 12) & 0x0f) | 0xe0; + target[at++] = ((value >> 6) & 0x3f) | 0x80; + } else if ((value & 0xffe00000) === 0) { // 4-byte + target[at++] = ((value >> 18) & 0x07) | 0xf0; + target[at++] = ((value >> 12) & 0x3f) | 0x80; + target[at++] = ((value >> 6) & 0x3f) | 0x80; + } else { + isContinue = true; + } + if (!isContinue) { + target[at++] = (value & 0x3f) | 0x80; + } + return [isContinue, target, at] +} + +function uint8Array(tlen, target) { + const update = new Uint8Array(tlen); + update.set(target); + return update +} + +function readFile(fn) { + if (!fs.existsSync(fn)) { + return ""; + } + let data = fs.readFileSync(fn); + data = utf8ArrayToStr(data); + return data; +} +function writeFile(fn, str) { + let data = stringToUint8Array(str); + fs.writeFileSync(fn, data); +} + +function createFolder(path) { + fs.mkdir(path, {recursive:true}, (err) => { + if (err) { + return console.error(err); + } + }) +} + +module.exports = { + readFile, + writeFile, + createFolder +} \ No newline at end of file diff --git a/service-gen/src/tools/NapiLog.js b/service-gen/src/tools/NapiLog.js new file mode 100644 index 00000000..6da323d9 --- /dev/null +++ b/service-gen/src/tools/NapiLog.js @@ -0,0 +1,125 @@ +/* +* 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 fs = require('fs'); +const path = require("path"); +let vscode = null; +try { + vscode = require('vscode'); +} +catch (err) { + vscode = null; +} + +class NapiLog { + constructor() { + } +} +NapiLog.LEV_NONE = 0; +NapiLog.LEV_ERROR = 1; +NapiLog.LEV_DEBUG = 2; +NapiLog.LEV_INFO = 3; + +const LEV_STR = ["[NON]", "[ERR]", "[DBG]", "[INF]"] +var logLevel = NapiLog.LEV_ERROR; +var logFileName = null; +var logResultMessage = [true, ""] + +function getDateString() { + let nowDate = new Date(); + return nowDate.toLocaleString(); +} + +function saveLog(dateStr, levStr, detail) { + if (logFileName) { + let logStr = dateStr + " " + levStr + " " + detail + "\n"; + fs.appendFileSync(logFileName, logStr); + } +} + +NapiLog.init = function (level, fileName) { + logLevel = level in [NapiLog.LEV_NONE, NapiLog.LEV_ERROR, NapiLog.LEV_DEBUG, NapiLog.LEV_INFO] + ? level : NapiLog.LEV_ERROR; + logFileName = fileName ? fileName : "napi_generator.log"; +} + +function getCallPath() { + let callPath = "" + let stackArray = new Error().stack.split('\n'); + for (let i = stackArray.length -1; i >=0 ; --i) { + if (stackArray[i].indexOf("NapiLog.log") > 0 || stackArray[i].indexOf("Function.log") > 0) { + let stackMsg = stackArray[i+1].trim() + let leftIndex = stackMsg.indexOf("(") + let rightIndex = stackMsg.indexOf(")") + + if (leftIndex > 0 && rightIndex > 0) { + let funInfo = stackMsg.substring(0, leftIndex); + let srcPath = stackMsg.substring(leftIndex + 1, rightIndex) + let colNumIndex = srcPath.lastIndexOf(":") + let colNum = srcPath.substring(colNumIndex + 1, srcPath.length) + let lineNumIndex = srcPath.lastIndexOf(":", colNumIndex - 1) + let lineNum = srcPath.substring(lineNumIndex + 1, colNumIndex) + let filePath = srcPath.substring(0, lineNumIndex) + + callPath = "%s[%s(%s:%s)]".format(funInfo,filePath,lineNum,colNum) + } + break; + } + } + + return callPath; +} + +function print(...args) { + if (vscode) { + vscode.window.showInformationMessage(...args); + } + console.log(args + ""); +} + +function recordLog(lev, ...args) { + let origMsgInfo = args; + let callPath = getCallPath(); + let dataStr = getDateString(); + let detail = args.join(" "); + saveLog(dataStr + " " + callPath, LEV_STR[lev], detail); + if (lev == NapiLog.LEV_ERROR) { + logResultMessage = [false, detail]; + } + let logStr = callPath + " " + detail; + if (logLevel <= lev) return logStr; + NapiLog.logInfo(origMsgInfo[0]); + return logStr; +} + +NapiLog.logError = function (...args) { + let logInfo = recordLog(NapiLog.LEV_ERROR, args); + print(logInfo); +} + +NapiLog.logDebug = function (...args) { + recordLog(NapiLog.LEV_DEBUG, args); +} + +NapiLog.logInfo = function (...args) { + recordLog(NapiLog.LEV_INFO, args); +} + +NapiLog.getResult = function () { + return logResultMessage; +} + +module.exports = { + NapiLog +} \ No newline at end of file diff --git a/service-gen/src/tools/common.js b/service-gen/src/tools/common.js new file mode 100644 index 00000000..c9914c6d --- /dev/null +++ b/service-gen/src/tools/common.js @@ -0,0 +1,49 @@ +/* +* 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. +*/ +// remote消息中变量类型名(key)与对应的写parcel方法名(value)的映射(参考parcel.h) +const DATA_W_MAP = new Map( + [["bool", "WriteBoolUnaligned"], ["int8_t", "WriteInt8Unaligned"], ["uint8_t", "ReadUint8Unaligned"], + ["int16_t", "WriteInt16Unaligned"], ["uint16_t", "WriteUint16Unaligned"], + ["int32_t", "WriteInt32"], ["uint32_t", "WriteUint32"], ["int64_t", "WriteInt64"], ["uint64_t", "WriteUint64"], + ["float", "WriteFloat"], ["double", "WriteDouble"], ["char *", "WriteCString"], ["std::string", "WriteString"], + ["string", "WriteString"] +]); + +// remote消息中变量类型名(key)与对应的读parcel方法名(value)的映射(参考parcel.h) +const DATA_R_MAP = new Map( + [["bool", "ReadBoolUnaligned"], ["int8_t", "ReadInt8Unaligned"], ["uint8_t", "ReadUint8Unaligned"], + ["int16_t", "ReadInt16Unaligned"], ["uint16_t", "ReadUint16Unaligned"], + ["int32_t", "ReadInt32"], ["uint32_t", "ReadUint32"], ["int64_t", "ReadInt64"], ["uint64_t", "ReadUint64"], + ["float", "ReadFloat"], ["double", "ReadDouble"], ["char *", "ReadCString"], ["std::string", "ReadString"], + ["string", "ReadString"] +]); + +// 常用类型转换表, 将C语言常见类型(key)转换为remote data读写函数使用的类型(value) +// 例如 ErrCode 类型在框架中的系统原型为int类型,这里映射成int32_t, +// 因为int32_t类型在 DATA_W_MAP/DATA_R_MAP 表中有对应的读写数据方法(WriteInt32/ReadInt32) +const TYPE_DEF_MAP = new Map( + [["ErrCode", "int32_t"], ["char", "int8_t"], ["short", "int16_t"], ["int", "int32_t"], ["long", "int64_t"], + ["unsigned char", "uint8_t"], ["unsigned short", "uint16_t"], ["unsigned int", "uint32_t"], + ["unsigned long", "uint64_t"] +]); + +function getParcelType(srcType) { + let parcelType = TYPE_DEF_MAP.get(srcType); + return parcelType === undefined ? srcType : parcelType; +} + +module.exports = { + DATA_W_MAP, DATA_R_MAP, getParcelType +} \ No newline at end of file diff --git a/service-gen/src/tools/re.js b/service-gen/src/tools/re.js new file mode 100644 index 00000000..4b81144f --- /dev/null +++ b/service-gen/src/tools/re.js @@ -0,0 +1,80 @@ +/* +* 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 path = require('path'); + +function search(ss, data) { + ss = replaceAll(ss, "\\.", "\\.") + let reg = new RegExp(ss); + let tt = reg.exec(data); + if (tt == null) return null; + let ret = { "regs": [] } + for (let i = 0; i < tt.length; i++) { + let p = data.indexOf(tt[i]); + if (tt[i] == null) { + ret["regs"].push([-1, -1]); + } + else { + ret["regs"].push([p, p + tt[i].length]); + } + } + + return ret; +} + +function match(ss, data) { + let tt = search(ss, data) + if (tt != null && tt.regs[0][0] == 0) return tt; + return null; +} + +function removeReg(data, reg) { + return data.substring(0, reg[0]) + data.substring(reg[1], data.length); +} + +function getReg(data, reg) { + return data.substring(reg[0], reg[1]); +} + +function getFileInPath(tpath) { + return path.parse(tpath).base; +} + +function getPathInPath(tpath) { + return path.parse(tpath).dir; +} + +function all(sfrom) { + return new RegExp(sfrom, "g"); +} + +function replaceAll(ss, sfrom, sto) { + return ss.replace(all(sfrom), sto); +} + +function pathJoin(...args) { + return path.join(...args); +} + +module.exports = { + search, + match, + removeReg, + getReg, + getFileInPath, + getPathInPath, + pathJoin, + replaceAll, + all +} \ No newline at end of file diff --git a/service-gen/src/tools/tool.js b/service-gen/src/tools/tool.js new file mode 100644 index 00000000..c22d0785 --- /dev/null +++ b/service-gen/src/tools/tool.js @@ -0,0 +1,68 @@ +/* +* 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. +*/ +let vscode = null; +try { + vscode = require('vscode'); +} +catch (err) { + vscode = null; +} + +function print(...args) { + if (vscode) { + vscode.window.showInformationMessage(...args); + } + console.log(...args); +} + +String.prototype.format = function (...args) { + var result = this; + let reg = new RegExp("%[sd]{1}") + for (let i = 0; i < args.length; i++) { + let p = result.search(reg); + if (p < 0) break; + result = result.substring(0, p) + args[i] + result.substring(p + 2, result.length); + } + return result; +} + +String.prototype.replaceAll = function (...args) { + let result = this; + while (result.indexOf(args[0]) >= 0) { + result = result.replace(args[0], args[1]); + } + return result; +} + +function replaceAll(s, sfrom, sto) { + while (s.indexOf(sfrom) >= 0) { + s = s.replace(sfrom, sto); + } + return s; +} + +function getTab(tabLv) { + let tab = ""; + for(var i = 0; i < tabLv; ++i) { + tab += " "; + } + return tab; +} + +module.exports = { + replaceAll, + print, + getTab +} -- Gitee