diff --git a/hdc/appCodeGen/src/analyze.js b/hdc/appCodeGen/src/analyze.js new file mode 100755 index 0000000000000000000000000000000000000000..e8e71a7531fbc45bee1de740a65d00de7669a848 --- /dev/null +++ b/hdc/appCodeGen/src/analyze.js @@ -0,0 +1,181 @@ +/* +* 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 re = require("./tools/re"); +const { removeExplains, removeEmptyLine, replaceTab, checkOutBody, getLicense } = require("./tools/tool"); +const { NumberIncrease } = require("./tools/common"); +const { readFile } = require("./tools/FileRW"); + +const { analyzeNamespace } = require("./analyze/namespace"); +const { NapiLog } = require("./tools/NapiLog"); + +function analyzeFile(fn) { + NumberIncrease.reset(); + let data = readFile(fn); + let licenseData = getLicense(data) + // 去除注释 + data = removeExplains(data) + // 去除空行 + data = removeEmptyLine(data) + // 去除Tab + data = replaceTab(data) + let result = { + exportDefault: [], + exports: [], + imports:[], + declareType: [], + declareFunction: [], + declareNamespace: [], + declareInterface: [], + declareLicense: [], + } + while (true) { + // import + let matchImport = re.search("import ([{}A-Za-z ,]+) from [\"']{1}([@_./a-zA-Z]+)[\"']{1};*", data); + if (matchImport != null) { + result.imports.push(re.getReg(data, matchImport.regs[0])) + data = re.removeReg(data, matchImport.regs[0]); + } + else break; + } + + if (null != licenseData) { + result.declareLicense.push(licenseData) + } + return analyze(data, result) +} + +function analyze(data, result) { + while (true) { + let oldData = data + data = removeEmptyLine(data) + let matchs = re.match(" *\n*", data) + // 只剩下空格和回车时,解析完成 + if (matchs && matchs.regs[0][1] == data.length) break + matchs = re.match("export default ([a-zA-Z0-9_]+);", data); + if (matchs != null) { + let exportName = re.getReg(data, matchs.regs[1]) + data = re.removeReg(data, matchs.regs[0]); + result.exportDefault.push(exportName) + } + data = re.replaceAll(data, "\n{", "{"); + let matchType = analyzeMatchType(matchs, data, result) + if (matchType != null) { + data = matchType[0] + if (matchType[1] != null) { + result = matchType[1] + } + } + let namespace = analyzeMatchNamespace(matchs, data, result) + if (namespace != null) { + data = namespace[0] + result = namespace[1] + } + let interface = analyzeMatchInterface(matchs, data, result) + if (interface != null) { + data = interface[0] + result = interface[1] + } + let functionMatch = analyzeMatchFunction(matchs, data, result) + if (functionMatch != null) { + data = functionMatch[0] + result = functionMatch[1] + } + if (oldData == data) { + NapiLog.logError("\nvvv 解析文件失败 vvv"); + NapiLog.logError("[", data.substring(0, data.length > 64 ? 64 : data.length), "]"); + break; + } + } + return result +} + +function analyzeMatchNamespace(matchs, data, result) { + matchs = re.match("declare namespace ([a-zA-Z_0-9]+) *({)", data); + // 解析declare + if (matchs != null) { + let namespaceName = re.getReg(data, matchs.regs[1]) + let namespaceData = checkOutBody(data, matchs.regs[2][0], null, true) + data = data.substring(matchs.regs[2][1] + namespaceData.length + 1, data.length) + result.declareNamespace.push({ + name: namespaceName, + body: analyzeNamespace(namespaceData) + }) + } + return [data, result] +} + +function analyzeMatchInterface(matchs, data, result) { + matchs = re.match("(export )*(declare )*interface ([A-Za-z_0-9<>= ]+) (extends [a-zA-Z]+ )*({)", data) + if (matchs) { + let interfaceName = re.getReg(data, matchs.regs[3]) + let interfaceData = checkOutBody(data, matchs.regs[5][0], null, true) + data = data.substring(matchs.regs[5][1] + interfaceData.length + 1, data.length) + result.declareInterface.push({ + name: interfaceName, + body: {} + }) + } + return [data, result] +} + +function analyzeMatchFunction(matchs, data, result) { + matchs = re.match("declare function ([A-Za-z0-9_]+)\\(([\n a-zA-Z:;=,_0-9?<>{}|]*)\\) *:" + + "*([A-Za-z0-9_<>{}:, .]+);*", data) + if (matchs) { + let functionName = re.getReg(data, matchs.regs[1]) + let functionBody = re.getReg(data, matchs.regs[2]) + data = re.removeReg(data, matchs.regs[0]) + result.declareFunction.push({ + name: functionName, + body: functionBody + }) + } + return [data, result] +} + +function analyzeMatchType(matchs, data, result) { + matchs = re.match("(export )*type ([a-zA-Z]+) *= *([()a-zA-Z :=>,\"| ]+);", data) + if (matchs) { + let exportName = re.getReg(data, matchs.regs[2]) + let exportBody = re.getReg(data, matchs.regs[3]) + data = re.removeReg(data, matchs.regs[0]); + result.declareType.push({ + name: exportName, + body: exportBody + }) + if (matchs.regs[1][0] != -1) { + result.exports.push(exportName) + } + } + + matchs = re.match("(export )*type ([a-zA-Z]+) *= *(\n{)", data) + if (matchs) { + let exportName = re.getReg(data, matchs.regs[2]) + let exportBody = checkOutBody(data, matchs.regs[3][0], null, true) + data = data.substring(matchs.regs[3][1] + exportBody.length + 2, data.length) + result.declareType.push({ + name: exportName, + body: exportBody + }) + if (matchs.regs[1][0] != -1) { + result.exports.push(exportName) + } + } + return [data, result] +} + +module.exports = { + analyzeFile +} \ No newline at end of file diff --git a/hdc/appCodeGen/src/analyze/enum.js b/hdc/appCodeGen/src/analyze/enum.js new file mode 100755 index 0000000000000000000000000000000000000000..a7f288080c50b515caee2ee2375824b5c3fdc893 --- /dev/null +++ b/hdc/appCodeGen/src/analyze/enum.js @@ -0,0 +1,91 @@ +/* +* 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 { NumberIncrease } = require("../tools/common"); +const re = require("../tools/re"); + +/** Enum解析 */ +function analyzeEnum(data) { + let body = re.replaceAll(data, "\n", "").split(",") + let result = { + element: [], + function: [], + enumValueType: 0 // 0代表数字,1代表字符串 + } + for (let i in body) { + let bodyContent = body[i] + while (bodyContent.length > 0 && bodyContent[0] == ' ') { + bodyContent = bodyContent.substring(1, bodyContent.length) + } + while (bodyContent.length > 0 && bodyContent[-1] == ' ') { + bodyContent = bodyContent.substring(0, bodyContent.length - 1) + } + if (bodyContent == "") { + break + } + analyzeEnumResult(result, bodyContent, i) + } + return result +} + +function analyzeEnumResult(result, bodyContent, index) { + let regString = re.match(" *([a-zA-Z0-9_]+) * = *\"([\x21-\x7e]+)*\"", bodyContent) + let regSingleQuotes = re.match(" *([a-zA-Z0-9_]+) * = *'([\x21-\x7e]+)*'", bodyContent) + let regNumber = re.match(" *([a-zA-Z0-9_]+) * = *([a-zA-Z_0-9<>-]+)", bodyContent) + let reg = re.match(" *([a-zA-Z0-9_]+) *", bodyContent) + if (regString) { + let elementName = re.getReg(bodyContent, regString.regs[1]) + let elementValue = re.getReg(bodyContent, regString.regs[2]) + result.element.push({ + name: elementName, + value: elementValue, + type: 'string' + }) + result.enumValueType = 1 + } else if (regSingleQuotes) { + let elementName = re.getReg(bodyContent, regSingleQuotes.regs[1]) + let elementValue = re.getReg(bodyContent, regSingleQuotes.regs[2]) + result.element.push({ + name: elementName, + value: elementValue, + type: 'string' + }) + result.enumValueType = 1 + } else if (regNumber) { + let elementName = re.getReg(bodyContent, regNumber.regs[1]) + let elementValue = re.getReg(bodyContent, regNumber.regs[2]) + typeof (elementValue) + result.element.push({ + name: elementName, + value: elementValue, + type: "NUMBER_TYPE_" + NumberIncrease.getAndIncrease() + }) + result.enumValueType = 0 + } else if (reg) { + let elementName = re.getReg(bodyContent, reg.regs[1]) + let elementValue = index + result.element.push({ + name: elementName, + value: elementValue, + type: "NUMBER_TYPE_" + NumberIncrease.getAndIncrease() + }) + result.enumValueType = 0 + } + return result +} + +module.exports = { + analyzeEnum, + analyzeEnumResult +} \ No newline at end of file diff --git a/hdc/appCodeGen/src/analyze/function.js b/hdc/appCodeGen/src/analyze/function.js new file mode 100755 index 0000000000000000000000000000000000000000..c8bf59e15b965971732712a693156f457f4d9e5e --- /dev/null +++ b/hdc/appCodeGen/src/analyze/function.js @@ -0,0 +1,277 @@ +/* +* 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 re = require("../tools/re"); +const { FuncType, NumberIncrease, isEnum, EnumValueType, enumIndex, isType, typeIndex, isOnObjCallback, + getOnObjCallbackType, getLogErrInfo } = require("../tools/common"); +const { analyzeParams } = require("./params"); +const { analyzeReturn } = require("./return"); +const { NapiLog } = require("../tools/NapiLog"); +const { randomInt } = require("crypto"); +const { print } = require("../tools/tool"); + +function analyzeSubInterface(data) { + let body = re.replaceAll(data, "\n", "").split(";") // # replace(" ", ""). + let result = { + value: [], + function: [], + parentNameList: [], + childList: [], + parentList: [] + } + for (let i in body) { + let t = body[i] + while (t.length > 0 && t[0] === ' ') // 去除前面的空格 + t = t.substring(1, t.length) + while (t.length > 0 && t[-1] === ' ') // 去除后面的空格 + t = t.substring(0, t.length - 1) + if (t === "") break // 如果t为空直接返回 + let tt = re.match(" *([a-zA-Z0-9_]+) *: *([a-zA-Z_0-9<>,:{}[\\] ]+)", t) + if (tt) { // 变量 + + let valueName = re.getReg(t, tt.regs[1]) + let valueType = re.getReg(t, tt.regs[2]) + let index = valueType.indexOf("number") + while (index !== -1) { + valueType = valueType.replace("number", "NUMBER_TYPE_" + NumberIncrease.getAndIncrease()) + index = valueType.indexOf("number") + } + result.value.push({ + name: valueName, + type: valueType + }) + } + } + return result +} + +function getFuncParaType(v, interfaceName, data, results) { + let arrayType = re.match("(Async)*Callback<(Array<([a-zA-Z_0-9]+)>)>", v["type"]) + let parameter = v["type"] + if (arrayType) { + parameter = re.getReg(v["type"], arrayType.regs[2]) + } + if (isEnum(parameter, data)) { + let index = enumIndex(parameter, data) + if (data.enum[index].body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_NUMBER) { + v["type"] = v["type"].replace(parameter, "NUMBER_TYPE_" + NumberIncrease.getAndIncrease()) + } else if (data.enum[index].body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_STRING) { + v["type"] = v["type"].replace(parameter, "string") + } else { + NapiLog.logError("analyzeFunction getFuncParaType is not support this type %s." + .format(data.enum[index].body.enumValueType), getLogErrInfo); + return null + } + } + // interface & class中的方法参数类型是enum的情况 + else if (isEnum(parameter, results)) { + let index = enumIndex(parameter, results) + if (results.enum[index].body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_NUMBER) { + v["type"] = v["type"].replace(parameter, "NUMBER_TYPE_" + NumberIncrease.getAndIncrease()) + } else if (results.enum[index].body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_STRING) { + v["type"] = v["type"].replace(parameter, "string") + } else { + NapiLog.logError("analyzeFunction getFuncParaType is not support this type %s." + .format(results.enum[index].body.enumValueType), getLogErrInfo()); + return null + } + } + + let interfaceType = re.match("{([A-Za-z0-9_]+:[A-Za-z0-9_,]+)([A-Za-z0-9_]+:[A-Za-z0-9_]+)}$", v["type"]) + if (interfaceType) { + v["type"] = interfaceName + } + + if (parameter.indexOf("number") >= 0) { + v["type"] = v["type"].replace("number", "NUMBER_TYPE_" + NumberIncrease.getAndIncrease()) + } + + // type的处理 + if (isType(parameter, data)) { + let index = typeIndex(parameter, data) + if (data.type[index].isEnum) { + v["type"] = v["type"].replace(parameter, "string") + } + } + return v +} + +function analyzeFuncNoNameInterface(data, values, results) { + values = re.replaceAll(re.replaceAll(values, " ", ""), "\n", "") + let interfaceName = "" + let matchNoName = "([:{<}>,;a-zA-Z_0-9]*)\\?*(:[A-Za-z0-9_,;]*)?:((Async)*Callback<)?{(([A-Za-z0-9_]+:"+ + "[A-Za-z0-9_,;]+)*)([A-Za-z0-9_]+:[A-Za-z0-9_]+)}(}|,|;|>)?$" + let matchs = re.match(matchNoName, values) + if (matchs) { + let st = values.lastIndexOf("{") + let end = values.indexOf("}") + let number = NumberIncrease.getAndIncrease(); + interfaceName = "AUTO_INTERFACE_%s".format(number) + let interfaceBody = values.substring(st+1, end) + let typeInterface = "{%s}".format(interfaceBody) + values = re.replaceAll(values, typeInterface, interfaceName) + interfaceBody = re.replaceAll(interfaceBody, ",", ";") + if (Object.prototype.hasOwnProperty.call(data, "interface")) { + data.interface.push({ + name: interfaceName, + body: analyzeSubInterface(interfaceBody) + }) + } else if (Object.prototype.hasOwnProperty.call(results, "interface")) { + results.interface.push({ + name: interfaceName, + body: analyzeSubInterface(interfaceBody) + }) + } + } + + matchs = re.match(matchNoName, values) + if(matchs) { + let resNoNameInter = analyzeFuncNoNameInterface(data, values) + values = resNoNameInter.values + } + + let result = { + interfaceName: interfaceName, + values: values + } + return result +} + +function analyseSubReturn(ret, data, results) { + // 匿名interface返回值 function fun4(input: string): { read: number; written: number }; + let tt = null + if (ret.indexOf(":") > 0) { + ret = re.replaceAll(re.replaceAll(ret, " ", ""), "\n", "") + ret = re.replaceAll(ret, ",", ";") + ret = ret.substring(1, ret.length - 1) + tt = ret.split(";") + } + if (tt) { + let len = tt.length + let res = "" + let interfaceName = "" + for (let i=0; i= 0) { + ret = ret.replaceAll("number", "NUMBER_TYPE_" + NumberIncrease.getAndIncrease()) + } + return ret +} + +function getObjCallFunc(results, onObjCbType, values, ret) { + if (results !== undefined) { + results.callFunction.push({ + "name": onObjCbType, + "body": values, + "ret": ret + }) + } +} +function getFuncResult(name, funcType, values, ret, isStatic) { + let result = { + name: name, + type: funcType, + value: values, + ret: ret, + isStatic: isStatic + } + return result +} + +function getArrowCallFunc(tmp, results) { + let callbackFunc = null + + if (tmp[2][0] !== undefined) { + callbackFunc = tmp[2][0] // 当方法的参数是回调方法,并且回调方法写法为=>函数 + } + if (results !== undefined && callbackFunc !== null) { + results.callFunction.push(callbackFunc) + } +} + +/**函数解析 */ +function analyzeFunction(data, isStatic, name, values, ret, results, interfaceName = '') { + let res = analyzeFuncNoNameInterface(data, values, results) + let tmp + let funcType + + if (res) { + tmp = analyzeParams(name, res.values) + if (tmp !== null) { + values = tmp[0] + funcType = tmp[1] + getArrowCallFunc(tmp, results) + } + } + + tmp = analyzeReturn(ret) + ret = tmp[0] + if (tmp[1]) { // 返回类型为 Promise, 解析成等价的AsyncCallback方法 + funcType = FuncType.ASYNC + // 返回值是Promise的匿名interface + let paramTypeVal = analyseSubReturn(ret.substring(8, ret.length - 1), data, results); + // 将返回值Promise改为AsyncCallback,作为方法的入参 + let paramType = ret.replace("Promise", "AsyncCallback") + if (paramTypeVal) { + // 匿名interface处理 + let paramAsync = paramType.substring(14, paramType.length - 1) + paramType = paramType.replace(paramAsync, paramTypeVal); + } + values.push({name: "promise", optional: false, type: paramType}) + ret = "void" // 返回值由Promise改为void,与AsyncCallback接口保持一致 + } + for (let j in values) { + let v = values[j] + v = getFuncParaType(v, res.interfaceName, data, results) + if (v === null) { + NapiLog.logError("analyzeFunction is not support this type %s.".format(v), getLogErrInfo()); + } + } + ret = analyseSubReturn(ret, data, results) + let result = getFuncResult(name, funcType, values, ret, isStatic) + if (isOnObjCallback(name)) { + let onObjCbType = getOnObjCallbackType(name, interfaceName) + getObjCallFunc(results, onObjCbType, values, ret) + } + return result +} + +module.exports = { + analyzeFunction, + analyzeSubInterface, + getFuncParaType, + analyzeFuncNoNameInterface, + analyseSubReturn +} \ No newline at end of file diff --git a/hdc/appCodeGen/src/analyze/interface.js b/hdc/appCodeGen/src/analyze/interface.js new file mode 100755 index 0000000000000000000000000000000000000000..1796e486fcf2a37e85f43078792bbbb4aeba7595 --- /dev/null +++ b/hdc/appCodeGen/src/analyze/interface.js @@ -0,0 +1,108 @@ +/* +* 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 re = require("../tools/re"); +const { NumberIncrease } = require("../tools/common"); +const { addUniqFunc2List } = require("../tools/tool"); +const { analyzeFunction } = require("./function"); + +/* 匿名interface */ +function analyzeNoNameInterface(valueType, valueName, rsltInterface) { + valueType = re.replaceAll(valueType, " ", "") + let matchs = re.match("{(([A-Za-z0-9_]+:[A-Za-z0-9_,;]+)*)([A-Za-z0-9_]+:[A-Za-z0-9_]+)}$", valueType) + if (matchs) { + let number = NumberIncrease.getAndIncrease(); + let interfaceTypeName = 'AUTO_INTERFACE_%s_%s'.format(valueName, number) + let interfaceBody = valueType.substring(1, valueType.length-1) + interfaceBody = re.replaceAll(interfaceBody, ",", ";\n") + rsltInterface.push({ + name: interfaceTypeName, + body: analyzeInterface(interfaceBody, rsltInterface) + }) + valueType = interfaceTypeName + } + return valueType +} + +/* 去除单行注释// */ +function parseNotes(data) { + let notes = data.indexOf("//") >= 0 ? data.substring(data.indexOf("//"), data.length) : ""; + while(notes != "") { + notes = notes.substring(0, notes.indexOf("\n")); + data = data.replace(notes, ""); + notes = "" + let st = data.indexOf("//"); + if(st >= 0) { + notes = data.substring(st, data.length); + } + } + return data +} + +/**interface解析 */ +function analyzeInterface(data, rsltInterface = null, results, interfaceName = '') { // same as class + let body = data + body = body.indexOf("//") < 0 ? body : parseNotes(body) + let arr = [...body.matchAll(/;\s*\n+/g)] + for (let i = 0; i < arr.length; i++) { + let result = arr[i] + body = re.replaceAll(body, result[0], ";\n") + } + body = body.split(";\n") + let result = { + value: [], + function: [] + } + for (let i in body) { + let t = body[i] + t = re.replaceAll(t, "\n", "") + while (t.length > 0 && t[0] == ' ') t = t.substring(1, t.length) // 去除前面的空格 + while (t.length > 0 && t[-1] == ' ') t = t.substring(0, t.length - 1) // 去除后面的空格 + if (t == "") break // 如果t为空直接返回 + let tt = re.match(" *([a-zA-Z0-9_]+)(\\?*)*: *([a-zA-Z_0-9<>,:{}[\\]| ]+)", t) + if (tt && t.indexOf("=>") < 0) { // 接口成员变量, 但不包括带'=>'的成员,带'=>'的接口成员需要按函数处理 + let valueName = re.getReg(t, tt.regs[1]) + let valueType = re.getReg(t, tt.regs[3]) + let index = valueType.indexOf("number") + let optionalFlag = re.getReg(t, tt.regs[2]) == '?' ? true : false; + while (index !== -1) { + valueType = valueType.replace("number", "NUMBER_TYPE_" + NumberIncrease.getAndIncrease()) + index = valueType.indexOf("number") + } + valueType = analyzeNoNameInterface(valueType, valueName, rsltInterface) + result.value.push({ + name: valueName, + type: valueType, + optional: optionalFlag + }) + } + tt = re.match("(static )* *(\\$*[A-Za-z0-9_]+) *[:]? *\\(([\n 'a-zA-Z\'\'\"\":;=,_0-9?<>{}()=>|[\\]]*)\\)" + + " *(:|=>)? *([A-Za-z0-9_<>{}:;, .[\\]]+)?", t) + if (tt) { // 接口函数成员 + let ret = re.getReg(t, tt.regs[5]) == ''? 'void': re.getReg(t, tt.regs[5]) + let funcDetail = analyzeFunction(data, re.getReg(t, tt.regs[1]) != '', re.getReg(t, tt.regs[2]), + re.getReg(t, tt.regs[3]), ret, results, interfaceName) + if (funcDetail != null) { + // 完全一样的方法不重复添加 (如同名同参的AsyncCallback和Promise方法) + addUniqFunc2List(funcDetail, result.function) + } + } + } + return result +} + +module.exports = { + analyzeInterface, + parseNotes +} \ No newline at end of file diff --git a/hdc/appCodeGen/src/analyze/namespace.js b/hdc/appCodeGen/src/analyze/namespace.js new file mode 100755 index 0000000000000000000000000000000000000000..cc5c64a1f420ad04f4996540f9c261ca3756c45e --- /dev/null +++ b/hdc/appCodeGen/src/analyze/namespace.js @@ -0,0 +1,419 @@ +/* +* 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 re = require("../tools/re"); +const { removeEmptyLine, checkOutBody, addUniqFunc2List } = require("../tools/tool"); +const { analyzeFunction } = require("./function"); +const { analyzeInterface, parseNotes } = require("./interface"); +const { analyzeEnum } = require("./enum"); +const { NapiLog } = require("../tools/NapiLog"); +const { analyzeType, analyzeType2, analyzeType2Result } = require("./type"); +const { NumberIncrease, EnumValueType, getLogErrInfo } = require("../tools/common"); + +function preProcessData(data) { + data = data.indexOf("//") < 0 ? data : parseNotes(data); + data = re.replaceAll(data, "\n{", "{"); + return data; +} + +function getDataByResult(result) { + let data = null + if (result !== null) { + data = result + } + return data +} + +/**namespace解析 */ +function analyzeNamespace(data) { + let result = { + exports: [], + enum: [], + const: [], + type: [], + function: [], + interface: [], + class: [], + namespace: [], + callFunction: [], + } + while (data !== '\n') { + let oldData = data + data = removeEmptyLine(data) + let matchs = re.match(" *\n*", data) + data = preProcessData(data); + // 只剩下空格和回车时,解析完成 + if (matchs && matchs.regs[0][1] === data.length) break + let parseEnumResult = parseEnum(matchs, data, result) + data = getDataByResult(parseEnumResult) + + result = parseEnumType(result); + + let parseInterResult = parseInterface(matchs, data, result) + data = getDataByResult(parseInterResult) + + let parseFunctionResult = parseFunction(matchs, data, result) + data = getDataByResult(parseFunctionResult) + + let parseTypeResult = parseType(matchs, data, result) + data = getDataByResult(parseTypeResult) + + let parseClassResult = parseClass(matchs, data, result) + data = getDataByResult(parseClassResult) + + let parseNamespaceResult = parseNamespace(matchs, data, result) + data = getDataByResult(parseNamespaceResult) + + data = removeReg(matchs, data, result) + if (oldData === data) { + NapiLog.logError("解析Namespace失败"); + NapiLog.logError("[", data.substring(0, data.length > 128 ? 128 : data.length), "]"); + break; + } + } + return result +} + +function parseEnumType(result) { + if (null === result) { + return null; + } + + for (let i in result.enum) { + let enumm = result.enum[i] + + // interface 匹配 + for (let i in result.interface) { + let interf = result.interface[i] + if(!isValidValue(interf)) { + NapiLog.logError("parseEnumType interf is null!"); + return null; + } + + // function 匹配 + for (let j in interf.body.function) { + let func = interf.body.function[j]; + if(!isValidValue(func)) { + NapiLog.logError("parseEnumType func is null!"); + return null; + } + + // 参数匹配 + for (let k in func.value) { + let v = func.value[k]; + if(!isValidValue(v)) { + NapiLog.logError("parseEnumType func.value is null!"); + return null; + } + + if (v.type === enumm.name) { + if (enumm.body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_NUMBER) { + v.type = "NUMBER_TYPE_" + NumberIncrease.getAndIncrease(); + } else if (enumm.body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_STRING) { + v.type = "string"; + } else { + NapiLog.logError("parseEnumType for interface function value is not support this type %s." + .format(enumm.body.enumValueType), getLogErrInfo()); + return null; + } + result.interface[i].body.function[j].value[k].type = v.type; + } + } + } + } + } + return result +} + +function parseNamespace(matchs, data, result) { + matchs = re.match("(export )*namespace ([a-zA-Z0-9]+) ({)", data) + if (matchs) { + let namespaceName = re.getReg(data, matchs.regs[2]) + let namespaceBody = checkOutBody(data, matchs.regs[3][0], null, true) + result.namespace.push({ + name: namespaceName, + body: analyzeNamespace(namespaceBody) + }) + data = data.substring(matchs.regs[3][0] + namespaceBody.length + 2, data.length) + if (matchs.regs[1][0] !== -1) { + result.exports.push(namespaceName) + } + } + return data +} + +function parseClass(matchs, data, result) { + matchs = re.match( + "(export )*class ([A-Za-z_0-9]+)()* *(extends [a-zA-Z_0-9, ]+)* *(implements [a-zA-Z_0-9, ]+)* *({)" + , data) + if (matchs) { + // class类型也解析成interface结构,该结构在后面生成C++代码时会按napi_define_class处理成C++的class + return createInterfaceData(matchs, data, result) + } + return data +} + +function parseEnum(matchs, data, result) { + matchs = re.match("(export )*enum *([A-Za-z_0-9]+) *({)", data) + if (matchs !== null) { + let enumName = re.getReg(data, matchs.regs[2]); + let enumBody = checkOutBody(data, matchs.regs[3][0], null, null) + result.enum.push({ + name: enumName, + body: analyzeEnum(enumBody.substring(1, enumBody.length - 1)) + }) + data = data.substring(matchs.regs[3][0] + enumBody.length) + if (matchs.regs[1][0] !== -1) { + result.exports.push(enumName) + } + } + matchs = re.match("(export )*const ([A-Za-z_0-9]+) *[:=]{1} ([A-Za-z_0-9]+);", data) + if (matchs) { + let constName = re.getReg(data, matchs.regs[1]) + result.const.push({ + name: constName, + body: re.getReg(data, matchs.regs[2]) + }) + data = re.removeReg(data, matchs.regs[0]) + if (matchs.regs[1][0] !== -1) { + result.exports.push(constName) + } + } + return data +} + +function isValidValue(value) { + if (value === null || value === undefined) { + return false; + } + return true; +} + +function getTypeInfo(result, typeName, typeType, isEnum) { + if (!isValidValue(result) || !isValidValue(result.type)) { + NapiLog.logError("getTypeInfo: result or result.type is invalid!"); + } + result.type.push({ + name: typeName, + body: typeType, + isEnum: isEnum + }) +} + +function parseType(matchs, data, result) { + matchs = re.match("(export )*type ([a-zA-Z]+) *= *([\\(\\):=a-zA-Z<> |]+);", data) + if (matchs) { + let typeName = re.getReg(data, matchs.regs[2]); + let typeType = re.getReg(data, matchs.regs[3]); + let index = typeType.indexOf("number") + if (index !== -1) { + typeType = typeType.replace("number", "NUMBER_TYPE_" + NumberIncrease.getAndIncrease()) + } + getTypeInfo(result, typeName, typeType, false); + data = re.removeReg(data, matchs.regs[0]) + if (matchs.regs[1][0] !== -1) { + result.exports.push(typeName) + } + } + + matchs = re.match("(export )*type ([a-zA-Z]+) *= *([\\(\\):=a-zA-Z<> |\n']+);", data) + if (matchs) { + let typeName = re.getReg(data, matchs.regs[2]); + let typeBody = re.getReg(data, matchs.regs[3]); + + getTypeInfo(result, typeName, analyzeType2(typeBody.substring(1, typeBody.length - 1)), true); + data = re.removeReg(data, matchs.regs[0]) + if (matchs.regs[1][0] !== -1) { + result.exports.push(typeName) + } + } + + matchs = re.match("(export )*type ([a-zA-Z]+) *= *({)", data) + if (matchs) { + let typeName = re.getReg(data, matchs.regs[2]); + let typeBody = checkOutBody(data, matchs.regs[3][0], null, true) + if (typeBody === null) { + NapiLog.logError("ParseType typeBody is null!"); + } + let bodyObj = analyzeType(typeBody.substring(1, typeBody.length - 1), result.type) + getTypeInfo(result, typeName, bodyObj, false); + data = data.substring(matchs.regs[3][0] + typeBody.length + 2, data.length) + if (matchs.regs[1][0] !== -1) { + result.exports.push(typeName) + } + } + return data +} + +function parseFunction(matchs, data, result) { + matchs = re.match("(export )*function (\\$*[A-Za-z0-9_]+) *(\\()", data) + if (null == matchs) { + matchs = re.match("(export )*function (static )*(\\$*[A-Za-z0-9_]+) *(\\()", data) + } + if (null == matchs) { + matchs = re.match("(export )*function (static )*(register\\$*[A-Za-z0-9_]+) *(\\()", data) + } + if (matchs) { + let funcName = re.getReg(data, + matchs.regs.length === 5 ? [matchs.regs[2][0], matchs.regs[3][1]] : matchs.regs[2]) + let funcValue = checkOutBody(data, + matchs.regs.length === 5 ? matchs.regs[4][0] : matchs.regs[3][0], ["(", ")"], null) + let funcRet = checkOutBody(data.substring(matchs.regs.length === 5 ? + matchs.regs[4][0] : matchs.regs[3][0] + funcValue.length), 0, ["", "\n"], null) + data = data.substring(matchs.regs.length === 5 ? + matchs.regs[4][0] : matchs.regs[3][0] + funcValue.length + funcRet.length) + let matchFunc = re.match(" *: *([A-Za-z0-9_<>{}\\[\\]:;, .=]+);*", funcRet) + let matchFuncArray = re.match(" *: *([A-Za-z0-9]+)(\\[]);*", funcRet) + if (matchFuncArray) { + funcRet = re.getReg(funcRet, [matchFuncArray.regs[1][0], matchFuncArray.regs[2][1]]) + } + else if (matchFunc) { + funcRet = re.getReg(funcRet, matchFunc.regs[1]) + } + else { + funcRet = "void" + } + funcRet = re.replaceAll(re.replaceAll(funcRet, " ", ""), "\n", "") + + if(funcRet[funcRet.length-1] === ";"){ + funcRet = funcRet.substring(0, funcRet.length-1) + } + let funcDetail = analyzeFunction( + result, false, funcName, funcValue.substring(1, funcValue.length - 1), funcRet, result) + if (funcDetail !== null) { + // 完全一样的方法不重复添加 (如同名同参的AsyncCallback和Promise方法) + addUniqFunc2List(funcDetail, result.function) + } + if (matchs.regs[1][0] !== -1) { + result.exports.push(funcName) + } + } + return data +} + +/** + * 提取当前类继承或实现的父类名称列表 + * @param firstKey 继承/实现关键字 (extends或implements) + * @param secondKey 继承/实现关键字 (extends或implements) + * @param parentStr 正则匹配到的继承语句 (如 extends xx1, xx2 implements yy1, yy2) + * @returns 继承的名称列表 ([xx1, xx2, yy1, yy2]) + */ +function getParentNameList(firstKey, secondKey, parentStr) { + if (parentStr === '') { + return [] + } + + let firstParents = '' + let secondParents = '' + if (parentStr.indexOf(secondKey) > 0) { + // 同时出现extends和implements关键字的情况 (如 extends xx1, xx2 implements yy1, yy2) + firstParents = parentStr.split(secondKey)[0].split(firstKey)[1] + secondParents = parentStr.split(secondKey)[1].trim() + } else { + // 只有extends或implements一种关键字的情况 (如 extends xx1, xx2 或者 implements yy1, yy2) + firstParents = parentStr.split(firstKey)[1] + } + + let nameList = firstParents.split(",") + if (secondParents !== '') { + let secondList = secondParents.split(",") + nameList.push(...secondList) + } + + return nameList +} + +/** + * 创建interface数据结构 + * @param matchs 正则匹配对象 + * @param data 原始ts文件内容 + * @param result 解析后的ts数据结构 + * @returns data 原始ts文件内容中剩余未解析的部分 + */ +function createInterfaceData (matchs, data, result) { + let interfaceName = re.getReg(data, matchs.regs[2]) + let interfaceBody = checkOutBody(data, matchs.regs[6][0], null, null) + let bodyObj = analyzeInterface(interfaceBody.substring(1, interfaceBody.length - 1), result.interface, + result, interfaceName) + let extendsParent = re.getReg(data, matchs.regs[4]) + let implementParent = re.getReg(data, matchs.regs[5]) + bodyObj.parentNameList = [] + if(extendsParent !== '') { + bodyObj.parentNameList = getParentNameList("extends", "implements", extendsParent) + } + if(implementParent !== '') { + bodyObj.parentNameList = getParentNameList("implements", "extends", implementParent) + } + for (let i in bodyObj.parentNameList) { + bodyObj.parentNameList[i] = bodyObj.parentNameList[i].trim() + if (bodyObj.parentNameList[i] === interfaceName) { + // 接口不能自己继承自己 + NapiLog.logError("The interface [%s] can not extends with itself.".format(interfaceName)) + return data + } + } + + bodyObj.parentList = [] // 该接口继承的父类型列表 + bodyObj.childList = [] // 继承自该接口的子类型列表 + + result.interface.push({ + name: interfaceName, + body: bodyObj + }) + let rr = matchs.regs[6][0] + rr = matchs.regs[6][0] + interfaceBody.length + let tmp = data[rr] + data = data.substring(matchs.regs[6][0] + interfaceBody.length, data.length) + if (matchs.regs[1][0] !== -1) { + result.exports.push(interfaceName) + } + return data +} + +function parseInterface(matchs, data, result) { + matchs = re.match( + "(export )*interface ([A-Za-z_0-9]+)()* *(extends [a-zA-Z_0-9, ]+)* *(implements [a-zA-Z_0-9, ]+)* *({)" + , data) + if (matchs) { + return createInterfaceData (matchs, data, result) + } + return data +} + +function removeReg(matchs, data, result) { + matchs = re.match("export { ([a-zA-Z]+) };", data) + if (matchs) { + let exportName = re.getReg(data, matchs.regs[1]) + result.exports.push(exportName) + data = re.removeReg(data, matchs.regs[0]) + } + matchs = re.match("export import [a-zA-Z]+ = [a-zA-Z\\.]+;", data) + if (matchs) { + data = re.removeReg(data, matchs.regs[0]) + } + matchs = re.match("readonly [a-zA-Z]+: [a-z\\[\\]]+;*", data) + if (matchs) { + data = re.removeReg(data, matchs.regs[0]) + } + return data +} +module.exports = { + analyzeNamespace, + parseNamespace, + parseEnum, + parseFunction, + parseInterface, + parseClass, + parseType +} \ No newline at end of file diff --git a/hdc/appCodeGen/src/analyze/params.js b/hdc/appCodeGen/src/analyze/params.js new file mode 100755 index 0000000000000000000000000000000000000000..46b8c30fe53210cf18f1b0ffc4b55124209350f5 --- /dev/null +++ b/hdc/appCodeGen/src/analyze/params.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 re = require("../tools/re"); +const { checkOutBody, print } = require("../tools/tool"); +const { FuncType, NumberIncrease,isFuncType, isArrowFunc,isRegisterFunc } = require("../tools/common"); +const { NapiLog } = require("../tools/NapiLog"); + +function isSyncFuncType(type, funcType) { + let isSync = false; + if (funcType === FuncType.DIRECT && type.indexOf("Callback") >= 0 && type.indexOf("AsyncCallback") < 0 || + isFuncType(type) || isArrowFunc(type)) { + isSync = true; + } + return isSync; +} + +/** + * on方法中回调方法的解析 + * @param {*} valueType 回调方法体 + * @param {*} valueName 参数名 + * @param {*} rsltCallFunction 解析结果 + */ +function analyzeCallbackFunction(valueType, valueName, rsltCallFunction) { + + if (valueType.indexOf('=>') > 0) { + valueType = re.replaceAll(valueType, ' ', '') + } + let matchs = re.match("\\(([a-zA-Z_0-9:,]+)*\\)=>([a-zA-Z_0-9]+)", valueType) + + if (matchs) { + let number = NumberIncrease.getAndIncrease(); + let functionTypeName = 'AUTO_CALLFUNCTION_%s_%s'.format(valueName, number) + + let functionRet = re.getReg(valueType, matchs.regs[2]); + let functionBody = re.getReg(valueType, matchs.regs[1]); + + let tmp = analyzeParams(functionTypeName, functionBody) + let bodyRes = tmp[0] + for (let i in bodyRes) { + let hasProperty = Object.prototype.hasOwnProperty.call(bodyRes[i], "type") + if (hasProperty && bodyRes[i].type === "number") { + bodyRes[i].type = "NUMBER_TYPE_" + NumberIncrease.getAndIncrease(); + } + } + + rsltCallFunction.push({ + "name": functionTypeName, + "body": bodyRes, + "ret": functionRet + }) + valueType = functionTypeName + } + return valueType +} + +/**函数参数解析 */ +function analyzeParams(funcName, values) { + let result = [] + let rsltCallFunction = [] + let funcType = FuncType.DIRECT + let optionalParamCount = 0; // 可选参数的个数 + while (values.length > 0) { + let v = checkOutBody(values, 0, ["", ","]) + if (v === null) { + v = values + } + + values = values.substring(v.length, values.length) + let matchs = re.match("([a-zA-Z_0-9\\.]+)(\\?*): *([a-zA-Z<,>|_0-9\\[\\]\\(\\):='{}]+)", v) + if (matchs === null && (funcName === "on" || funcName === "off")) { + // on和off的第一个参数的类型可以是一串字符 + matchs = re.match("([a-zA-Z_0-9\\.]+)(\\?*): *\"([a-zA-Z|_0-9\\[\\]\\(\\):='{}]+)\"", v) + } + if (matchs !== null) { + let type = re.getReg(v, matchs.regs[3]) + if (type.indexOf("Map") < 0 && !isArrowFunc(type)) { + type = type.replace(/,/g, "") + } + + let valueName = re.getReg(v, matchs.regs[1]) + type = analyzeCallbackFunction(type, valueName, rsltCallFunction) + + let optionalFlag = re.getReg(v, matchs.regs[2]) === '?' ? true : false; + let checkParamOk = true; + if (optionalFlag) { + optionalParamCount++; + } else if (optionalParamCount > 0) { + // 可选参数之后不能再有必选参数,须是可选参数。 + NapiLog.logError("Invalid parameter [%s] of function [%s],".format(v, funcName) + + " the required parameter cannot follow an optional parameter."); + checkParamOk = false; + } + if (checkParamOk) { + result.push({ "name": re.getReg(v, matchs.regs[1]), "type": type, "optional": optionalFlag}) + if (type.indexOf("AsyncCallback") >= 0) { + funcType = FuncType.ASYNC + } + + if (isSyncFuncType(type, funcType)) { + funcType = FuncType.SYNC + } + } + } + else { + NapiLog.logError("Failed to analyse parameter [%s] of function [%s].".format(v, funcName)); + } + } + return [result, funcType, rsltCallFunction] +} + +module.exports = { + analyzeParams +} diff --git a/hdc/appCodeGen/src/analyze/return.js b/hdc/appCodeGen/src/analyze/return.js new file mode 100755 index 0000000000000000000000000000000000000000..d651b952eb464d8310df7cd1f05388cf09b843af --- /dev/null +++ b/hdc/appCodeGen/src/analyze/return.js @@ -0,0 +1,28 @@ +/* +* 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 re = require("../tools/re"); + +/**函数返回值解析 */ +function analyzeReturn(ret) { + let isPromise = false + if (ret.indexOf("Promise") >= 0) { + isPromise = true + } + return [ret, isPromise] +} + +module.exports = { + analyzeReturn +} \ No newline at end of file diff --git a/hdc/appCodeGen/src/analyze/type.js b/hdc/appCodeGen/src/analyze/type.js new file mode 100755 index 0000000000000000000000000000000000000000..f89658a4e94fe26bb01129cbf81b084ea25d50af --- /dev/null +++ b/hdc/appCodeGen/src/analyze/type.js @@ -0,0 +1,113 @@ +/* +* 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 re = require("../tools/re"); +const { NumberIncrease } = require("../tools/common"); + +/* 去除单行注释// */ +function parseNotes(data) { + let notes = data.indexOf("//") >= 0 ? data.substring(data.indexOf("//"), data.length) : ""; + while(notes != "") { + notes = notes.substring(0, notes.indexOf("\n")); + data = data.replace(notes, ""); + notes = "" + let st = data.indexOf("//"); + if(st >= 0) { + notes = data.substring(st, data.length); + } + } + return data +} + +/**type解析 */ +function analyzeType(data, rsltInterface = null) { // same as class + let body = data + body = body.indexOf("//") < 0 ? body : parseNotes(body) + body = re.replaceAll(body, "\n", "").split(";") + let result = { + value: [], + // function: [] + } + for (let i in body) { + let t = body[i] + while (t.length > 0 && t[0] == ' ') // 去除前面的空格 + t = t.substring(1, t.length) + while (t.length > 0 && t[-1] == ' ') // 去除后面的空格 + t = t.substring(0, t.length - 1) + if (t == "") break // 如果t为空直接返回 + let tt = re.match(" *([a-zA-Z0-9_]+)(\\?*)*: *([a-zA-Z_0-9<>,:{}[\\]| ]+)", t) + if (tt && t.indexOf("=>") < 0) { // 接口成员变量, 但不包括带'=>'的成员,带'=>'的接口成员需要按函数处理 + let valueName = re.getReg(t, tt.regs[1]) + let valueType = re.getReg(t, tt.regs[3]) + let index = valueType.indexOf("number") + let optionalFlag = re.getReg(t, tt.regs[2]) == '?' ? true : false; + while (index !== -1) { + valueType = valueType.replace("number", "NUMBER_TYPE_" + NumberIncrease.getAndIncrease()) + index = valueType.indexOf("number") + } + result.value.push({ + name: valueName, + type: valueType, + optional: optionalFlag + }) + } + } + return result +} + +function analyzeType2(data) { + let body = re.replaceAll(data, " ", "").split("'|'") + let result = { + element: [], + function: [], + enumValueType: 0 // 0代表数字,1代表字符串 + } + for (let i in body) { + let bodyContent = body[i] + while (bodyContent.length > 0 && bodyContent[0] == ' ') { + bodyContent = bodyContent.substring(1, bodyContent.length) + } + while (bodyContent.length > 0 && bodyContent[-1] == ' ') { + bodyContent = bodyContent.substring(0, bodyContent.length - 1) + } + if (bodyContent == "") { + break + } + analyzeType2Result(result, bodyContent, i) + } + return result +} + +function analyzeType2Result(result, bodyContent, index) { + let regString = re.match(" *([a-zA-Z0-9_]+) *", bodyContent) + if (regString) { + let elementName = re.getReg(bodyContent, regString.regs[1]) + elementName = 'NAME_' + elementName.toUpperCase() + let elementValue = re.getReg(bodyContent, regString.regs[1]) + result.element.push({ + name: elementName, + value: elementValue, + type: 'string' + }) + result.enumValueType = 1 + } + return result +} + +module.exports = { + analyzeType, + analyzeType2, + analyzeType2Result, + parseNotes +} \ No newline at end of file diff --git a/hdc/appCodeGen/src/app_code_gen.js b/hdc/appCodeGen/src/app_code_gen.js new file mode 100755 index 0000000000000000000000000000000000000000..8c91ea9924348379a8f5b676ce49ac5eb6f264b3 --- /dev/null +++ b/hdc/appCodeGen/src/app_code_gen.js @@ -0,0 +1,112 @@ +/* +* 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 main = require("./main"); + +const re = require("./tools/re"); +const { checkFileError } = require("./tools/common"); +const { NapiLog } = require("./tools/NapiLog"); +const path = require("path"); +const stdio = require("stdio"); +var fs = require('fs'); +const { print } = require("./tools/tool"); + +let ops = stdio.getopt({ + 'filename': { key: 'f', args: 1, description: ".d.ts file", default: "@ohos.napitest.d.ts" }, + 'out': { key: 'o', args: 1, description: "output directory", default: "." }, + 'loglevel': { key: 'l', args: 1, description: "Log Level : 0~3", default: "1" } +}); + + + /* 新增业务代码可配置参数:写在json文件里: + * [{"includeName":"xxx.h", "cppName":"xxx.cpp","interfaceName": "functest", + * "serviceCode":"out = codeTestFunc(v);"}] + * 配置cfg.json文件路径 + */ + // 'serviceCode': {key: 's', args: 1, description: "configure the service code", default: ""}, + // 'directory': { key: 'd', args: 1, description: ".d.ts directory", default: "" }, + // 'imports': { key: 'i', args: 1, description: "enable or disable support imports self-define file", default: false }, + +NapiLog.init(ops.loglevel, path.join("" + ops.out, "napi_gen.log")) + +let fileNames = ops.filename; +// var pathDir = ops.directory; +// var imports = ops.imports; +if (fileNames == null) { + NapiLog.logInfo("fileNames cannot be empty!"); +} 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); + } + } +} + + +/** + * 获取Json配置文件内容 + * @returns + */ +function getJsonCfg(currentPath) { + let jsonCfg = null; // cfg.json 配置文件 + currentPath = currentPath.replace(/(^\s*)|(\s*$)/g, ''); // trim before and after espace + let jsonFilePath = path.join(currentPath); + let jsonFile = fs.readFileSync(jsonFilePath, { encoding: "utf8" }); + jsonCfg = JSON.parse(jsonFile); + return jsonCfg; +} + +function checkGenerate(fileName) { + NapiLog.logInfo("check file []".format(fileName)) + + let fn = re.getFileInPath(fileName); + let tt = re.match('(@ohos\.)*([.a-z_A-Z0-9]+).d.ts', fn); + if (tt) { + let result = checkFileError(fileName); + let jsonConfig + // if (ops.serviceCode) { + // jsonConfig = getJsonCfg(ops.serviceCode); + // } + if (result[0]) { + main.doGenerate(fileName, ops.out, jsonConfig); + } + else { + NapiLog.logError(result[1]); + } + } + else { + NapiLog.logError('file name ' + fn + ' format invalid in function of checkGenerate!'); + } +} + +let ret = NapiLog.getResult(); +if (ret[0]) { + print('success'); + NapiLog.logInfo('success'); +} +else { + print('fail\n' + ret[1]); + NapiLog.logInfo('fail\n' + ret[1]); +} diff --git a/hdc/appCodeGen/src/generate.js b/hdc/appCodeGen/src/generate.js new file mode 100755 index 0000000000000000000000000000000000000000..2c162a4ad8e4812236cfacbfc06335b8bae482b2 --- /dev/null +++ b/hdc/appCodeGen/src/generate.js @@ -0,0 +1,190 @@ +/* +* 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. +*/ +// 生成BUILD.gn +// 生成tool_utility.h,生成tool_utility.cpp +const { replaceAll } = require("./tools/tool"); +// const { generateNamespace } = require("./generate/namespace"); +const { writeFile } = require("./tools/FileRW"); +const re = require("./tools/re"); + +const { jsonCfgList } = require("./tools/common"); +const os = require("os"); +const path = require('path') +const { NapiLog } = require("./tools/NapiLog"); +var fs = require('fs'); + +let indexEtsTemplete = `\ +import napitest from '@ohos.napitest'; +import hilog from '@ohos.hilog'; + +@Entry +@Component +struct Index { + @State message: string = 'Hello NAPI Sample'; + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + [test_interface_button] + } + .width('100%') + } + .height('100%') + } +} +` +let buttonTemplate = ` +Button() { + Text([button_test_interface_name]) + .fontSize(20) + .fontWeight(FontWeight.Bold) + } + .type(ButtonType.Capsule) + .margin({ + top: 10 + }) + .backgroundColor('#0D9FFB') + .width('90%') + .height('5%') + .onClick(() => { + hilog.info(0x0000, 'testTag', 'button onClick!'); + + [button_test_interface_code] + + hilog.info(0x0000, 'testTag', 'button onClick end !'); + }); +` + +// var genFileList = [] +// function deleteFolder(folderPath) { +// if (fs.existsSync(folderPath)) { +// fs.rmSync(folderPath, {"recursive": true}) +// } +// } + +// function createFolder(folderPath) { +// if (!fs.existsSync(folderPath)) { +// fs.mkdirSync(folderPath) +// } +// } + +// function formatCode(destDir) { +// let sysInfo = os.platform() +// let clangFmtName = sysInfo === 'win32' ? "clang-format.exe" : "clang-format" +// let callPath = NapiLog.getCallPath(); +// callPath = callPath.substring(callPath.indexOf("[") + 1, callPath.indexOf("src")); +// let dumyClangFmtFile = path.join(callPath, clangFmtName) +// let dumyFmtCfgFile = path.join(callPath, ".clang-format") + +// if(!fs.existsSync(dumyClangFmtFile)) { +// NapiLog.logInfo("Warning: clang-format does not exist, can not format cpp file."); +// return +// } + +// // 使用pkg打包的napi_generator工具,其中的clang-format程序在运行时是解压到一个名为snapshot的虚拟目录中的,如C:\snapshot\napi_generator\ +// // 虚拟目录中的clang-format程序不能直接运行,必须先将它拷贝到本地硬盘的真实目录下。 +// createFolder(path.resolve("./tmpLocal")) +// let localClangFmtFile = path.resolve("./tmpLocal/" + clangFmtName) // clang-format可执行程序 +// let localFmtCfgFile = path.resolve("./tmpLocal/.clang-format") // clang-format格式化配置文件 +// fs.copyFileSync(dumyClangFmtFile, localClangFmtFile) +// fs.copyFileSync(dumyFmtCfgFile, localFmtCfgFile) + +// let execSync = require("child_process").execSync +// if (sysInfo != 'win32') { +// // linux系统下需要为临时复制的clang-format程序增加可执行权限 +// execSync("chmod +x " + "\"" + localClangFmtFile + "\"") +// } + +// for (let i = 0; i < genFileList.length; ++i) { +// // 文件路径前后要用引号包含,防止因为路径中存在空格而导致命令执行失败 (windows的文件夹允许有空格) +// let cmd = "\"" + localClangFmtFile + "\" -style=file -i \"" + path.resolve(path.join(destDir, genFileList[i])) +// + "\"" +// try { +// execSync(cmd) // C++文件格式化 +// } catch (err) { +// NapiLog.logError("Failed to format code, exception: " + err.stderr) +// } +// } +// // 格式化结束后,删除临时目录文件 +// deleteFolder(path.resolve("./tmpLocal")) +// } + +function analyzeJsonCfg(jsonCfg) { + let len = jsonCfg.length; + let jsonConfig = [] + // 将json文件的数据存入jsonCfgList中 + for (let i = 0; i < len; i++) { + let interfaceBody = null + if (jsonCfg[i].interfaceName.indexOf("::") > 0) { + let tt = jsonCfg[i].interfaceName.split("::") + interfaceBody = { + className: tt[0], + funcName: tt[1], + } + } else { + interfaceBody = { + className: "", + funcName: jsonCfg[i].interfaceName, + } + } + + jsonConfig.push({ + includeName: jsonCfg[i].includeName, + cppName: jsonCfg[i].cppName, + interfaceName: interfaceBody, + serviceCode: jsonCfg[i].serviceCode.replaceAll('\\n', '\n'), + }) + } + jsonCfgList.push(jsonConfig) +} + +function generateAppCode(structOfTs, destDir, moduleName, jsonCfg) { + let ns0 = structOfTs.declareNamespace[0]; + let license = structOfTs.declareLicense[0]; + if (ns0 === undefined) { + NapiLog.logError('generateAll error:get namespace fail!'); + return; + } + // 分析业务配置代码的调用代码: 分析Json文件 + // if (jsonCfg) { + // analyzeJsonCfg(jsonCfg); + // } + // jsonCfgList.pop() + + // let result = generateNamespace(ns0.name, ns0.body) + + // index.ets文件中测试接口button代码生成 + let testInterfaceName = 'testFunStr' + let testInterfaceButton = replaceAll(buttonTemplate, "[button_test_interface_name]", testInterfaceName) + + let buttonTestInterface = ` + // funStr(v: string): string; + let strIn: string = "funStrTest"; + let strRet: string = napitest.funStr(strIn); + hilog.info(0x0000, 'testTag', 'napitest.funStr ret: ' + strRet); + ` + testInterfaceButton = replaceAll(testInterfaceButton, "[button_test_interface_code]", buttonTestInterface); + + // index.ets文件生成 + let indexEts = replaceAll(indexEtsTemplete, "[test_interface_button]", testInterfaceButton) + writeFile(re.pathJoin(destDir, "Index.ets"), null != license ? (license + "\n" + indexEts) : indexEts) +} + +module.exports = { + generateAppCode +} diff --git a/hdc/appCodeGen/src/main.js b/hdc/appCodeGen/src/main.js new file mode 100755 index 0000000000000000000000000000000000000000..c5fd39bf6571a319ce48cdde7e90f0af5d1be024 --- /dev/null +++ b/hdc/appCodeGen/src/main.js @@ -0,0 +1,46 @@ +/* +* 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 { analyzeFile } = require("./analyze"); +const { generateAppCode } = require("./generate"); +const { NapiLog } = require("./tools/NapiLog"); +const re = require("./tools/re"); +var fs = require('fs'); + +function doGenerate(ifname, destdir, jsonCfg) { + // step1: analyze file + let structOfTs = analyzeFile(ifname); + let fn = re.getFileInPath(ifname); + let tt = re.match('(@ohos\.)*([.a-z_A-Z0-9]+).d.ts', fn); + if (structOfTs === undefined || structOfTs.declareNamespace.length == 0 || + structOfTs.declareNamespace[0].name === undefined) { + NapiLog.logError('analyzeFile file fail and file name is: ' + fn); + return; + } + + // step2: generate code + if (tt) { + let moduleName = re.getReg(fn, tt.regs[2]); + + structOfTs.imports = []; + generateAppCode(structOfTs, destdir, moduleName, jsonCfg); + } else { + NapiLog.logError('file name ' + fn + ' format invalid in function of doGenerate!'); + } + return structOfTs.declareNamespace[0].name +} + +module.exports = { + doGenerate +} diff --git a/hdc/appCodeGen/src/tools/FileRW.js b/hdc/appCodeGen/src/tools/FileRW.js new file mode 100755 index 0000000000000000000000000000000000000000..a54d11468b7acf8d0ac2c9a49fafb5227261c3bb --- /dev/null +++ b/hdc/appCodeGen/src/tools/FileRW.js @@ -0,0 +1,140 @@ +/* +* 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); +} + +module.exports = { + readFile, + writeFile +} \ No newline at end of file diff --git a/hdc/appCodeGen/src/tools/NapiLog.js b/hdc/appCodeGen/src/tools/NapiLog.js new file mode 100755 index 0000000000000000000000000000000000000000..cc674d86d6f95920d4532179004e763536c94064 --- /dev/null +++ b/hdc/appCodeGen/src/tools/NapiLog.js @@ -0,0 +1,138 @@ +/* +* 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"; +} + +/** + * 通过调用栈获取当前正在执行的方法名,代码行数及文件路径 + * @param {} callerFuncName 指定取调用栈中哪个方法名所在的帧作为目标帧 + * @returns + */ +NapiLog.getCallPath = function(callerFuncName = null) { + let callPath = "" + let stackArray = new Error().stack.split('\n'); + + // 如果没有指定目标方法,默认在调用栈中查找当前方法"getCallPath"所在的帧 + let destFuncName = callerFuncName != null ? callerFuncName : "getCallPath" + + for (let i = stackArray.length -1; i >=0 ; --i) { + // debug模式和打包后的可执行程序调用栈函数名不同, 以NapiLog.log()方法为例: + // vscode debug模式下调用栈打印的方法名为NapiLog.log,而可执行程序的调用栈中显示为Function.log() + let callerMatch = (stackArray[i].indexOf("NapiLog." + destFuncName) > 0 + || stackArray[i].indexOf("Function." + destFuncName) > 0) + if (callerMatch) { + 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 = NapiLog.getCallPath("log"); + 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/hdc/appCodeGen/src/tools/common.js b/hdc/appCodeGen/src/tools/common.js new file mode 100755 index 0000000000000000000000000000000000000000..152d277a54e633911c972d92b831d5dcab3b278a --- /dev/null +++ b/hdc/appCodeGen/src/tools/common.js @@ -0,0 +1,475 @@ +/* +* 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 re = require("./re"); +const tsc = require("../../../../node_modules/typescript"); + +function checkFileError(ifname) { + let program = tsc.createProgram([ifname], {target: tsc.ScriptTarget.Latest,}) + let emitResult = program.emit(); + let allDiagnostics = tsc.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); + + let errorMsg = '' + allDiagnostics.forEach(diagnostic => { + if (diagnostic.file) { + let { line, character } = tsc.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); + let message = tsc.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); + errorMsg += `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}\n`; + } else { + errorMsg += tsc.flattenDiagnosticMessageText(diagnostic.messageText, "\n") + "\n"; + } + }); + + if (allDiagnostics.length > 0) { + return [false, errorMsg]; + } + return [true, ""]; +} + +class FuncType { } +FuncType.DIRECT = 1 +FuncType.SYNC = 2 +FuncType.ASYNC = 4 +FuncType.PROMISE = 8 +FuncType.ToString = function (type) { + if (type === FuncType.DIRECT) return "DIRECT"; + else if (type === FuncType.SYNC) return "SYNC"; + else if (type === FuncType.ASYNC) return "ASYNC"; + else if (type === FuncType.PROMISE) return "PROMISE"; + return "UNKNOW"; +} + +class NumberIncrease { } +NumberIncrease.num = 1 +NumberIncrease.getAndIncrease = function () { + return NumberIncrease.num++; +} +NumberIncrease.get = function () { + return NumberIncrease.num; +} +NumberIncrease.reset = function () { + NumberIncrease.num = 1 +} + +class InterfaceList { } +InterfaceList.interfacess_ = []; +InterfaceList.push = function (ifs) { + InterfaceList.interfacess_.push(ifs) +} +InterfaceList.pop = function () { + InterfaceList.interfacess_.pop() +} +InterfaceList.getValue = function (name) { + let ifs = InterfaceList.interfacess_[InterfaceList.interfacess_.length - 1] + for (let i in ifs) { + let vv = ifs[i]; + if (ifs[i].name === name) { + var hasProperty = Object.prototype.hasOwnProperty.call(ifs[i].body, "allProperties") + if (hasProperty) { + return ifs[i].body.allProperties.values; + } else { + return ifs[i].body.value; + } + } + } + return null; +} + +InterfaceList.getBody = function (name) { + let ifs = InterfaceList.interfacess_[InterfaceList.interfacess_.length - 1] + for (let i in ifs) { + if (ifs[i].name === name) { + return ifs[i].body; + } + } + return null; +} + +class CallFunctionList { } +CallFunctionList.callFuncs = []; +CallFunctionList.push = function (ifs) { + CallFunctionList.callFuncs.push(ifs) +} +CallFunctionList.pop = function () { + CallFunctionList.callFuncs.pop() +} +CallFunctionList.getValue = function (name) { + if (CallFunctionList.callFuncs.length === 0) { + return null + } + + let cfs = CallFunctionList.callFuncs[CallFunctionList.callFuncs.length - 1] + if (cfs === undefined) { + return null + } + + for (let i = 0; i < cfs.length; i++) { + if (cfs[i].name === name) { + return [cfs[i].body, cfs[i].ret] + } + } + return null +} + +CallFunctionList.getObjOnFuncName = function (interfaceName) { + let cfs = CallFunctionList.callFuncs[CallFunctionList.callFuncs.length - 1] + let funNames = [] + for (let i = 0; i < cfs.length; i++) { + if (cfs[i].name.indexOf(interfaceName) === 0) { + let funName = cfs[i].name.substring(interfaceName.length+1, cfs[i].name.length) + funNames.push(funName) + } + } + return funNames +} + +class TypeList { } +TypeList.types = []; +TypeList.push = function (ifs) { + TypeList.types.push(ifs) +} +TypeList.pop = function () { + TypeList.types.pop() +} +TypeList.getValue = function (name) { + let ifs = TypeList.types[TypeList.types.length - 1] + for (let i in ifs) { + if (ifs[i].name === name) { + var hasProperty = Object.prototype.hasOwnProperty.call(ifs[i].body, "allProperties") + if (hasProperty) { + return ifs[i].body.allProperties.values; + } else { + return ifs[i].body; + } + } + } + return null; +} + +class EnumList { } +EnumList.enum_ = []; +EnumList.push = function (ifs) { + EnumList.enum_.push(ifs) +} +EnumList.pop = function () { + EnumList.enum_.pop() +} +EnumList.getValue = function (name) { + let ifs = EnumList.enum_[EnumList.enum_.length - 1] + for (let i in ifs) { + if (ifs[i].name === name) { + return ifs[i].body.element; + } + } + return null; +} + +function getArrayType(type) { + let tt = re.match("Array<([a-zA-Z_0-9]+)>", type) + if (tt != null) { + return re.getReg(type, tt.regs[1]) + } + + tt = re.match("Array<{([[a-z:]+)([a-z:]]+)([a-zA-Z_1-9:]+)", type) + if (tt != null) { + let res = '' + let len = tt.regs.length + for (let i=1; i]+)>", type) + let ttMap = re.search(",([a-zA-Z_0-9]+)>>", type) + let ttArray = re.search("Array<([a-zA-Z_0-9]+)>", type) + + if(ttArray === null) { + ttArray = re.search("([a-zA-Z_0-9]+)\\[\\]>", type) + } + + let valueType + let valueMapType + let valueArrayType + if (ttKey === null && ttValue === null && ttMap === null) { + ttKey = re.search("key:([a-zA-Z_0-9]+)", type) + ttValue = re.search(":([a-zA-Z_0-9]+)}", type) + ttMap = re.search(":([a-zA-Z_0-9]+)}}", type) + ttArray = re.search("Array<([a-zA-Z_0-9]+)>", type) + if (ttArray === null) { + ttArray = re.search(":([a-zA-Z_0-9]+)\\[\\]}", type) + } + } + + if (ttValue != null) { + valueType = re.getReg(type, ttValue.regs[1]) + if (valueType.indexOf("Array<") === 0) { + valueArrayType = re.getReg(valueType, ttArray.regs[1]) + valueType = undefined + } else if (ttMap != undefined) { + valueMapType = re.getReg(type, ttMap.regs[1]) + valueType = undefined + } + } + if (ttMap != null) { + valueMapType = re.getReg(type, ttMap.regs[1]) + } + if (ttArray != null) { + valueArrayType = re.getReg(type, ttArray.regs[1]) + } + return [re.getReg(type, ttKey.regs[1]), valueType, valueMapType, valueArrayType] +} + +function getUnionType(type) { + type = type.replace(/\s*/g,"") + var typeArr = new Array() + typeArr = type.split("|") + return typeArr +} + +function isFuncType(type) { + let isFunction = false; + if (type === null || type === undefined) { + return isFunction; + } + + if (type === 'function' || type === 'Function') { + isFunction = true; + return isFunction; + } +} + +function isRegisterFunc(name) { + let regIndex = name.indexOf('register'); + let isRegister = false + if (regIndex === 0) { + isRegister = true + } + return isRegister +} + +function isUnRegisterFunc(name) { + let unRegIndex = name.indexOf('unRegister'); + let isRegister = false + if (unRegIndex === 0) { + isRegister = true + } + return isRegister +} + +function isOnObjCallback(name) { + let regIndex = name.indexOf('on'); + let flag = false + let onLen = 2; + if (regIndex === 0 && name.length > onLen) { + flag = true + } + return flag +} + +// 箭头函数,如funTest(cb: (wid: boolean) => void): string; +function isArrowFunc(type) { + let arrowFunc = false; + if (type.indexOf('AUTO_CALLFUNCTION') >= 0 || type.indexOf('=>') > 0) { + arrowFunc = true; + } + return arrowFunc; +} + +function isOnOffRegisterFunc(name) { + let flag = false; + if (name === 'on' || name === 'off' || isRegisterFunc(name) || isUnRegisterFunc(name) || + isOnObjCallback(name)) { + flag = true; + } + return flag; +} + +function isCreateThreadsafeFunc(name) { + let index = name.indexOf('createThreadSafeFunc'); + let isTdSafeFunc = false + if (index === 0) { + isTdSafeFunc = true + } + return isTdSafeFunc +} + +function getOnObjCallbackType(funcName, interName) { + let onObjCbType = '' + if (interName != '') { + onObjCbType = interName + '_' + funcName + } else { + onObjCbType = funcName + } + return 'AUTO_CALLFUNCTION_' + onObjCbType +} + +function getOnCallbackFunAndInterName(CallbackType) { + CallbackType = CallbackType.replaceAll('AUTO_CALLFUNCTION_', '') + let CallbackTypes = CallbackType.split('_') + let funcName = CallbackTypes[1]; + let interName = CallbackTypes[0]; + return [interName, funcName] +} + +class jsonCfgList { } +jsonCfgList.jsonCfg = []; +jsonCfgList.push = function (ifs) { + jsonCfgList.jsonCfg.push(ifs) +} +jsonCfgList.pop = function () { + jsonCfgList.jsonCfg.pop() +} +jsonCfgList.getValue = function (className, inter) { + let ifs = jsonCfgList.jsonCfg[jsonCfgList.jsonCfg.length - 1] + for (let i in ifs) { + if (ifs[i].interfaceName.className === className && ifs[i].interfaceName.funcName === inter) { + return ifs[i].serviceCode + } + } + return null; +} + +function getLogErrInfo() { + let errInfo = " Please refer to for support capacity:" + + "https://gitee.com/openharmony/napi_generator/tree/master/release-notes" + return errInfo +} + +module.exports = { + FuncType, + EnumValueType, + NumberIncrease, + InterfaceList, + TypeList, + CallFunctionList, + isType, + typeIndex, + getArrayType, + getArrayTypeTwo, + checkFileError, + isEnum, + enumIndex, + getMapType, + EnumList, + jsType2CType, + getUnionType, + isFuncType, + isArrowFunc, + jsonCfgList, + isRegisterFunc, + isUnRegisterFunc, + isOnObjCallback, + isOnOffRegisterFunc, + getOnObjCallbackType, + getLogErrInfo, + isCreateThreadsafeFunc +} \ No newline at end of file diff --git a/hdc/appCodeGen/src/tools/re.js b/hdc/appCodeGen/src/tools/re.js new file mode 100755 index 0000000000000000000000000000000000000000..ff9657b5d0ea63dedc2a0c05899da0516bfb34f1 --- /dev/null +++ b/hdc/appCodeGen/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/hdc/appCodeGen/src/tools/tool.js b/hdc/appCodeGen/src/tools/tool.js new file mode 100755 index 0000000000000000000000000000000000000000..b7ffe5541c9599dd47dc7b0d829591f60ee8ac78 --- /dev/null +++ b/hdc/appCodeGen/src/tools/tool.js @@ -0,0 +1,316 @@ +/* +* 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 re = require("./re"); +let vscode = null; +try { + vscode = require('vscode'); +} +catch (err) { + vscode = null; +} + +const NUM_CONST_MAP = new Map([ + [0, "XNapiTool::ZERO"], [1, "XNapiTool::ONE"], [2, "XNapiTool::TWO"], [3, "XNapiTool::THREE"], + [4, "XNapiTool::FOUE"], [5, "XNapiTool::FIVE"], [6, "XNapiTool::SIX"], [7, "XNapiTool::SEVEN"], + [8, "XNapiTool::EIGHT"], [9, "XNapiTool::NINE"] +]); + +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 checkOutBody(body, off, flag, binside) { + off = off || 0; + flag = flag || ["{", "}"]; + binside = binside || false; + let idx = { + "(": ")", + "{": "}", + "<": ">", + //"<": "<", + //">": ">", + }; + let csl = {}; + let csr = {}; + for (let f in idx) { + csl[f] = 0 + csr[idx[f]] = 0 + } + let cs1 = 0 + if (flag[0].length > 0 && body.substring(off, off + flag[0].length) !== flag[0]) { + return null; + } + + for (let i = off + flag[0].length; i < body.length; i++) { + if (body[i] === '"') cs1 += 1 + if (cs1 % 2 === 0) { + let tb1 = true; + for (let k in csl) { + if (csl[k] !== csr[idx[k]]) { + tb1 = false; + break; + } + } + if (tb1 && body.substring(i, i + flag[1].length) === flag[1]) { + if (binside) + return body.substring(off + flag[0].length, i); + return body.substring(off, i + flag[1].length); + } + + if (body[i] in csl) { + csl[body[i]] += 1; + if (body[i] in csr) csr[body[i]] += 1; + } + if (body[i] in csr) { + if (!(body[i] === '>' && body[i-1] === '=')) { // 尖括号匹配时忽略关键字 "=>" + csr[body[i]] += 1; + } + } + } + } + return null; +} + +function removeExplains(data) { + // 去除 /** */ 类型的注释 + while (data.indexOf("/*") >= 0) { + let i1 = data.indexOf("/*") + let i2 = data.indexOf("*/") + 2 + data = data.substring(0, i1) + data.substring(i2, data.length) + } + + // 去除 namespace 域外 // 类型的注释 + // 如果换行格式是\r\n, 去除\r, 统一成\n格式 + while (data.indexOf("\r") >= 0) { + data = data.replace("\r", "") + } + while (data.indexOf("//") >= 0) { + let i1 = data.indexOf("//") + let i2 = data.indexOf("\n") + let end = data.indexOf("declare namespace ") + while (i2 < end && i1 < end) { + while (i1 > i2) { + data = data.substring(0, i2) + data.substring(i2 + 2, data.length) + i2 = data.indexOf("\n") + i1 = data.indexOf("//") + } + data = data.substring(0, i1) + data.substring(i2 + 1, data.length) + i1 = data.indexOf("//") + i2 = data.indexOf("\n") + end = data.indexOf("declare namespace ") + } + if (i2 > end || i1 > end) { + break; + } + } + + return data +} + +function getLicense(data) { + while (data.indexOf("/*") >= 0) { + let i1 = data.indexOf("/*") + let i2 = data.indexOf("*/") + 2 + let licenseData = data.substring(i1, i2) + if (licenseData.search("Copyright") !== -1) { + return licenseData + } else { + return null + } + } +} + +function removeEmptyLine(data) { + while (data.indexOf("\r") >= 0) { + data = data.replace("\r", "") + } + while (data.indexOf("\t") >= 0) { + data = data.replace("\t", "") + } + while (data.indexOf(" \n") >= 0) { + data = data.replace(" \n", "\n") + } + while (data.indexOf("\n ") >= 0) { + data = data.replace("\n ", "\n") + } + while (data.indexOf("\n\n") >= 0) { + data = data.replace("\n\n", "\n") + } + while (data.indexOf("\n") === 0) { + data = data.substring(1, data.length) + } + while (data.indexOf(" ") === 0) { + data = data.substring(1, data.length) + } + return data +} + +function replaceTab(data) { + while (data.indexOf("\t") >= 0) { + data = data.replace("\t", " ") + } + return data +} + +function removeEmptyLine2(data) { + while (data.indexOf(" \n")) + data = data.replace(" \n", "\n") + while (data.indexOf("\n\n\n")) + data = data.replace("\n\n\n", "\n\n") + return data +} + +function replaceAll(s, sfrom, sto) { + while (s.indexOf(sfrom) >= 0) { + s = s.replace(sfrom, sto) + } + return s; +} + +/** + * 比较两个方法是否完全相同 + * @param func1 方法1 + * @param func2 方法2 + * @returns 方法名称与形参是否完全相同 + */ + function isSameFunc(func1, func2) { + if (func1.name !== func2.name) { // 判断方法名称是否相同 + return false; + } + + let func1ParamCount = func1.value.length + if (func1ParamCount !== func2.value.length) { // 判断方法形参个数是否一样 + return false; + } + + for (let i in func1.value) { // 判断方法每个形参数据类型是否相同 + if (func1.value[i].type !== func2.value[i].type) { + if (!(func1.value[i].type.indexOf("NUMBER_TYPE_") >= 0 && + func2.value[i].type.indexOf("NUMBER_TYPE_") >= 0)) { + return false; + } + } + } + + // 以上全部相同,判定为相同方法 + return true; +} + +/** + * 将方法对象插入列表(重复的方法对象不插入) + * @param obj 待插入的方法对象 + * @param list 目标列表 + * @returns 是否成功插入列表 + */ + function addUniqFunc2List(obj, list) { + for (let i in list) { + if (isSameFunc(obj, list[i])) { + return false + } + } + list.push(obj) + return true +} + +/** + * 找到子类中重写父类的方法并将它设置为override + * @param parentFunc 父类被重写的方法名 + * @param childFunclist 子类全部方法列表 + * @returns void + */ + function setOverrideFunc(parentFunc, childFunclist) { + for (let i in childFunclist) { + if (isSameFunc(parentFunc, childFunclist[i])) { + childFunclist[i].isOverride = true + return + } + } +} + +/** + * 将对象插入列表(名称重复的属性对象不插入) + * @param obj 待插入的对象 + * @param list 目标列表 + * @returns void + */ + function addUniqObj2List(obj, list) { + for (let i in list) { + if (list[i].name === obj.name) { + return + } + } + list.push(obj) +} + +/** + * 如果方法所在的类为基类,生成的c++函数定义为虚函数 + * @param data 方法所在的类信息 + * @param funcInfo 方法信息 + * return tabStr 缩进,staticStr 静态函数关键词,virtualStr 虚函数关键词, overrideStr 重写关键词 + */ + function getPrefix(data, funcInfo) { + let isStatic = funcInfo.isStatic + let tabStr = "" + let virtualStr = "" + let staticStr = isStatic ? "static " : "" + if (data.childList) { + tabStr = " " // 类中的方法增加一个缩进 + virtualStr = (data.childList.length > 0 && !isStatic) ? "virtual " : "" //如果是基类中的非静态方法,定义为虚函数 + } + let overrideStr = funcInfo.isOverride ? " override" : "" // 重写了父类方法,需要加上override关键字,否则触发c++门禁告警 + return [tabStr, staticStr, virtualStr, overrideStr] +} + +function getConstNum(num) { + return NUM_CONST_MAP.get(parseInt(num)); +} + +module.exports = { + checkOutBody, + removeExplains, + removeEmptyLine, + removeEmptyLine2, + replaceAll, + print, + getLicense, + replaceTab, + addUniqObj2List, + addUniqFunc2List, + getPrefix, + getConstNum, + setOverrideFunc +}