support interface func is on

Signed-off-by: gou-jingjing <goujingjing@kaihong.com>
This commit is contained in:
gou-jingjing 2023-10-19 20:19:22 +08:00
parent 7f0d31cc11
commit 4bd16fcfe3
13 changed files with 233 additions and 75 deletions

View File

@ -194,10 +194,15 @@ function analyzeFunction(data, isStatic, name, values, ret, results) {
let res = analyzeFuncNoNameInterface(data, values, results)
let tmp
let funcType
let callbackFunc = null
if (res) {
tmp = analyzeParams(name, res.values)
values = tmp[0]
funcType = tmp[1]
callbackFunc = tmp[2] // 当方法的参数是回调方法,并且回调方法写法为=>函数
if (results != undefined && callbackFunc) {
results.callFunction.push(callbackFunc)
}
}
tmp = analyzeReturn(ret)

View File

@ -87,7 +87,7 @@ function analyzeInterface(data, rsltInterface = null, results) { // same as clas
optional: optionalFlag
})
}
tt = re.match("(static )* *(\\$*[A-Za-z0-9_]+) *[:]? *\\(([\n 'a-zA-Z:;=,_0-9?<>{}|[\\]]*)\\)"
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])

View File

@ -47,6 +47,7 @@ function analyzeNamespace(data) {
interface: [],
class: [],
namespace: [],
callFunction: [],
}
while (data != '\n') {
let oldData = data

View File

@ -14,12 +14,44 @@
*/
const re = require("../tools/re");
const { checkOutBody, print } = require("../tools/tool");
const { FuncType } = require("../tools/common");
const { FuncType, NumberIncrease } = require("../tools/common");
const { NapiLog } = require("../tools/NapiLog");
/**
* 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)
rsltCallFunction.push({
"name": functionTypeName,
"body": tmp[0],
"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) {
@ -36,10 +68,12 @@ function analyzeParams(funcName, values) {
}
if (matchs != null) {
let type = re.getReg(v, matchs.regs[3])
if (type.indexOf("Map") < 0) {
if (type.indexOf("Map") < 0 && type.indexOf("=>") < 0) {
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) {
@ -54,7 +88,8 @@ function analyzeParams(funcName, values) {
result.push({ "name": re.getReg(v, matchs.regs[1]), "type": type , "optional": optionalFlag})
if (type.indexOf("AsyncCallback") >= 0)
funcType = FuncType.ASYNC
if (funcType == FuncType.DIRECT && type.indexOf("Callback") >= 0 && type.indexOf("AsyncCallback") < 0)
if (funcType == FuncType.DIRECT && type.indexOf("Callback") >= 0 && type.indexOf("AsyncCallback") < 0 ||
type.indexOf("AsyncCallback") < 0 && type.indexOf("=>") > 0)
funcType = FuncType.SYNC
}
}
@ -63,7 +98,7 @@ function analyzeParams(funcName, values) {
NapiLog.logError("Failed to analyse parameter [%s] of function [%s].".format(v, funcName));
}
}
return [result, funcType]
return [result, funcType, rsltCallFunction]
}
module.exports = {

View File

@ -1673,27 +1673,39 @@ void XNapiTool::CallAsyncFunc(AsyncFunc *pAsyncFuncs, napi_value ret)
napi_handle_scope scope = nullptr;
napi_open_handle_scope(paf->env_, &scope);
napi_value cb = 0;
napi_value cb;
napi_status result_status = napi_get_reference_value(paf->env_, paf->funcRef_, &cb);
CC_ASSERT(result_status == napi_ok);
napi_value thisvar = 0;
napi_value thisvar;
result_status = napi_get_reference_value(paf->env_, paf->thisVarRef_, &thisvar);
CC_ASSERT(result_status == napi_ok);
napi_value retValue = 0;
napi_value retValue;
result_status = napi_get_reference_value(paf->env_, data->resultRef, &retValue);
CC_ASSERT(result_status == napi_ok);
napi_value args[1] = {retValue};
uint32_t length = 0;
napi_value element;
napi_get_array_length(paf->env_, retValue, &length);
napi_value* args = reinterpret_cast<napi_value*>(malloc(sizeof(napi_value) * length));
if (args == NULL) {
// 内存分配失败的处理
return;
}
for (uint32_t i = 0; i < length; i++) {
napi_get_element(paf->env_, retValue, i, &element);
napi_set_element(paf->env_, *args, i, element);
}
napi_value cb_result;
result_status = napi_call_function(paf->env_, thisvar, cb, 1, args, &cb_result);
result_status = napi_call_function(paf->env_, thisvar, cb, length, args, &cb_result);
CC_ASSERT(result_status == napi_ok);
result_status = napi_delete_reference(paf->env_, data->resultRef);
CC_ASSERT(result_status == napi_ok);
napi_close_handle_scope(paf->env_, scope);
free(args);
free(data);
delete work;
});

View File

@ -13,6 +13,7 @@
* limitations under the License.
*/
const { replaceAll } = require("../tools/tool");
const re = require("../tools/re");
const { eventParamGenerate } = require("./param_generate");
const { returnGenerate } = require("./return_generate");
const { cToJs } = require("./return_generate");
@ -49,7 +50,7 @@ struct [funcName]_value_struct {
`
let middleAsyncCallbackTemplate = `
void AsyncCallback(const std::string &eventName, [callback_param_type] &ret)
void [eventNames]AsyncCallback(const std::string &eventName, [callback_param_type])
{
if(XNapiTool::asyncFuncs_.count(eventName) <= 0) {
return;
@ -58,19 +59,27 @@ void AsyncCallback(const std::string &eventName, [callback_param_type] &ret)
napi_value exports = nullptr;
XNapiTool *pxt = std::make_unique<XNapiTool>(pAsyncFuncs->env_, exports).release();
napi_value result = nullptr;
napi_status status = napi_create_array(pAsyncFuncs->env_, &result);
if (status != napi_ok) {
return; // napi数组创建失败
}
[native_return_define]
[native_return]
[value_set_array]
XNapiTool::CallAsyncFunc(pAsyncFuncs, result);
delete pxt;
}
`
let middleEventCallbakTemplate = `
void [eventName]Callback([callback_param_type] &ret) {
AsyncCallback("[eventName]", ret);
void [eventName]Callback([callback_param_type]) {
struct on_value_struct *vio = new on_value_struct();
[eventName]AsyncCallback(vio->eventName, [callback_param_name]);
delete vio;
}
`
let implHEventCallbakTemplate = `
void [eventName]Callback([callback_param_type] &ret);
void [eventName]Callback([callback_param_type]);
`
function isOnTypeExist(onTypeList, newType) {
@ -136,26 +145,46 @@ return true;
}
function gennerateEventCallback(codeContext, data, param) {
returnGenerate(param.callback, param, data)
let paramType = param.valueOut.substring(0, param.valueOut.length - "out;".length)
let realParamType = paramType.substring(0, 12) == "NUMBER_TYPE_" ? "uint32_t" : paramType
if (!isOnTypeExist(data.onTypeList, realParamType)) {
let params = ''; // 回调的一个或者多个参数
let useParams = ''; // 使用回调的一个或者多个参数
let nativeReturn = ''
let resultDefine = ''
let valueSetArray = ''
for (let i = 0; i < param.callback.length; i++) {
returnGenerate(param.callback[i], param, data, i)
let paramType = param.valueOut.substring(0, param.valueOut.length - "out;".length) // 待修改
paramType = re.replaceAll(paramType, " ", "")
let realParamType = paramType.substring(0, 12) == "NUMBER_TYPE_" ? "uint32_t" : paramType // 待修改
let tag = i == param.callback.length - 1? '' : ', '
params += realParamType + ' &valueIn' + i + tag // 定义回调函数输入的参数
useParams += 'valueIn' + i + tag // 使用回调函数输入的参数
// if (!isOnTypeExist(data.onTypeList, realParamType)) {
// 为每种callback参数类型的on方法生成一个统一回调方法
let nativeReturn = cToJs("ret", param.callback.type, "result")
let callbackFunc = replaceAll(middleAsyncCallbackTemplate, "[callback_param_type]", realParamType)
callbackFunc = replaceAll(callbackFunc, "[native_return]", nativeReturn)
codeContext.middleFunc += callbackFunc
resultDefine += 'napi_value result%d = nullptr;\n '.format(i)
nativeReturn += cToJs("valueIn" + i, param.callback[i].type, "result" + i) + '\n' // 待修改
valueSetArray += 'napi_set_element(pAsyncFuncs->env_, result, %d, result%d);\n '.format(i, i)
addOnTypeToList(data, realParamType)
// }
}
let callbackFunc = middleAsyncCallbackTemplate
callbackFunc = replaceAll(middleAsyncCallbackTemplate, "[eventNames]", param.eventName)
callbackFunc = replaceAll(callbackFunc, "[callback_param_type]", params)
callbackFunc = replaceAll(callbackFunc, "[native_return_define]", resultDefine)
callbackFunc = replaceAll(callbackFunc, "[native_return]", nativeReturn)
callbackFunc = replaceAll(callbackFunc, "[callback_param_length]", param.callback.length)
callbackFunc = replaceAll(callbackFunc, "[value_set_array]", valueSetArray)
codeContext.middleFunc += callbackFunc
// 为每个on的event事件生成回调方法
let middleEventCallBack = replaceAll(middleEventCallbakTemplate, "[eventName]", param.eventName)
middleEventCallBack = replaceAll(middleEventCallBack, "[callback_param_type]", realParamType)
middleEventCallBack = replaceAll(middleEventCallBack, "[callback_param_name]", useParams)
middleEventCallBack = replaceAll(middleEventCallBack, "[callback_param_type]", params)
codeContext.middleFunc += middleEventCallBack;
// 为每个on的event事件生成回调接口供用户侧使用
let implHCallBack = replaceAll(implHEventCallbakTemplate, "[eventName]", param.eventName)
implHCallBack = replaceAll(implHCallBack, "[callback_param_type]", paramType)
implHCallBack = replaceAll(implHCallBack, "[callback_param_type]", params)
codeContext.implH += implHCallBack
}
@ -168,7 +197,8 @@ function generateFunctionOnOff(func, data, className) {
valuePackage: "", // 输出参数打包
valueDefine: "", // impl参数定义
eventName:"", // 注册/去注册事件名称
optionalParamDestory: "" // 可选参数内存释放
optionalParamDestory: "", // 可选参数内存释放
callback: [] // 回调函数参数
}
for (let i in func.value) {

View File

@ -15,6 +15,7 @@
const { generateFunctionDirect } = require("./function_direct");
const { generateFunctionSync } = require("./function_sync");
const { generateFunctionAsync } = require("./function_async");
const { generateFunctionOnOff } = require("./function_onoff");
const { FuncType, InterfaceList, getArrayType, getArrayTypeTwo, getMapType, EnumList, jsType2CType }
= require("../tools/common");
const { jsToC, getCType, paramGenerate } = require("./param_generate");
@ -366,6 +367,10 @@ function connectResult(data, inNamespace, name) {
for (let i in data.allProperties.functions) {
let func = data.allProperties.functions[i]
let tmp;
if (func.name == 'on' || func.name == 'off' ) {
tmp = generateFunctionOnOff(func, data, name)
}
if (!tmp) {
switch (func.type) {
case FuncType.DIRECT:
tmp = generateFunctionDirect(func, data, name, implH)
@ -380,6 +385,7 @@ function connectResult(data, inNamespace, name) {
default:
return
}
}
middleFunc += tmp[0]
implH += tmp[1]
implCpp += tmp[2]

View File

@ -18,7 +18,7 @@ const { generateFunctionAsync } = require("./function_async");
const { generateInterface } = require("./interface");
const { generateClass } = require("./class");
const { generateType } = require("./type");
const { FuncType, InterfaceList, EnumList, TypeList } = require("../tools/common");
const { FuncType, InterfaceList, EnumList, TypeList, CallFunctionList } = require("../tools/common");
const { generateEnum } = require("./enum");
const { generateFunctionOnOff } = require("./function_onoff");
const { NapiLog } = require("../tools/NapiLog");
@ -103,6 +103,7 @@ function generateNamespace(name, data, inNamespace = "") {
InterfaceList.push(data.interface)
TypeList.push(data.type)
EnumList.push(data.enum)
CallFunctionList.push(data.callFunction)
enumNamespaceFunction(data, namespaceResult);
for (let i in data.type) {
let ii = data.type[i]
@ -137,6 +138,7 @@ function generateNamespace(name, data, inNamespace = "") {
InterfaceList.pop();
TypeList.pop();
EnumList.pop();
CallFunctionList.pop();
if (inNamespace.length > 0) {
namespaceResult.middleInit += "}"
}

View File

@ -14,7 +14,7 @@
*/
const { InterfaceList, getArrayType, getArrayTypeTwo, NumberIncrease,
enumIndex, isEnum, EnumValueType, getMapType,
EnumList, getUnionType, TypeList } = require("../tools/common");
EnumList, getUnionType, TypeList, CallFunctionList } = require("../tools/common");
const re = require("../tools/re");
const { NapiLog } = require("../tools/NapiLog");
const { getConstNum } = require("../tools/tool");
@ -764,7 +764,7 @@ function mapArray(mapType, napiVn, dest, lt) {
function paramGenerateCallBack(data, funcValue, param, p) {
let type = funcValue.type
let arrayType = re.match("(Async)*Callback<(Array<([a-zA-Z_0-9]+)>)>", type)
let regType
let regType = type
if (arrayType) {
regType = re.getReg(type, arrayType.regs[2])
}
@ -789,12 +789,18 @@ function paramGenerateCallBack(data, funcValue, param, p) {
return
}
}
param.callback = {
let paramCallback = {
type: regType,
offset: p,
optional: funcValue.optional,
isAsync: type.indexOf("AsyncCallback") >= 0
}
if (param.callback) {
param.callback.push(paramCallback)
} else {
param.callback = paramCallback
}
}
function isArrayType(type) {
@ -1023,9 +1029,18 @@ function eventParamGenerate(p, funcValue, param, data) {
if (type.substring(0, 9) == "Callback<" || type.substring(0, 14) == "AsyncCallback<") {
// callback参数处理
paramGenerateCallBack(data, funcValue, param, p)
} else if (CallFunctionList.getValue(type)) { // 判断条件
// callFunction => 函数参数处理
let funcBody = CallFunctionList.getValue(type)[0] // 取出回调方法参数
for (let i in funcBody) {
paramGenerateCallBack(data, funcBody[i], param, p)
}
} else if (regName) {
// event type参数处理
param.eventName = re.getReg(type, regName.regs[1])
param.eventName = re.getReg(type, regName.regs[1]) // string类型如何处理
if (param.eventName == "string") {
param.eventName = "string%d".format(NumberIncrease.getAndIncrease())
}
param.valueDefine += "%sstd::string &%s".format(param.valueDefine.length > 0 ? ", " : "", name)
} else {
NapiLog.logError("function eventParamGenerate:The current version do not support to this param to generate :"

View File

@ -455,11 +455,13 @@ function returnGenerateObject(returnInfo, param, data) {
* @param param 方法的所有参数信息
* @returns 返回参数的填充代码123 返回测试的值
*/
function getReturnFill(returnInfo, param) {
function getReturnFill(returnInfo, param, i) {
let type = returnInfo.type
let valueFillStr = ""
if (param.callback) { // callback方法的返回参数处理
if (param.callback.isAsync) {
let isCallback = i == undefined? param.callback : param.callback[i]
if (isCallback) { // callback方法的返回参数处理
let isCallbackAsync = i == undefined? param.callback.isAsync : param.callback[i].isAsync
if (isCallbackAsync) {
// 异步callback方法返回的是一个结构体包含errcode和data两部分 详见basic.d.ts中AsyncCallback的定义
valueFillStr = "vio->outErrCode"
param.valueDefine += "%suint32_t& outErrCode".format(param.valueDefine.length > 0 ? ", " : "")
@ -482,7 +484,7 @@ function isObjectType(type) {
return false;
}
function generateOptionalAndUnion(returnInfo, param, data, outParam) {
function generateOptionalAndUnion(returnInfo, param, data, outParam, i) {
let type = returnInfo.type
if (type === undefined) {
NapiLog.logError("returnGenerate: type of returnInfo is undefined!");
@ -494,24 +496,28 @@ function generateOptionalAndUnion(returnInfo, param, data, outParam) {
}
if (!isEnum(type, data)) {
if (i != undefined) {
param.valuePackage = cToJs(outParam, type, "result" + i)
} else {
param.valuePackage = cToJs(outParam, type, "result")
}
} else if (type.indexOf("|") >= 0) {
returnGenerateUnion(param)
}
}
function returnGenerate(returnInfo, param, data) {
function returnGenerate(returnInfo, param, data, i) {
let type = returnInfo.type
if (type === undefined) {
NapiLog.logError("returnGenerate: type of returnInfo is undefined!");
return;
}
let valueFillStr = getReturnFill(returnInfo, param)
let valueFillStr = getReturnFill(returnInfo, param, i)
param.valueFill += ("%s" + valueFillStr).format(param.valueFill.length > 0 ? ", " : "")
let outParam = returnInfo.optional ? "(*vio->out)" : "vio->out"
let modifiers = returnInfo.optional ? "*" : "&"
generateOptionalAndUnion(returnInfo, param, data, outParam);
generateOptionalAndUnion(returnInfo, param, data, outParam, i);
if (type == "string") {
param.valueOut = returnInfo.optional ? "std::string* out = nullptr;" : "std::string out;"

View File

@ -86,6 +86,27 @@ InterfaceList.getValue = function (name) {
return null;
}
class CallFunctionList { }
CallFunctionList.callFuncs = [];
CallFunctionList.push = function (ifs) {
CallFunctionList.callFuncs.push(ifs)
}
CallFunctionList.pop = function () {
CallFunctionList.callFuncs.pop()
}
CallFunctionList.getValue = function (name) {
let cfs = CallFunctionList.callFuncs[CallFunctionList.callFuncs.length - 1]
for (let i = 0; i < cfs.length; i++) {
let len = cfs[i].length
for (let j = 0; j < len; j++) {
if (cfs[i][j].name == name) {
return [cfs[i][j].body, cfs[i][j].ret]
}
}
}
return null
}
class TypeList { }
TypeList.types = [];
TypeList.push = function (ifs) {
@ -294,6 +315,7 @@ module.exports = {
NumberIncrease,
InterfaceList,
TypeList,
CallFunctionList,
isType,
typeIndex,
getArrayType,

View File

@ -32,15 +32,17 @@ declare namespace napitest {
on(type: string, callback: Callback<ModelEvent>): void; // Callback为interface
}
interface TestClass3 {
on(type: string, callback: Callback<{topic:string,message:string}>): void; // Callback为匿名interface
}
// interface TestClass3 {
// on(type: string, callback: Callback<{topic:string,message:string}>): void; // Callback为匿名interface
// }
// function on(type: "heartbeat", callback: Callback<boolean>): void; // 固定事件回调参数为boolean待支持
// function on(type: "heartbeat", callback: Callback<ModelEvent>): void; // 固定事件回调参数为ModelEvent待支持
// function on(type: string, callback: (wid: boolean) => void): void; // 箭头函数待支持
// function on(type: string, callback: (wid: boolean) => string): void; // 返回值待支持
// on(type: 'inputStart', callback: (wid: boolean, modeEv: ModelEvent) => void): void // 回调函数参数个数大于1待支持
interface TestClass4 {
on(type: "heartbeat", callback: Callback<boolean>): void; // 固定事件回调参数为boolean待支持
on(type: "heartbeat2", callback: Callback<ModelEvent>): void; // 固定事件回调参数为ModelEvent待支持
on(type: string, callback: (wid: boolean) => void): void; // 箭头函数待支持
// // on(type: string, callback: (wid: boolean) => string): void; // 返回值待支持
on(type: "inputStart", callback: (wid: boolean, modeEv: ModelEvent) => void): void // 回调函数参数个数大于1待支持
}
}
export default napitest;

View File

@ -12,7 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { TestClass1, TestClass2, ModelEvent, TestClass3, on} = require("./out/build/Release/napitest")
const { TestClass1, TestClass2, ModelEvent, TestClass3, on, TestClass4} = require("./out/build/Release/napitest")
var assert = require("assert");
describe('on', function () {
@ -59,12 +59,34 @@ describe('on', function () {
assert.strictEqual(ret, true);
});
// interface TestClass3 {
// on(type: string, callback: Callback<{topic:string,message:string}>): void; // Callback为匿名interface
// // interface TestClass3 {
// // on(type: string, callback: Callback<{topic:string,message:string}>): void; // Callback为匿名interface
// // }
// let tc3 = new TestClass3();
// it('test TestClass3 on', function () {
// tc3.on('OnEvent', onCallbackTest3);
// assert.strictEqual(ret, true);
// });
// interface TestClass4 {
// on(type: "heartbeat", callback: Callback<boolean>): void; // 固定事件回调参数为boolean待支持
// on(type: "heartbeat2", callback: Callback<ModelEvent>): void; // 固定事件回调参数为ModelEvent待支持
// on(type: string, callback: (wid: boolean) => void): void; // 箭头函数待支持
// // // on(type: string, callback: (wid: boolean) => string): void; // 返回值待支持
// on(type: "inputStart", callback: (wid: boolean, modeEv: ModelEvent) => void): void // 回调函数参数个数大于1待支持
// }
let tc3 = new TestClass3();
it('test TestClass3 on', function () {
tc3.on('OnEvent', onCallbackTest3);
let tc4 = new TestClass4();
it('test TestClass4 on', function () {
tc4.on('heartbeat', onCallback);
assert.strictEqual(ret, true);
ret = false;
tc4.on('heartbeat2', onCallback);
assert.strictEqual(ret, true);
ret = false;
tc4.on('test', onCallback);
assert.strictEqual(ret, true);
ret = false;
tc4.on('inputStart', onCallback);
assert.strictEqual(ret, true);
});
});