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)
-- we need at least 2 arguments since the last one is the output file
assert(#arg >= 1)
-- we need at least 3 arguments since the last two are output files
assert(#arg >= 2)
functions = {}
-- names of all headers relative to the source root (for inclusion in the
-- generated file)
headers = {}
-- output file(dispatch function + metadata serialized with msgpack)
outputf = arg[#arg]
-- output c file(dispatch function + metadata serialized with msgpack)
outputf = arg[#arg-1]
-- output mpack file (metadata)
mpack_outputf = arg[#arg]
-- 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 parts = {}
for part in string.gmatch(full_path, '[^/]+') do
@ -165,7 +167,7 @@ for i = 1, #functions do
local fn = functions[i]
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 Object ret = NIL;')
-- Declare/initialize variables that will hold converted arguments
@ -311,3 +313,7 @@ MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name,
]])
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 autodir = arg[2]
local metadata_file = arg[3]
if nvimsrcdir == '--help' then
print([[
@ -18,9 +21,20 @@ local funcsfname = autodir .. '/funcs.generated.h'
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
if type(args) == 'number' then
args = {args, args}
@ -28,7 +42,8 @@ for name, def in pairs(funcs.funcs) do
args[2] = 'MAX_FUNC_ARGS'
end
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')
end
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)
file(GLOB API_HEADERS api/*.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(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include)
set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch.c)
@ -192,8 +193,8 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES}
${EASTASIANWIDTH_FILE}
)
add_custom_command(OUTPUT ${GENERATED_API_DISPATCH}
COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${API_HEADERS} ${GENERATED_API_DISPATCH}
add_custom_command(OUTPUT ${GENERATED_API_DISPATCH} ${API_METADATA}
COMMAND ${LUA_PRG} ${DISPATCH_GENERATOR} ${API_HEADERS} ${GENERATED_API_DISPATCH} ${API_METADATA}
DEPENDS
${API_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}
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>
${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}

View File

@ -3,13 +3,15 @@
#include "nvim/api/private/defs.h"
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
/// functions of this type.
typedef struct {
Object (*fn)(uint64_t channel_id,
typedef Object (*ApiDispatchWrapper)(uint64_t channel_id,
uint64_t request_id,
Array args,
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
// put in a request queue for handling when nvim waits for input.
} MsgpackRpcRequestHandler;

View File

@ -445,13 +445,14 @@ typedef struct {
} timer_T;
/// 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
typedef struct fst {
uint8_t min_argc; ///< Minimal number of arguments.
uint8_t max_argc; ///< Maximal number of arguments.
VimLFunc func; ///< Function implementation.
void *data; ///< Userdata for function implementation.
} VimLFuncDef;
KHASH_MAP_INIT_STR(functions, VimLFuncDef)
@ -6989,7 +6990,7 @@ call_func (
error = ERROR_TOOMANY;
} else {
argvars[argcount].v_type = VAR_UNKNOWN;
fdef->func(argvars, rettv);
fdef->func(argvars, rettv, fdef->data);
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
// call math functions indirectly from inside an inlined function, causing
// compile-time errors. Avoid `inline` in that case. #3072
#ifndef ARCH_32
inline
#endif
static void float_op_wrapper(typval_T *argvars, typval_T *rettv,
float_T (*function)(float_T))
static void float_op_wrapper(typval_T *argvars, typval_T *rettv, void *data)
{
float_T f;
float_T (*function)(float_T) = data;
rettv->v_type = VAR_FLOAT;
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
*/
@ -7515,14 +7541,6 @@ static void f_asin(typval_T *argvars, typval_T *rettv)
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
*/

View File

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