• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd.
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15const { NapiLog } = require("../gen/tools/NapiLog");
16const { writeFile } = require("../gen/tools/FileRW");
17const path = require('path')
18const re = require("../gen/tools/re");
19const fs = require("fs");
20const os = require("os");
21
22function parseFileAll(hFilePath) {
23    let execSync = require("child_process").execSync
24    let cmd = ""
25    if(fs.existsSync("./src/tsGen/header_parser.py")) {
26        // call python file (for debug test)
27        cmd = "python ./src/tsGen/header_parser.py " + hFilePath
28    } else {
29        // call exe file (for real runtime)
30        let sysInfo = os.platform()
31        let execPath = path.dirname(process.execPath)
32        let exeFile = sysInfo === 'win32' ? path.join(execPath, "header_parser.exe") :
33                                            path.join(execPath, "header_parser")
34        cmd = exeFile + " " + hFilePath
35    }
36
37    let parseResult = null
38    let stdout = execSync(cmd)
39    parseResult = JSON.parse(stdout.toString()).result
40    return parseResult
41}
42
43function createNameSpaceInfo(parseNameSpaceInfo) {
44    let nameSpaceInfo = {
45        "name": "",
46        "classes": [],
47        "functions": []
48    }
49    nameSpaceInfo.name = parseNameSpaceInfo
50    return nameSpaceInfo
51}
52
53function analyzeNameSpace(rootInfo, parseResult) {
54    let parseNameSpaces = parseResult.namespaces
55    for(var i = 0; i < parseNameSpaces.length; ++i) {
56        let nameSpaceInfo = createNameSpaceInfo(parseNameSpaces[i])
57        rootInfo.namespaces.push(nameSpaceInfo)
58    }
59}
60
61function basicC2js(cType) {
62    let jsType = ""
63    switch (cType) {
64        case 'string':
65        case 'std::string':
66        case 'char':
67        case 'wchar_t':
68        case 'char16_t':
69        case 'char32_t':
70            jsType = 'string'
71            break;
72        case 'bool':
73            jsType = 'boolean'
74            break
75        case 'short':
76        case 'int':
77        case 'uint32_t':
78        case 'size_t':
79        case 'long':
80        case 'long long':
81        case 'float':
82        case 'double':
83        case 'long double':
84            jsType = 'number'
85            break
86        default:
87            jsType = cType
88            break
89    }
90    return jsType
91}
92
93function getJsTypeFromC(cType, typeInfo) {
94    let basicCtype = cType
95    let matchs = re.match("(std::)?vector<([\x21-\x7e ]+)>", basicCtype);
96    if (matchs) {
97        basicCtype = re.getReg(basicCtype, matchs.regs[2]).trim()
98        typeInfo.array = 1
99    }
100
101    let unsignedIdx = basicCtype.indexOf('unsigned')
102    if ( unsignedIdx >= 0) {
103        // cut off the keywords 'unsigned'
104        basicCtype = basicCtype.substring(unsignedIdx + 8, basicCtype.length).trim()
105    }
106    let jsType = basicC2js(basicCtype)
107    if (typeInfo.array) {
108        jsType = "Array<%s>".format(jsType)
109    }
110    return jsType
111}
112
113function createParam(parseParamInfo) {
114    let param = {
115        "name": "",
116        "type": ""
117    }
118    param.name = parseParamInfo.name
119    let rawType = getJsTypeFromC(parseParamInfo.raw_type, parseParamInfo)
120    param.type = rawType
121
122    return param
123}
124
125function createFuncInfo(parseFuncInfo) {
126    let funcInfo = {
127        "name": "",
128        "params": [],
129        "namespace": "",
130        "retType": ""
131    }
132    funcInfo.name = parseFuncInfo.name
133    funcInfo.namespace = parseFuncInfo.namespace
134    let tokenIndex = funcInfo.namespace.indexOf('::')
135    if (tokenIndex >= 0) {
136        // delete '::' in namespace, get the pure space name.
137        funcInfo.namespace = funcInfo.namespace.substring(0, tokenIndex)
138    }
139
140    let parseParams = parseFuncInfo.parameters
141    for(var i = 0; i < parseParams.length; ++i) {
142        let param = createParam(parseParams[i])
143        funcInfo.params.push(param)
144    }
145
146    let retType = parseFuncInfo.returns === '' ? parseFuncInfo.rtnType : parseFuncInfo.returns
147    funcInfo.retType = getJsTypeFromC(retType, parseFuncInfo)
148
149    return funcInfo
150}
151
152function putFuncIntoNamespace(funcInfo, namespaces) {
153    for (var i = 0; i < namespaces.length; ++i) {
154        if (namespaces[i].name === funcInfo.namespace) {
155            namespaces[i].functions.push(funcInfo)
156            return
157        }
158    }
159    NapiLog.logError('The namespace [%s] of function %s is not found.'.format(funcInfo.namespace, funcInfo.name));
160}
161
162function analyzeRootFunction(rootInfo, parseResult) {
163    let parseFunctions = parseResult.functions
164    for(var i = 0; i < parseFunctions.length; ++i) {
165        let funcInfo = createFuncInfo(parseFunctions[i])
166        if (parseFunctions[i].namespace != '') {
167            // function in namespace
168            putFuncIntoNamespace(funcInfo, rootInfo.namespaces)
169        } else {
170            // function without namespace, put on root
171            rootInfo.functions.push(funcInfo)
172        }
173    }
174}
175
176function createProperties(parseProperties) {
177    let propertyList = []
178    for (var i = 0; i < parseProperties.length; ++i) {
179        let property = {}
180        property.name = parseProperties[i].name
181        property.type = getJsTypeFromC(parseProperties[i].raw_type, parseProperties[i])
182        propertyList.push(property)
183    }
184    return propertyList
185}
186
187function createClassFunctions(parseFuncs) {
188    let funcList = []
189    for(var i = 0; i < parseFuncs.length; ++i) {
190        let funcInfo = createFuncInfo(parseFuncs[i])
191        funcInfo.isClassFunc = true
192        funcList.push(funcInfo)
193    }
194    return funcList
195}
196
197function createClassInfo(parseClassInfo) {
198    let classInfo = {
199        "name": "",
200        "namespace": "",
201        "properties": [],
202        "functions": [],
203        "extends":[]
204    }
205    classInfo.name = parseClassInfo.name
206    classInfo.namespace = parseClassInfo.namespace
207    classInfo.properties = createProperties(parseClassInfo.properties.public)
208    classInfo.functions = createClassFunctions(parseClassInfo.methods.public)
209
210    return classInfo
211}
212
213function putClassIntoNamespace(classInfo, namespaces) {
214    for (var i = 0; i < namespaces.length; ++i) {
215        if (namespaces[i].name === classInfo.namespace) {
216            namespaces[i].classes.push(classInfo)
217            return
218        }
219    }
220    NapiLog.logError('The namespace [%s] of class %s is not found.'.format(classInfo.namespace, classInfo.name));
221}
222
223function analyzeClasses(rootInfo, parseResult) {
224    let parseClasses = parseResult.classes;
225
226    for(var className in parseClasses) {
227        let classInfo = createClassInfo(parseClasses[className])
228        if (classInfo.namespace != '') {
229            // class in namespace
230            putClassIntoNamespace(classInfo, rootInfo.namespaces)
231        } else {
232            // class without namespace, put on root
233            rootInfo.classes.push(classInfo)
234        }
235    }
236}
237
238function getTab(tabLv) {
239    let tab = ""
240    for(var i = 0; i < tabLv; ++i) {
241        tab += "    "
242    }
243    return tab
244}
245
246function genFunction(func, tabLv, needDeclare = false) {
247    let tab = getTab(tabLv)
248    let funcPrefix = func.isClassFunc ? "" : "function "
249    let funcParams = ""
250    for (var i = 0; i < func.params.length; ++i) {
251        funcParams += i > 0 ? ", " : ""
252        funcParams += func.params[i].name + ": " + func.params[i].type
253    }
254    let declareStr = needDeclare ? "declare " : ""
255    return "%s%s%s%s(%s): %s;\n".format(tab, declareStr, funcPrefix, func.name, funcParams, func.retType)
256}
257
258function genClass(classInfo, tabLv, needDeclare = false) {
259    let tab = getTab(tabLv)
260    let declareStr = needDeclare ? "declare " : ""
261    let tsClass = tab + declareStr + "class " + classInfo.name + " {\n"
262    let tab1 = getTab(tabLv+1)
263    for (var i = 0; i < classInfo.properties.length; ++i) {
264        tsClass += "%s%s: %s;\n".format(tab1, classInfo.properties[i].name, classInfo.properties[i].type)
265    }
266    for (var i = 0; i < classInfo.functions.length; ++i) {
267        tsClass += genFunction(classInfo.functions[i], tabLv+1)
268    }
269    tsClass += tab + "}\n"
270    return tsClass
271}
272
273function genNamespace(namespace, tabLv) {
274    let tab = getTab(tabLv)
275    let tsNamespace = tab + "declare namespace %s {\n".format(namespace.name)
276    for(var i = 0; i < namespace.functions.length; ++i) {
277        tsNamespace += genFunction(namespace.functions[i], tabLv+1)
278    }
279    for(var i = 0; i < namespace.classes.length; ++i) {
280        tsNamespace += genClass(namespace.classes[i], tabLv+1)
281    }
282    tsNamespace += tab + "}\n"
283    return tsNamespace
284}
285
286function genTsContent(rootInfo) {
287    let tsContent = rootInfo.needCallback ? "import { AsyncCallback, Callback } from './../basic';\n\n" : ""
288
289    for(var i = 0; i < rootInfo.classes.length; ++i) {
290        tsContent += genClass(rootInfo.classes[i], 0, true)
291    }
292
293    for(var i = 0; i < rootInfo.namespaces.length; ++i) {
294        tsContent += genNamespace(rootInfo.namespaces[i], 0)
295    }
296
297    for(var i = 0; i < rootInfo.functions.length; ++i) {
298        tsContent += genFunction(rootInfo.functions[i], 0, true)
299    }
300
301    if (rootInfo.namespaces.length > 0) {
302        // export the first namespace as default
303        tsContent += "\nexport default %s;".format(rootInfo.namespaces[0].name)
304    }
305
306    return tsContent
307}
308
309function doGenerate(hFilePath, destDir) {
310    let parseResult = parseFileAll(hFilePath)
311    let rootInfo = {
312        "namespaces": [],
313        "classes": [],
314        "functions": [],
315        "needCallback": false
316    }
317    analyzeNameSpace(rootInfo, parseResult)
318    analyzeRootFunction(rootInfo, parseResult)
319    analyzeClasses(rootInfo, parseResult)
320    let hfileName = path.basename(hFilePath, ".h")
321    let tsFilePath = re.pathJoin(destDir, "%s.d.ts".format(hfileName))
322    let tsContent = genTsContent(rootInfo)
323    writeFile(tsFilePath,tsContent)
324}
325
326module.exports = {
327    doGenerate
328}
329