api: auto generate api function wrappers for viml

This commit is contained in:
Björn Linse 2016-06-18 12:06:50 +02:00
parent de3a515123
commit 3bd3b3b768
6 changed files with 77 additions and 35 deletions

View File

@ -41,18 +41,20 @@ c_proto = Ct(
) )
grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1) grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1)
-- we need at least 2 arguments since the last one is the output file -- we need at least 3 arguments since the last two are output files
assert(#arg >= 1) assert(#arg >= 2)
functions = {} functions = {}
-- names of all headers relative to the source root (for inclusion in the -- names of all headers relative to the source root (for inclusion in the
-- generated file) -- generated file)
headers = {} headers = {}
-- output file(dispatch function + metadata serialized with msgpack) -- output c file(dispatch function + metadata serialized with msgpack)
outputf = arg[#arg] outputf = arg[#arg-1]
-- output mpack file (metadata)
mpack_outputf = arg[#arg]
-- read each input file, parse and append to the api metadata -- read each input file, parse and append to the api metadata
for i = 1, #arg - 1 do for i = 1, #arg - 2 do
local full_path = arg[i] local full_path = arg[i]
local parts = {} local parts = {}
for part in string.gmatch(full_path, '[^/]+') do for part in string.gmatch(full_path, '[^/]+') do
@ -165,7 +167,7 @@ for i = 1, #functions do
local fn = functions[i] local fn = functions[i]
local args = {} local args = {}
output:write('static Object handle_'..fn.name..'(uint64_t channel_id, uint64_t request_id, Array args, Error *error)') output:write('Object handle_'..fn.name..'(uint64_t channel_id, uint64_t request_id, Array args, Error *error)')
output:write('\n{') output:write('\n{')
output:write('\n Object ret = NIL;') output:write('\n Object ret = NIL;')
-- Declare/initialize variables that will hold converted arguments -- Declare/initialize variables that will hold converted arguments
@ -311,3 +313,7 @@ MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name,
]]) ]])
output:close() output:close()
mpack_output = io.open(mpack_outputf, 'wb')
mpack_output:write(packed)
mpack_output:close()

View File

@ -1,5 +1,8 @@
mpack = require('mpack')
local nvimsrcdir = arg[1] local nvimsrcdir = arg[1]
local autodir = arg[2] local autodir = arg[2]
local metadata_file = arg[3]
if nvimsrcdir == '--help' then if nvimsrcdir == '--help' then
print([[ print([[
@ -18,9 +21,20 @@ local funcsfname = autodir .. '/funcs.generated.h'
local funcspipe = io.open(funcsfname .. '.hsh', 'w') local funcspipe = io.open(funcsfname .. '.hsh', 'w')
local funcs = require('eval') local funcs = require('eval').funcs
for name, def in pairs(funcs.funcs) do local metadata = mpack.unpack(io.open(arg[3], 'rb'):read("*all"))
for i,fun in ipairs(metadata) do
funcs['api_'..fun.name] = {
args=#fun.parameters,
func='api_wrapper',
data='handle_'..fun.name,
}
end
for name, def in pairs(funcs) do
args = def.args or 0 args = def.args or 0
if type(args) == 'number' then if type(args) == 'number' then
args = {args, args} args = {args, args}
@ -28,7 +42,8 @@ for name, def in pairs(funcs.funcs) do
args[2] = 'MAX_FUNC_ARGS' args[2] = 'MAX_FUNC_ARGS'
end end
func = def.func or ('f_' .. name) func = def.func or ('f_' .. name)
local val = ('{ %s, %s, &%s }'):format(args[1], args[2], func) data = def.data or "NULL"
local val = ('{ %s, %s, &%s, %s }'):format(args[1], args[2], func, data)
funcspipe:write(name .. '\n' .. val .. '\n') funcspipe:write(name .. '\n' .. val .. '\n')
end end
funcspipe:close() funcspipe:close()

View File

@ -14,6 +14,7 @@ set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto)
set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendispatch.lua) set(DISPATCH_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendispatch.lua)
file(GLOB API_HEADERS api/*.h) file(GLOB API_HEADERS api/*.h)
file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h) file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h)
set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack)
set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua) set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua)
set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include) set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include)
set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch.c) set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch.c)
@ -192,8 +193,8 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES}
${EASTASIANWIDTH_FILE} ${EASTASIANWIDTH_FILE}
) )
add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${API_METADATA}
COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${API_HEADERS} ${GENERATED_API_DISPATCH} COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${API_HEADERS} ${GENERATED_API_DISPATCH} ${API_METADATA}
DEPENDS DEPENDS
${API_HEADERS} ${API_HEADERS}
${MSGPACK_RPC_HEADERS} ${MSGPACK_RPC_HEADERS}
@ -220,10 +221,10 @@ add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS}
add_custom_command(OUTPUT ${GENERATED_FUNCS} add_custom_command(OUTPUT ${GENERATED_FUNCS}
COMMAND ${LUA_PRG} ${FUNCS_GENERATOR} COMMAND ${LUA_PRG} ${FUNCS_GENERATOR}
${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_DIR} ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_DIR} ${API_METADATA}
COMMAND $<TARGET_FILE:genhash> COMMAND $<TARGET_FILE:genhash>
${GENERATED_FUNCS_HASH_INPUT} ${GENERATED_FUNCS} functions functions VimLFuncDef "NOFUNC" ${GENERATED_FUNCS_HASH_INPUT} ${GENERATED_FUNCS} functions functions VimLFuncDef "NOFUNC"
DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} genhash DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} ${API_METADATA} genhash
) )
add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}

View File

@ -3,13 +3,15 @@
#include "nvim/api/private/defs.h" #include "nvim/api/private/defs.h"
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores typedef Object (*ApiDispatchWrapper)(uint64_t channel_id,
/// functions of this type.
typedef struct {
Object (*fn)(uint64_t channel_id,
uint64_t request_id, uint64_t request_id,
Array args, Array args,
Error *error); Error *error);
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
/// functions of this type.
typedef struct {
ApiDispatchWrapper fn;
bool async; // function is always safe to run immediately instead of being bool async; // function is always safe to run immediately instead of being
// put in a request queue for handling when nvim waits for input. // put in a request queue for handling when nvim waits for input.
} MsgpackRpcRequestHandler; } MsgpackRpcRequestHandler;

View File

@ -445,13 +445,14 @@ typedef struct {
} timer_T; } timer_T;
/// Prototype of C function that implements VimL function /// Prototype of C function that implements VimL function
typedef void (*VimLFunc)(typval_T *args, typval_T *rvar); typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, void *data);
/// Structure holding VimL function definition /// Structure holding VimL function definition
typedef struct fst { typedef struct fst {
uint8_t min_argc; ///< Minimal number of arguments. uint8_t min_argc; ///< Minimal number of arguments.
uint8_t max_argc; ///< Maximal number of arguments. uint8_t max_argc; ///< Maximal number of arguments.
VimLFunc func; ///< Function implementation. VimLFunc func; ///< Function implementation.
void *data; ///< Userdata for function implementation.
} VimLFuncDef; } VimLFuncDef;
KHASH_MAP_INIT_STR(functions, VimLFuncDef) KHASH_MAP_INIT_STR(functions, VimLFuncDef)
@ -6981,7 +6982,7 @@ call_func (
} }
} else { } else {
// Find the function name in the table, call its implementation. // Find the function name in the table, call its implementation.
VimLFuncDef *const fdef = find_internal_func((char *) fname); VimLFuncDef *const fdef = find_internal_func((char *)fname);
if (fdef != NULL) { if (fdef != NULL) {
if (argcount < fdef->min_argc) { if (argcount < fdef->min_argc) {
error = ERROR_TOOFEW; error = ERROR_TOOFEW;
@ -6989,7 +6990,7 @@ call_func (
error = ERROR_TOOMANY; error = ERROR_TOOMANY;
} else { } else {
argvars[argcount].v_type = VAR_UNKNOWN; argvars[argcount].v_type = VAR_UNKNOWN;
fdef->func(argvars, rettv); fdef->func(argvars, rettv, fdef->data);
error = ERROR_NONE; error = ERROR_NONE;
} }
} }
@ -7100,13 +7101,10 @@ static inline int get_float_arg(typval_T *argvars, float_T *f)
// Some versions of glibc on i386 have an optimization that makes it harder to // Some versions of glibc on i386 have an optimization that makes it harder to
// call math functions indirectly from inside an inlined function, causing // call math functions indirectly from inside an inlined function, causing
// compile-time errors. Avoid `inline` in that case. #3072 // compile-time errors. Avoid `inline` in that case. #3072
#ifndef ARCH_32 static void float_op_wrapper(typval_T *argvars, typval_T *rettv, void *data)
inline
#endif
static void float_op_wrapper(typval_T *argvars, typval_T *rettv,
float_T (*function)(float_T))
{ {
float_T f; float_T f;
float_T (*function)(float_T) = data;
rettv->v_type = VAR_FLOAT; rettv->v_type = VAR_FLOAT;
if (get_float_arg(argvars, &f) == OK) { if (get_float_arg(argvars, &f) == OK) {
@ -7116,6 +7114,34 @@ static void float_op_wrapper(typval_T *argvars, typval_T *rettv,
} }
} }
static void api_wrapper(typval_T *argvars, typval_T *rettv, void *data)
{
ApiDispatchWrapper fn = data;
Array args = ARRAY_DICT_INIT;
for (typval_T *tv = argvars; tv->v_type != VAR_UNKNOWN; tv++) {
ADD(args, vim_to_object(tv));
}
Error err = ERROR_INIT;
Object result = fn(-1, -1, args, &err);
if (err.set) {
vim_report_error(cstr_as_string(err.msg));
goto end;
}
if (!object_to_vim(result, rettv, &err)) {
EMSG2(_("Error converting the call result: %s"), err.msg);
}
end:
// All arguments were freed already, but we still need to free the array
xfree(args.items);
api_free_object(result);
}
/* /*
* "abs(expr)" function * "abs(expr)" function
*/ */
@ -7515,14 +7541,6 @@ static void f_asin(typval_T *argvars, typval_T *rettv)
float_op_wrapper(argvars, rettv, &asin); float_op_wrapper(argvars, rettv, &asin);
} }
/*
* "atan()" function
*/
static void f_atan(typval_T *argvars, typval_T *rettv)
{
float_op_wrapper(argvars, rettv, &atan);
}
/* /*
* "atan2()" function * "atan2()" function
*/ */
@ -20115,7 +20133,7 @@ void free_all_functions(void)
int translated_function_exists(char_u *name) int translated_function_exists(char_u *name)
{ {
if (builtin_function(name, -1)) { if (builtin_function(name, -1)) {
return find_internal_func((char *) name) != NULL; return find_internal_func((char *)name) != NULL;
} }
return find_func(name) != NULL; return find_func(name) != NULL;
} }

View File

@ -33,7 +33,7 @@ return {
assert_notequal={args={2, 3}}, assert_notequal={args={2, 3}},
assert_notmatch={args={2, 3}}, assert_notmatch={args={2, 3}},
assert_true={args={1, 2}}, assert_true={args={1, 2}},
atan={args=1}, atan={args=1, func="float_op_wrapper", data="atan"},
atan2={args=2}, atan2={args=2},
browse={args=4}, browse={args=4},
browsedir={args=2}, browsedir={args=2},