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 re = require('../tools/re');
16const { FuncType, NumberIncrease, isEnum, EnumValueType, enumIndex, isType, typeIndex, isOnObjCallback,
17    getOnObjCallbackType, getLogErrInfo } = require('../tools/common');
18const { analyzeParams } = require('./params');
19const { analyzeReturn } = require('./return');
20const { NapiLog } = require('../tools/NapiLog');
21const { randomInt } = require('crypto');
22const { print } = require('../tools/tool');
23
24function analyzeSubInterface(data) {
25    let body = re.replaceAll(data, '\n', '').split(';'); //  # replace(' ', '').
26    let result = {
27        value: [],
28        function: [],
29        parentNameList: [],
30        childList: [],
31        parentList: [],
32    };
33    for (let i in body) {
34        let t = body[i];
35        while (t.length > 0 && t[0] === ' ') {
36            t = t.substring(1, t.length);
37        }
38        while (t.length > 0 && t[-1] === ' ') {
39            t = t.substring(0, t.length - 1);
40        }
41        if (t === '') {
42            break;
43        }
44        let tt = re.match(' *([a-zA-Z0-9_]+) *: *([a-zA-Z_0-9<>,:{}[\\] ]+)', t);
45        if (tt) { // 变量
46            analyzeSubInterfaceVariable(t, tt, result);
47        }
48    }
49    return result;
50}
51
52function analyzeSubInterfaceVariable(t, tt, result) {
53    let valueName = re.getReg(t, tt.regs[1]);
54    let valueType = re.getReg(t, tt.regs[2]);
55    let index = valueType.indexOf('number');
56    while (index !== -1) {
57        valueType = valueType.replace('number', 'NUMBER_TYPE_' + NumberIncrease.getAndIncrease());
58        index = valueType.indexOf('number');
59    }
60    result.value.push({
61        name: valueName,
62        type: valueType,
63    });
64}
65
66function getFuncParaType(v, interfaceName, data, results) {
67    let arrayType = re.match('(Async)*Callback<(Array<([a-zA-Z_0-9]+)>)>', v.type);
68    let parameter = v.type;
69    if (arrayType) {
70        parameter = re.getReg(v.type, arrayType.regs[2]);
71    }
72    if (isEnum(parameter, data)) {
73        let index = enumIndex(parameter, data);
74        if (data.enum[index].body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_NUMBER) {
75            v.type = v.type.replace(parameter, 'NUMBER_TYPE_' + NumberIncrease.getAndIncrease());
76        } else if (data.enum[index].body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_STRING) {
77            v.type = v.type.replace(parameter, 'string');
78        } else {
79            NapiLog.logError('analyzeFunction getFuncParaType is not support this type %s.'
80                .format(data.enum[index].body.enumValueType), getLogErrInfo);
81            return null;
82        }
83    }
84    // interface & class中的方法参数类型是enum的情况
85    else if (isEnum(parameter, results)) {
86        let index = enumIndex(parameter, results);
87        if (results.enum[index].body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_NUMBER) {
88            v.type = v.type.replace(parameter, 'NUMBER_TYPE_' + NumberIncrease.getAndIncrease());
89        } else if (results.enum[index].body.enumValueType === EnumValueType.ENUM_VALUE_TYPE_STRING) {
90            v.type = v.type.replace(parameter, 'string');
91        } else {
92            NapiLog.logError('analyzeFunction getFuncParaType is not support this type %s.'
93                .format(results.enum[index].body.enumValueType), getLogErrInfo());
94            return null;
95        }
96    }
97
98    let interfaceType = re.match('{([A-Za-z0-9_]+:[A-Za-z0-9_,]+)([A-Za-z0-9_]+:[A-Za-z0-9_]+)}$', v.type);
99    if (interfaceType) {
100        v.type = interfaceName;
101    }
102
103    if (parameter.indexOf('number') >= 0) {
104        v.type = v.type.replace('number', 'NUMBER_TYPE_' + NumberIncrease.getAndIncrease());
105    }
106
107    // type的处理
108    if (isType(parameter, data)) {
109        let index = typeIndex(parameter, data);
110        if (data.type[index].isEnum) {
111            v.type = v.type.replace(parameter, 'string');
112        }
113    }
114    return v;
115}
116
117function analyzeFuncNoNameInterface(data, values, results) {
118    values = re.replaceAll(re.replaceAll(values, ' ', ''), '\n', '');
119    let interfaceName = '';
120    let matchNoName = '([:{<}>,;a-zA-Z_0-9]*)\\?*(:[A-Za-z0-9_,;]*)?:((Async)*Callback<)?{(([A-Za-z0-9_]+:' +
121        '[A-Za-z0-9_,;]+)*)([A-Za-z0-9_]+:[A-Za-z0-9_]+)}(}|,|;|>)?$';
122    let matchs = re.match(matchNoName, values);
123    if (matchs) {
124        let st = values.lastIndexOf('{');
125        let end = values.indexOf('}');
126        let number = NumberIncrease.getAndIncrease();
127        interfaceName = 'AUTO_INTERFACE_%s'.format(number);
128        let interfaceBody = values.substring(st + 1, end);
129        let typeInterface = '{%s}'.format(interfaceBody);
130        values = re.replaceAll(values, typeInterface, interfaceName);
131        interfaceBody = re.replaceAll(interfaceBody, ',', ';');
132        if (Object.prototype.hasOwnProperty.call(data, 'interface')) {
133            data.interface.push({
134                name: interfaceName,
135                body: analyzeSubInterface(interfaceBody),
136            });
137        } else if (Object.prototype.hasOwnProperty.call(results, 'interface')) {
138            results.interface.push({
139                name: interfaceName,
140                body: analyzeSubInterface(interfaceBody),
141            });
142        }
143    }
144
145    matchs = re.match(matchNoName, values);
146    if (matchs) {
147        let resNoNameInter = analyzeFuncNoNameInterface(data, values);
148        values = resNoNameInter.values;
149    }
150
151    let result = {
152        interfaceName: interfaceName,
153        values: values,
154    };
155    return result;
156}
157
158function analyseSubReturn(ret, data, results) {
159    // 匿名interface返回值 function fun4(input: string): { read: number; written: number };
160    let tt = null;
161    if (ret.indexOf(':') > 0) {
162        ret = re.replaceAll(re.replaceAll(ret, ' ', ''), '\n', '');
163        ret = re.replaceAll(ret, ',', ';');
164        ret = ret.substring(1, ret.length - 1);
165        tt = ret.split(';');
166    }
167    if (tt) {
168        let len = tt.length;
169        let res = '';
170        let interfaceName = '';
171        for (let i = 0; i < len; i++) {
172            let regs1 = tt[i] + ';';
173            res += regs1;
174        }
175
176        let number = NumberIncrease.getAndIncrease();
177        interfaceName = 'AUTO_INTERFACE_%s'.format(number);
178        let interfaceBody = res;
179        ret = interfaceName;
180
181        interfaceBody = re.replaceAll(interfaceBody, ',', ';');
182        if (Object.prototype.hasOwnProperty.call(data, 'interface')) {
183            data.interface.push({
184                name: interfaceName,
185                body: analyzeSubInterface(interfaceBody),
186            });
187        } else if (Object.prototype.hasOwnProperty.call(results, 'interface')) {
188            results.interface.push({
189                name: interfaceName,
190                body: analyzeSubInterface(interfaceBody),
191            });
192        }
193    }
194    if (ret.indexOf('number') >= 0) {
195        ret = ret.replaceAll('number', 'NUMBER_TYPE_' + NumberIncrease.getAndIncrease());
196    }
197    return ret;
198}
199
200function getObjCallFunc(results, onObjCbType, values, ret) {
201    if (results !== undefined) {
202        results.callFunction.push({
203            'name': onObjCbType,
204            'body': values,
205            'ret': ret,
206        });
207    }
208}
209function getFuncResult(name, funcType, values, ret, isStatic) {
210    let result = {
211        name: name,
212        type: funcType,
213        value: values,
214        ret: ret,
215        isStatic: isStatic,
216    };
217    return result;
218}
219
220function getArrowCallFunc(tmp, results) {
221    let callbackFunc = null;
222
223    if (tmp[2][0] !== undefined) {
224        callbackFunc = tmp[2][0]; // 当方法的参数是回调方法,并且回调方法写法为=>函数
225    }
226    if (results !== undefined && callbackFunc !== null) {
227        results.callFunction.push(callbackFunc);
228    }
229}
230
231/**函数解析 */
232function analyzeFunction(data, isStatic, name, values, ret, results, interfaceName = '') {
233    let res = analyzeFuncNoNameInterface(data, values, results);
234    let tmp;
235    let funcType;
236
237    if (res) {
238        tmp = analyzeParams(name, res.values);
239        if (tmp !== null) {
240            values = tmp[0];
241            funcType = tmp[1];
242            getArrowCallFunc(tmp, results);
243        }
244    }
245
246    tmp = analyzeReturn(ret);
247    ret = tmp[0];
248    if (tmp[1]) { // 返回类型为 Promise, 解析成等价的AsyncCallback方法
249        funcType = FuncType.ASYNC;
250        // 返回值是Promise的匿名interface
251        let paramTypeVal = analyseSubReturn(ret.substring(8, ret.length - 1), data, results);
252        // 将返回值Promise<type>改为AsyncCallback<type>,作为方法的入参
253        let paramType = ret.replace('Promise', 'AsyncCallback');
254        if (paramTypeVal) {
255            // 匿名interface处理
256            let paramAsync = paramType.substring(14, paramType.length - 1);
257            paramType = paramType.replace(paramAsync, paramTypeVal);
258        }
259        values.push({ name: 'promise', optional: false, type: paramType });
260        ret = 'void'; // 返回值由Promise改为void,与AsyncCallback接口保持一致
261    }
262    for (let j in values) {
263        let v = values[j];
264        v = getFuncParaType(v, res.interfaceName, data, results);
265        if (v === null) {
266            NapiLog.logError('analyzeFunction is not support this type %s.'.format(v), getLogErrInfo());
267        }
268    }
269    ret = analyseSubReturn(ret, data, results);
270    let result = getFuncResult(name, funcType, values, ret, isStatic);
271    if (isOnObjCallback(name)) {
272        let onObjCbType = getOnObjCallbackType(name, interfaceName);
273        getObjCallFunc(results, onObjCbType, values, ret);
274    }
275    return result;
276}
277
278module.exports = {
279    analyzeFunction,
280    analyzeSubInterface,
281    getFuncParaType,
282    analyzeFuncNoNameInterface,
283    analyseSubReturn,
284};