1/*
2* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd.
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15const { generateFunctionDirect } = require('./function_direct');
16const { generateFunctionSync } = require('./function_sync');
17const { generateFunctionAsync } = require('./function_async');
18const { generateInterface } = require('./interface');
19const { generateClass } = require('./class');
20const { generateType } = require('./type');
21const { FuncType, InterfaceList, EnumList, TypeList, CallFunctionList, isOnOffRegisterFunc, isCreateThreadsafeFunc } =
22require('../tools/common');
23const { generateEnum } = require('./enum');
24const { generateFunctionOnOff } = require('./function_onoff');
25const { generateThreadsafeFunc } = require('./function_threadsafe');
26
27const { NapiLog } = require('../tools/NapiLog');
28const { addUniqFunc2List, addUniqObj2List } = require('../tools/tool');
29
30function findParentByName(parentName, data) {
31    for (let i in data.interface) {
32        if (parentName === data.interface[i].name) {
33            return data.interface[i];
34        }
35    }
36
37    for (let i in data.class) {
38        if (parentName === data.class[i].name) {
39            return data.class[i];
40        }
41    }
42    return null;
43}
44
45/**
46 * 生成父类的成员变量和方法
47 * @param currentObj 当前类
48 * @param data 全局数据上下文
49 * @param parentBody 输出参数,保存父类的成员变量和方法
50 * @returns void
51 */
52function genParentPropties(currentObj, data, parentBody) {
53    for (let i in currentObj.body.parentNameList) {
54        let parentName = currentObj.body.parentNameList[i];
55        let parentObj = findParentByName(parentName, data);
56        if (!parentObj) {
57            NapiLog.logError("Failed to find %s's parent by name [%s]".format(currentObj.body.name, parentName));
58            return;
59        }
60
61        // 为父类添加子类的对象信息
62        addUniqObj2List(currentObj, parentObj.body.childList);
63
64        // 为当前类添加父类对象信息
65        addUniqObj2List(parentObj, currentObj.body.parentList);
66
67        for (let i in parentObj.body.value) {
68            // 添加父类的所有成员属性到parentBody
69            addUniqObj2List(parentObj.body.value[i], parentBody.value);
70        }
71        for (let i in parentObj.body.function) {
72            // 添加父类的所有成员方法到parentBody
73            addUniqFunc2List(parentObj.body.function[i], parentBody.function);
74        }
75        if (parentObj.body.parentNameList.length > 0) {
76            // 递归查找父类的父类
77            genParentPropties(parentObj, data, parentBody);
78        }
79    }
80}
81
82// 为有继承关系的interface和class类型建立父子类关系信息
83function genExtendsRelation(data) {
84    for (let i in data.interface) {
85        let ifObj = data.interface[i];
86        if (ifObj && ifObj.body.parentNameList && ifObj.body.parentNameList.length > 0) {
87            ifObj.body.parentBody = {value:[], function:[]};
88            genParentPropties(ifObj, data, ifObj.body.parentBody);
89        }
90    }
91
92    for (let i in data.class) {
93        let classObj = data.class[i];
94        if (classObj.body.parentName) {
95            classObj.body.parentBody = {value:[], function:[]};
96            genParentPropties(classObj, data, classObj.body.parentBody);
97        }
98    }
99}
100
101//生成module_middle.cpp、module.h、module.cpp
102function generateNamespace(name, data, inNamespace = '') {
103    let namespaceResult = { implH: '', implCpp: '', middleFunc: '', middleInit: '', declarationH: '', middleH: '', middleInitEnumDef: '' };
104    namespaceResult.middleInit += formatMiddleInit(inNamespace, name);
105    genExtendsRelation(data);
106    InterfaceList.push(data.interface);
107    TypeList.push(data.type);
108    EnumList.push(data.enum);
109    CallFunctionList.push(data.callFunction);
110    let toolNamespace = getToolNamespaceFunc(inNamespace, name);
111    enumNamespaceFunction(data, namespaceResult, inNamespace, name, toolNamespace);
112    for (let i in data.type) {
113      let ii = data.type[i];
114      let result = generateType(ii.name, ii.body, inNamespace + name + '::', inNamespace, name, toolNamespace);
115      namespaceResult = getNamespaceResult(result, namespaceResult);
116    }
117    for (let i in data.interface) {
118        let ii = data.interface[i];
119        let result = generateInterface(ii.name, ii.body, inNamespace + name + '::');
120        namespaceResult = getNamespaceResult(result, namespaceResult);
121    }
122    for (let i in data.class) {
123        let ii = data.class[i];
124        let result = generateClass(ii.name, ii.body, inNamespace + name + '::', ii.functiontType);
125        namespaceResult = getNamespaceResult(result, namespaceResult);
126    }
127    namespaceResult.implH = namespaceResult.declarationH + namespaceResult.implH;
128    for (let i in data.function) {
129        genNamespaceFunc(data, i, namespaceResult, inNamespace, name);
130    }
131    let flag = true;
132    if (data.namespace.length !== 0) {
133      flag = false;
134    }
135    for (let i in data.namespace) {
136        let ns = data.namespace[i];
137        let result = generateNamespace(ns.name, ns.body, inNamespace + name + '::');
138        namespaceResult = getNamespaceResult(result, namespaceResult);
139    }
140    InterfaceList.pop();
141    TypeList.pop();
142    EnumList.pop();
143    CallFunctionList.pop();
144    if (inNamespace.length > 0) {
145        namespaceResult.middleInit += '}';
146        flag = true;
147    }
148    return generateResult(name, namespaceResult.implH, namespaceResult.implCpp, namespaceResult.middleFunc,
149        namespaceResult.middleInit, namespaceResult.middleH, namespaceResult.middleInitEnumDef, flag);
150}
151
152function genNamespaceFunc(data, i, namespaceResult, inNamespace, name) {
153    let func = data.function[i];
154    let tmp = generateFunction(func, data, namespaceResult.implH);
155    namespaceResult.middleFunc += tmp[0];
156    namespaceResult.implH += tmp[1];
157    namespaceResult.implCpp += tmp[2];
158    namespaceResult.middleH += tmp[3];
159    let toolNamespace = getToolNamespaceFunc(inNamespace, name);
160    let middleTmp = '    pxt->DefineFunction("%s", %s%s::%s%s_middle%s);\n'
161      .format(func.name, inNamespace, name, toolNamespace, func.name, inNamespace.length > 0 ? ', ' + name : '');
162    if (namespaceResult.middleInit.indexOf(middleTmp) < 0) { // on方法不需要重复定义
163      namespaceResult.middleInit += middleTmp;
164    }
165}
166
167function getToolNamespaceFunc(inNamespace, name) {
168    let toolNamespace;
169    if (inNamespace !== '') {
170        let index = inNamespace.lastIndexOf('::');
171        let bodyTmp = inNamespace.substring(0, index);
172        let index2 = bodyTmp.lastIndexOf('::');
173        if (index2 > 0 && index2 < index) {
174            toolNamespace = inNamespace.substring(index2 + 2, index) + '_interface::';
175        } else {
176            toolNamespace = name + '_interface::';
177        }
178    } else {
179        toolNamespace = name + '_interface::';
180    }
181    return toolNamespace;
182}
183
184function enumNamespaceFunction(data, namespaceResult, inNamespace, nameSpaceName, toolNamespace) {
185  let result = generateEnumResult(data, inNamespace, nameSpaceName, toolNamespace);
186  namespaceResult.implH += result.implH;
187  namespaceResult.implCpp += result.implCpp;
188  namespaceResult.middleInit += result.middleInit;
189  namespaceResult.middleInitEnumDef += result.midInitEnumDefine;
190}
191
192function getNamespaceResult(subResult, returnResult) {
193    returnResult.middleFunc += subResult.middleBody;
194    returnResult.implH += subResult.implH;
195    returnResult.implCpp += subResult.implCpp;
196    returnResult.middleInit += subResult.middleInit;
197    returnResult.declarationH += subResult.declarationH;
198    returnResult.middleH += subResult.middleH;
199
200    if (subResult.midInitEnumDefine !== undefined) {
201      returnResult.middleInitEnumDef += subResult.midInitEnumDefine;
202    }
203
204    return returnResult;
205}
206
207  function generateEnumResult(data, inNamespace, nameSpaceName, toolNamespace) {
208    let resultEnum = {
209        implH: '',
210        implCpp: '',
211        middleInit: '',
212        midInitEnumDefine: ''
213    };
214
215    for (let i in data.enum) {
216        let enumm = data.enum[i];
217        let result = generateEnum(enumm.name, enumm.body, inNamespace, nameSpaceName, toolNamespace);
218        resultEnum.implH += result.implH;
219        resultEnum.implCpp += result.implCpp;
220        resultEnum.middleInit += result.midInitEnum;
221        resultEnum.midInitEnumDefine += result.midInitEnumDefine;
222    }
223    return resultEnum;
224}
225
226function generateResult(name, implH, implCpp, middleFunc, middleInit, middleH, middleInitEnumDef, flag) {
227  let result;
228  let middleEnumDefine = middleInitEnumDef.indexOf('undefined') >= 0 ? '' : middleInitEnumDef;
229  if (flag) {
230    result = {
231      implH: `\nnamespace %s {\nnamespace %s_interface {%s\n}\n}`.format(name, name, implH),
232      implCpp: `\nnamespace %s {\nnamespace %s_interface {%s\n}\n}`.format(name, name, implCpp),
233      middleBody: `\nnamespace %s {\nnamespace %s_interface {\n%s%s\n}\n}`.format(name, name, middleEnumDefine, middleFunc),
234      middleInit: middleInit,
235      middleH: `\nnamespace %s {\nnamespace %s_interface {%s\n}\n}`.format(name, name, middleH)
236    };
237  } else {
238        result = {
239        implH: `\nnamespace %s {%s\n}`.format(name, implH),
240        implCpp: `\nnamespace %s {%s}`.format(name, implCpp),
241        middleBody: `\nnamespace %s {\n%s%s}`.format(name, middleEnumDefine, middleFunc),
242        middleInit: middleInit,
243        middleH: `\nnamespace %s {%s\n}`.format(name, middleH)
244    };
245  }
246  return result;
247}
248
249function generateFunction(func, data, implH = null) {
250    let tmp;
251    let className;
252    if (isOnOffRegisterFunc(func.name)) {
253        return generateFunctionOnOff(func, data);
254    } else if (isCreateThreadsafeFunc(func.name)) {
255        return generateThreadsafeFunc(func, data);
256    }
257    switch (func.type) {
258        case FuncType.DIRECT:
259            tmp = generateFunctionDirect(func, data);
260            break;
261        case FuncType.SYNC:
262            tmp = generateFunctionSync(func, data);
263            break;
264        case FuncType.ASYNC:
265        case FuncType.PROMISE:
266            tmp = generateFunctionAsync(func, data, className, implH);
267            break;
268        default:
269            break;
270    }
271    return tmp;
272}
273
274function formatMiddleInit(inNamespace, name) {
275    let middleInit = '';
276    if (inNamespace.length > 0) {
277        let nsl = inNamespace.split('::');
278        nsl.pop();
279        let parentNs = nsl[nsl.length - 1];
280        middleInit = `{\nnapi_value %s = pxt->CreateSubObject(%s, "%s");\n`
281            .format(name, nsl.length === 1 ? 'exports' : parentNs, name);
282    }
283    return middleInit;
284}
285
286module.exports = {
287    generateNamespace,
288    getNamespaceResult,
289    generateEnumResult,
290    generateResult,
291    generateFunction,
292    formatMiddleInit
293};
294