From 8fb7273ac0b981eeb9afdc82675b908c1600d34f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 1 Jan 2016 12:17:22 +0300 Subject: eval: Move VimL functions list to a lua file Removes all kinds of problems with sorting, provides a ready-to-use function list representation for genvimvim.lua, does not require specifying function name twice (VimL function name (string) + f_ function name). --- scripts/geneval.lua | 42 ++++++++++++++++++++++++++++++++++++++++++ scripts/genvimvim.lua | 22 ++++++---------------- 2 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 scripts/geneval.lua (limited to 'scripts') diff --git a/scripts/geneval.lua b/scripts/geneval.lua new file mode 100644 index 0000000000..1f8a14d27b --- /dev/null +++ b/scripts/geneval.lua @@ -0,0 +1,42 @@ +local nvimsrcdir = arg[1] +local autodir = arg[2] + +if nvimsrcdir == '--help' then + print([[ +Usage: + lua geneval.lua src/nvim build/src/nvim/auto + +Will generate build/src/nvim/auto/funcs.generated.h with definition of functions +static const array. +]]) + os.exit(0) +end + +package.path = nvimsrcdir .. '/?.lua;' .. package.path + +local funcsfname = autodir .. '/funcs.generated.h' +local funcsfile = io.open(funcsfname, 'w') + +local funcs = require('eval') + +local sorted_funcs = {} +for name, def in pairs(funcs.funcs) do + def.name = name + def.args = def.args or 0 + if type(def.args) == 'number' then + def.args = {def.args, def.args} + elseif #def.args == 1 then + def.args[2] = 'MAX_FUNC_ARGS' + end + def.func = def.func or ('f_' .. def.name) + sorted_funcs[#sorted_funcs + 1] = def +end +table.sort(sorted_funcs, function(a, b) return a.name < b.name end) + +funcsfile:write('static const VimLFuncDef functions[] = {\n') +for _, def in ipairs(sorted_funcs) do + funcsfile:write((' { "%s", %s, %s, &%s },\n'):format( + def.name, def.args[1], def.args[2], def.func + )) +end +funcsfile:write('};\n') diff --git a/scripts/genvimvim.lua b/scripts/genvimvim.lua index 667af7be6c..729749a52f 100644 --- a/scripts/genvimvim.lua +++ b/scripts/genvimvim.lua @@ -23,6 +23,7 @@ end local options = require('options') local auevents = require('auevents') local ex_cmds = require('ex_cmds') +local eval = require('eval') local cmd_kw = function(prev_cmd, cmd) if not prev_cmd then @@ -113,23 +114,12 @@ local vimfun_start = 'syn keyword vimFuncName contained ' w('\n\n' .. vimfun_start) eval_fd = io.open(nvimsrcdir .. '/eval.c', 'r') local started = 0 -for line in eval_fd:lines() do - if line == '} functions[] =' then - started = 1 - elseif started == 1 then - assert (line == '{') - started = 2 - elseif started == 2 then - if line == '};' then - break - end - local func_name = line:match('^ { "([%w_]+)",') - if func_name then - if lld.line_length > 850 then - w('\n' .. vimfun_start) - end - w(' ' .. func_name) +for name, def in pairs(eval.funcs) do + if name then + if lld.line_length > 850 then + w('\n' .. vimfun_start) end + w(' ' .. name) end end eval_fd:close() -- cgit From 5e59916e84933b0b05fda8ed76cf5f24fa3f48b6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 1 Jan 2016 14:37:20 +0300 Subject: eval: Use generated hash to look up function list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problems: - Disables cross-compiling (alternative: keeps two hash implementations which need to be synchronized with each other). - Puts code-specific name literals into CMakeLists.txt. - Workaround for lua’s absence of bidirectional pipe communication is rather ugly. --- scripts/geneval.lua | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'scripts') diff --git a/scripts/geneval.lua b/scripts/geneval.lua index 1f8a14d27b..c9533d7908 100644 --- a/scripts/geneval.lua +++ b/scripts/geneval.lua @@ -15,28 +15,20 @@ end package.path = nvimsrcdir .. '/?.lua;' .. package.path local funcsfname = autodir .. '/funcs.generated.h' -local funcsfile = io.open(funcsfname, 'w') + +local funcspipe = io.open(funcsfname .. '.hsh', 'w') local funcs = require('eval') -local sorted_funcs = {} for name, def in pairs(funcs.funcs) do - def.name = name - def.args = def.args or 0 - if type(def.args) == 'number' then - def.args = {def.args, def.args} - elseif #def.args == 1 then - def.args[2] = 'MAX_FUNC_ARGS' + args = def.args or 0 + if type(args) == 'number' then + args = {args, args} + elseif #args == 1 then + args[2] = 'MAX_FUNC_ARGS' end - def.func = def.func or ('f_' .. def.name) - sorted_funcs[#sorted_funcs + 1] = def -end -table.sort(sorted_funcs, function(a, b) return a.name < b.name end) - -funcsfile:write('static const VimLFuncDef functions[] = {\n') -for _, def in ipairs(sorted_funcs) do - funcsfile:write((' { "%s", %s, %s, &%s },\n'):format( - def.name, def.args[1], def.args[2], def.func - )) + func = def.func or ('f_' .. name) + local val = ('{ %s, %s, &%s }'):format(args[1], args[2], func) + funcspipe:write(name .. '\n' .. val .. '\n') end -funcsfile:write('};\n') +funcspipe:close() -- cgit From de3a515123fa417ecce61efebe4bc117b010c657 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Thu, 16 Jun 2016 13:35:04 +0200 Subject: api: rename "msgpack_rpc/defs.h" to "api/private/dispatch.h" and use the header generator. --- scripts/gendispatch.lua | 313 ++++++++++++++++++++++++++++++++++++++++++++++++ scripts/msgpack-gen.lua | 313 ------------------------------------------------ 2 files changed, 313 insertions(+), 313 deletions(-) create mode 100644 scripts/gendispatch.lua delete mode 100644 scripts/msgpack-gen.lua (limited to 'scripts') diff --git a/scripts/gendispatch.lua b/scripts/gendispatch.lua new file mode 100644 index 0000000000..54bfdab45b --- /dev/null +++ b/scripts/gendispatch.lua @@ -0,0 +1,313 @@ +lpeg = require('lpeg') +mpack = require('mpack') + +-- lpeg grammar for building api metadata from a set of header files. It +-- ignores comments and preprocessor commands and parses a very small subset +-- of C prototypes with a limited set of types +P, R, S = lpeg.P, lpeg.R, lpeg.S +C, Ct, Cc, Cg = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg + +any = P(1) -- (consume one character) +letter = R('az', 'AZ') + S('_$') +alpha = letter + R('09') +nl = P('\r\n') + P('\n') +not_nl = any - nl +ws = S(' \t') + nl +fill = ws ^ 0 +c_comment = P('//') * (not_nl ^ 0) +c_preproc = P('#') * (not_nl ^ 0) +typed_container = + (P('ArrayOf(') + P('DictionaryOf(')) * ((any - P(')')) ^ 1) * P(')') +c_id = ( + typed_container + + (letter * (alpha ^ 0)) +) +c_void = P('void') +c_param_type = ( + ((P('Error') * fill * P('*') * fill) * Cc('error')) + + (C(c_id) * (ws ^ 1)) + ) +c_type = (C(c_void) * (ws ^ 1)) + c_param_type +c_param = Ct(c_param_type * C(c_id)) +c_param_list = c_param * (fill * (P(',') * fill * c_param) ^ 0) +c_params = Ct(c_void + c_param_list) +c_proto = Ct( + Cg(c_type, 'return_type') * Cg(c_id, 'name') * + fill * P('(') * fill * Cg(c_params, 'parameters') * fill * P(')') * + Cg(Cc(false), 'async') * + (fill * Cg((P('FUNC_API_ASYNC') * Cc(true)), 'async') ^ -1) * + (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1) * + fill * P(';') + ) +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) +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] + +-- read each input file, parse and append to the api metadata +for i = 1, #arg - 1 do + local full_path = arg[i] + local parts = {} + for part in string.gmatch(full_path, '[^/]+') do + parts[#parts + 1] = part + end + headers[#headers + 1] = parts[#parts - 1]..'/'..parts[#parts] + + local input = io.open(full_path, 'rb') + local tmp = grammar:match(input:read('*all')) + for i = 1, #tmp do + local fn = tmp[i] + if not fn.noexport then + functions[#functions + 1] = tmp[i] + if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then + -- this function should receive the channel id + fn.receives_channel_id = true + -- remove the parameter since it won't be passed by the api client + table.remove(fn.parameters, 1) + end + if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'error' then + -- function can fail if the last parameter type is 'Error' + fn.can_fail = true + -- remove the error parameter, msgpack has it's own special field + -- for specifying errors + fn.parameters[#fn.parameters] = nil + end + end + end + input:close() +end + + +-- start building the output +output = io.open(outputf, 'wb') + +output:write([[ +#include +#include +#include +#include +#include + +#include "nvim/map.h" +#include "nvim/log.h" +#include "nvim/vim.h" +#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/api/private/dispatch.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/private/defs.h" +]]) + +for i = 1, #headers do + if headers[i]:sub(-12) ~= '.generated.h' then + output:write('\n#include "nvim/'..headers[i]..'"') + end +end + +output:write([[ + + +static const uint8_t msgpack_metadata[] = { + +]]) +-- serialize the API metadata using msgpack and embed into the resulting +-- binary for easy querying by clients +packed = mpack.pack(functions) +for i = 1, #packed do + output:write(string.byte(packed, i)..', ') + if i % 10 == 0 then + output:write('\n ') + end +end +output:write([[ +}; + +void msgpack_rpc_init_function_metadata(Dictionary *metadata) +{ + msgpack_unpacked unpacked; + msgpack_unpacked_init(&unpacked); + if (msgpack_unpack_next(&unpacked, + (const char *)msgpack_metadata, + sizeof(msgpack_metadata), + NULL) != MSGPACK_UNPACK_SUCCESS) { + abort(); + } + Object functions; + msgpack_rpc_to_object(&unpacked.data, &functions); + msgpack_unpacked_destroy(&unpacked); + PUT(*metadata, "functions", functions); +} + +]]) + +local function real_type(type) + local rv = type + if typed_container:match(rv) then + if rv:match('Array') then + rv = 'Array' + else + rv = 'Dictionary' + end + end + return rv +end + +-- start the handler functions. Visit each function metadata to build the +-- handler function with code generated for validating arguments and calling to +-- the real API. +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('\n{') + output:write('\n Object ret = NIL;') + -- Declare/initialize variables that will hold converted arguments + for j = 1, #fn.parameters do + local param = fn.parameters[j] + local converted = 'arg_'..j + output:write('\n '..param[1]..' '..converted..' api_init_'..string.lower(real_type(param[1]))..';') + end + output:write('\n') + output:write('\n if (args.size != '..#fn.parameters..') {') + output:write('\n snprintf(error->msg, sizeof(error->msg), "Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);') + output:write('\n error->set = true;') + output:write('\n goto cleanup;') + output:write('\n }\n') + + -- Validation/conversion for each argument + for j = 1, #fn.parameters do + local converted, convert_arg, param, arg + param = fn.parameters[j] + converted = 'arg_'..j + local rt = real_type(param[1]) + if rt ~= 'Object' then + output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {') + output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..rt:lower()..';') + if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') or rt:match('^Boolean$') then + -- accept positive integers for Buffers, Windows and Tabpages + output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer > 0) {') + output:write('\n '..converted..' = (unsigned)args.items['..(j - 1)..'].data.integer;') + end + output:write('\n } else {') + output:write('\n snprintf(error->msg, sizeof(error->msg), "Wrong type for argument '..j..', expecting '..param[1]..'");') + output:write('\n error->set = true;') + output:write('\n goto cleanup;') + output:write('\n }\n') + else + output:write('\n '..converted..' = args.items['..(j - 1)..'];\n') + end + + args[#args + 1] = converted + end + + -- function call + local call_args = table.concat(args, ', ') + output:write('\n ') + if fn.return_type ~= 'void' then + -- has a return value, prefix the call with a declaration + output:write(fn.return_type..' rv = ') + end + + -- write the function name and the opening parenthesis + output:write(fn.name..'(') + + if fn.receives_channel_id then + -- if the function receives the channel id, pass it as first argument + if #args > 0 or fn.can_fail then + output:write('channel_id, '..call_args) + else + output:write('channel_id') + end + else + output:write(call_args) + end + + if fn.can_fail then + -- if the function can fail, also pass a pointer to the local error object + if #args > 0 then + output:write(', error);\n') + else + output:write('error);\n') + end + -- and check for the error + output:write('\n if (error->set) {') + output:write('\n goto cleanup;') + output:write('\n }\n') + else + output:write(');\n') + end + + if fn.return_type ~= 'void' then + output:write('\n ret = '..string.upper(real_type(fn.return_type))..'_OBJ(rv);') + end + -- Now generate the cleanup label for freeing memory allocated for the + -- arguments + output:write('\n\ncleanup:'); + + for j = 1, #fn.parameters do + local param = fn.parameters[j] + output:write('\n api_free_'..string.lower(real_type(param[1]))..'(arg_'..j..');') + end + output:write('\n return ret;\n}\n\n'); +end + +-- Generate a function that initializes method names with handler functions +output:write([[ +static Map(String, MsgpackRpcRequestHandler) *methods = NULL; + +void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandler handler) +{ + map_put(String, MsgpackRpcRequestHandler)(methods, method, handler); +} + +void msgpack_rpc_init_method_table(void) +{ + methods = map_new(String, MsgpackRpcRequestHandler)(); + +]]) + +-- Keep track of the maximum method name length in order to avoid walking +-- strings longer than that when searching for a method handler +local max_fname_len = 0 +for i = 1, #functions do + local fn = functions[i] + output:write(' msgpack_rpc_add_method_handler('.. + '(String) {.data = "'..fn.name..'", '.. + '.size = sizeof("'..fn.name..'") - 1}, '.. + '(MsgpackRpcRequestHandler) {.fn = handle_'.. fn.name.. + ', .async = '..tostring(fn.async)..'});\n') + + if #fn.name > max_fname_len then + max_fname_len = #fn.name + end +end + +output:write('\n}\n\n') + +output:write([[ +MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, + size_t name_len) +{ + String m = { + .data=(char *)name, + .size=MIN(name_len, ]]..max_fname_len..[[) + }; + MsgpackRpcRequestHandler rv = + map_get(String, MsgpackRpcRequestHandler)(methods, m); + + if (!rv.fn) { + rv.fn = msgpack_rpc_handle_missing_method; + } + + return rv; +} +]]) + +output:close() diff --git a/scripts/msgpack-gen.lua b/scripts/msgpack-gen.lua deleted file mode 100644 index 2da3c174f9..0000000000 --- a/scripts/msgpack-gen.lua +++ /dev/null @@ -1,313 +0,0 @@ -lpeg = require('lpeg') -mpack = require('mpack') - --- lpeg grammar for building api metadata from a set of header files. It --- ignores comments and preprocessor commands and parses a very small subset --- of C prototypes with a limited set of types -P, R, S = lpeg.P, lpeg.R, lpeg.S -C, Ct, Cc, Cg = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg - -any = P(1) -- (consume one character) -letter = R('az', 'AZ') + S('_$') -alpha = letter + R('09') -nl = P('\r\n') + P('\n') -not_nl = any - nl -ws = S(' \t') + nl -fill = ws ^ 0 -c_comment = P('//') * (not_nl ^ 0) -c_preproc = P('#') * (not_nl ^ 0) -typed_container = - (P('ArrayOf(') + P('DictionaryOf(')) * ((any - P(')')) ^ 1) * P(')') -c_id = ( - typed_container + - (letter * (alpha ^ 0)) -) -c_void = P('void') -c_param_type = ( - ((P('Error') * fill * P('*') * fill) * Cc('error')) + - (C(c_id) * (ws ^ 1)) - ) -c_type = (C(c_void) * (ws ^ 1)) + c_param_type -c_param = Ct(c_param_type * C(c_id)) -c_param_list = c_param * (fill * (P(',') * fill * c_param) ^ 0) -c_params = Ct(c_void + c_param_list) -c_proto = Ct( - Cg(c_type, 'return_type') * Cg(c_id, 'name') * - fill * P('(') * fill * Cg(c_params, 'parameters') * fill * P(')') * - Cg(Cc(false), 'async') * - (fill * Cg((P('FUNC_API_ASYNC') * Cc(true)), 'async') ^ -1) * - (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1) * - fill * P(';') - ) -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) -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] - --- read each input file, parse and append to the api metadata -for i = 1, #arg - 1 do - local full_path = arg[i] - local parts = {} - for part in string.gmatch(full_path, '[^/]+') do - parts[#parts + 1] = part - end - headers[#headers + 1] = parts[#parts - 1]..'/'..parts[#parts] - - local input = io.open(full_path, 'rb') - local tmp = grammar:match(input:read('*all')) - for i = 1, #tmp do - local fn = tmp[i] - if not fn.noexport then - functions[#functions + 1] = tmp[i] - if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then - -- this function should receive the channel id - fn.receives_channel_id = true - -- remove the parameter since it won't be passed by the api client - table.remove(fn.parameters, 1) - end - if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'error' then - -- function can fail if the last parameter type is 'Error' - fn.can_fail = true - -- remove the error parameter, msgpack has it's own special field - -- for specifying errors - fn.parameters[#fn.parameters] = nil - end - end - end - input:close() -end - - --- start building the output -output = io.open(outputf, 'wb') - -output:write([[ -#include -#include -#include -#include -#include - -#include "nvim/map.h" -#include "nvim/log.h" -#include "nvim/vim.h" -#include "nvim/msgpack_rpc/helpers.h" -#include "nvim/msgpack_rpc/defs.h" -#include "nvim/api/private/helpers.h" -#include "nvim/api/private/defs.h" -]]) - -for i = 1, #headers do - if headers[i]:sub(-12) ~= '.generated.h' then - output:write('\n#include "nvim/'..headers[i]..'"') - end -end - -output:write([[ - - -static const uint8_t msgpack_metadata[] = { - -]]) --- serialize the API metadata using msgpack and embed into the resulting --- binary for easy querying by clients -packed = mpack.pack(functions) -for i = 1, #packed do - output:write(string.byte(packed, i)..', ') - if i % 10 == 0 then - output:write('\n ') - end -end -output:write([[ -}; - -void msgpack_rpc_init_function_metadata(Dictionary *metadata) -{ - msgpack_unpacked unpacked; - msgpack_unpacked_init(&unpacked); - if (msgpack_unpack_next(&unpacked, - (const char *)msgpack_metadata, - sizeof(msgpack_metadata), - NULL) != MSGPACK_UNPACK_SUCCESS) { - abort(); - } - Object functions; - msgpack_rpc_to_object(&unpacked.data, &functions); - msgpack_unpacked_destroy(&unpacked); - PUT(*metadata, "functions", functions); -} - -]]) - -local function real_type(type) - local rv = type - if typed_container:match(rv) then - if rv:match('Array') then - rv = 'Array' - else - rv = 'Dictionary' - end - end - return rv -end - --- start the handler functions. Visit each function metadata to build the --- handler function with code generated for validating arguments and calling to --- the real API. -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('\n{') - output:write('\n Object ret = NIL;') - -- Declare/initialize variables that will hold converted arguments - for j = 1, #fn.parameters do - local param = fn.parameters[j] - local converted = 'arg_'..j - output:write('\n '..param[1]..' '..converted..' api_init_'..string.lower(real_type(param[1]))..';') - end - output:write('\n') - output:write('\n if (args.size != '..#fn.parameters..') {') - output:write('\n snprintf(error->msg, sizeof(error->msg), "Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);') - output:write('\n error->set = true;') - output:write('\n goto cleanup;') - output:write('\n }\n') - - -- Validation/conversion for each argument - for j = 1, #fn.parameters do - local converted, convert_arg, param, arg - param = fn.parameters[j] - converted = 'arg_'..j - local rt = real_type(param[1]) - if rt ~= 'Object' then - output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {') - output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..rt:lower()..';') - if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') or rt:match('^Boolean$') then - -- accept positive integers for Buffers, Windows and Tabpages - output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer > 0) {') - output:write('\n '..converted..' = (unsigned)args.items['..(j - 1)..'].data.integer;') - end - output:write('\n } else {') - output:write('\n snprintf(error->msg, sizeof(error->msg), "Wrong type for argument '..j..', expecting '..param[1]..'");') - output:write('\n error->set = true;') - output:write('\n goto cleanup;') - output:write('\n }\n') - else - output:write('\n '..converted..' = args.items['..(j - 1)..'];\n') - end - - args[#args + 1] = converted - end - - -- function call - local call_args = table.concat(args, ', ') - output:write('\n ') - if fn.return_type ~= 'void' then - -- has a return value, prefix the call with a declaration - output:write(fn.return_type..' rv = ') - end - - -- write the function name and the opening parenthesis - output:write(fn.name..'(') - - if fn.receives_channel_id then - -- if the function receives the channel id, pass it as first argument - if #args > 0 or fn.can_fail then - output:write('channel_id, '..call_args) - else - output:write('channel_id') - end - else - output:write(call_args) - end - - if fn.can_fail then - -- if the function can fail, also pass a pointer to the local error object - if #args > 0 then - output:write(', error);\n') - else - output:write('error);\n') - end - -- and check for the error - output:write('\n if (error->set) {') - output:write('\n goto cleanup;') - output:write('\n }\n') - else - output:write(');\n') - end - - if fn.return_type ~= 'void' then - output:write('\n ret = '..string.upper(real_type(fn.return_type))..'_OBJ(rv);') - end - -- Now generate the cleanup label for freeing memory allocated for the - -- arguments - output:write('\n\ncleanup:'); - - for j = 1, #fn.parameters do - local param = fn.parameters[j] - output:write('\n api_free_'..string.lower(real_type(param[1]))..'(arg_'..j..');') - end - output:write('\n return ret;\n}\n\n'); -end - --- Generate a function that initializes method names with handler functions -output:write([[ -static Map(String, MsgpackRpcRequestHandler) *methods = NULL; - -void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandler handler) -{ - map_put(String, MsgpackRpcRequestHandler)(methods, method, handler); -} - -void msgpack_rpc_init_method_table(void) -{ - methods = map_new(String, MsgpackRpcRequestHandler)(); - -]]) - --- Keep track of the maximum method name length in order to avoid walking --- strings longer than that when searching for a method handler -local max_fname_len = 0 -for i = 1, #functions do - local fn = functions[i] - output:write(' msgpack_rpc_add_method_handler('.. - '(String) {.data = "'..fn.name..'", '.. - '.size = sizeof("'..fn.name..'") - 1}, '.. - '(MsgpackRpcRequestHandler) {.fn = handle_'.. fn.name.. - ', .async = '..tostring(fn.async)..'});\n') - - if #fn.name > max_fname_len then - max_fname_len = #fn.name - end -end - -output:write('\n}\n\n') - -output:write([[ -MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, - size_t name_len) -{ - String m = { - .data=(char *)name, - .size=MIN(name_len, ]]..max_fname_len..[[) - }; - MsgpackRpcRequestHandler rv = - map_get(String, MsgpackRpcRequestHandler)(methods, m); - - if (!rv.fn) { - rv.fn = msgpack_rpc_handle_missing_method; - } - - return rv; -} -]]) - -output:close() -- cgit From 3bd3b3b76859b3eef80fa4969147efa881b60f40 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 18 Jun 2016 12:06:50 +0200 Subject: api: auto generate api function wrappers for viml --- scripts/gendispatch.lua | 18 ++++++++++++------ scripts/geneval.lua | 21 ++++++++++++++++++--- 2 files changed, 30 insertions(+), 9 deletions(-) (limited to 'scripts') diff --git a/scripts/gendispatch.lua b/scripts/gendispatch.lua index 54bfdab45b..ce1d8be222 100644 --- a/scripts/gendispatch.lua +++ b/scripts/gendispatch.lua @@ -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() diff --git a/scripts/geneval.lua b/scripts/geneval.lua index c9533d7908..396e7b81db 100644 --- a/scripts/geneval.lua +++ b/scripts/geneval.lua @@ -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 + +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.funcs) do +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() -- cgit From a2d25b7bf81728d913e7b317d33997c8b8268af2 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 19 Jun 2016 21:06:03 +0200 Subject: api: unify buffer numbers and window ids with handles also allow handle==0 meaning curbuf/curwin/curtab --- scripts/gendispatch.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/gendispatch.lua b/scripts/gendispatch.lua index ce1d8be222..9178996a56 100644 --- a/scripts/gendispatch.lua +++ b/scripts/gendispatch.lua @@ -193,9 +193,9 @@ for i = 1, #functions do output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {') output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..rt:lower()..';') if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') or rt:match('^Boolean$') then - -- accept positive integers for Buffers, Windows and Tabpages - output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer > 0) {') - output:write('\n '..converted..' = (unsigned)args.items['..(j - 1)..'].data.integer;') + -- accept nonnegative integers for Booleans, Buffers, Windows and Tabpages + output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer >= 0) {') + output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') end output:write('\n } else {') output:write('\n snprintf(error->msg, sizeof(error->msg), "Wrong type for argument '..j..', expecting '..param[1]..'");') -- cgit From 7e2348f2b1b487c875bbcf6c6711a276f9063040 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sun, 19 Jun 2016 16:41:08 +0200 Subject: eval: use gperf to generate the hash of builtin functions make api functions highlighted as builtins in vim.vim --- scripts/geneval.lua | 29 ++++++++++++++++++++++------- scripts/genvimvim.lua | 9 +++++---- 2 files changed, 27 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/geneval.lua b/scripts/geneval.lua index 396e7b81db..75a8bdfab3 100644 --- a/scripts/geneval.lua +++ b/scripts/geneval.lua @@ -3,6 +3,7 @@ mpack = require('mpack') local nvimsrcdir = arg[1] local autodir = arg[2] local metadata_file = arg[3] +local funcs_file = arg[4] if nvimsrcdir == '--help' then print([[ @@ -19,20 +20,34 @@ package.path = nvimsrcdir .. '/?.lua;' .. package.path local funcsfname = autodir .. '/funcs.generated.h' -local funcspipe = io.open(funcsfname .. '.hsh', 'w') +local gperfpipe = io.open(funcsfname .. '.gperf', 'wb') local funcs = require('eval').funcs - 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, + data='&handle_'..fun.name, } end +local funcsdata = io.open(funcs_file, 'w') +funcsdata:write(mpack.pack(funcs)) +funcsdata:close() + +gperfpipe:write([[ +%language=ANSI-C +%global-table +%define initializer-suffix ,0,0,NULL,NULL +%define word-array-name functions +%define hash-function-name hash_internal_func_gperf +%define lookup-function-name find_internal_func_gperf +%omit-struct-type +%struct-type +VimLFuncDef; +%% +]]) for name, def in pairs(funcs) do args = def.args or 0 @@ -43,7 +58,7 @@ for name, def in pairs(funcs) do end func = def.func or ('f_' .. name) data = def.data or "NULL" - local val = ('{ %s, %s, &%s, %s }'):format(args[1], args[2], func, data) - funcspipe:write(name .. '\n' .. val .. '\n') + gperfpipe:write(('%s, %s, %s, &%s, (FunPtr)%s\n') + :format(name, args[1], args[2], func, data)) end -funcspipe:close() +gperfpipe:close() diff --git a/scripts/genvimvim.lua b/scripts/genvimvim.lua index 729749a52f..24c147b811 100644 --- a/scripts/genvimvim.lua +++ b/scripts/genvimvim.lua @@ -1,3 +1,5 @@ +mpack = require('mpack') + if arg[1] == '--help' then print('Usage: lua genvimvim.lua src/nvim runtime/syntax/vim/generated.vim') os.exit(0) @@ -5,6 +7,7 @@ end local nvimsrcdir = arg[1] local syntax_file = arg[2] +local funcs_file = arg[3] package.path = nvimsrcdir .. '/?.lua;' .. package.path @@ -23,7 +26,6 @@ end local options = require('options') local auevents = require('auevents') local ex_cmds = require('ex_cmds') -local eval = require('eval') local cmd_kw = function(prev_cmd, cmd) if not prev_cmd then @@ -112,9 +114,9 @@ end w('\n\nsyn case match') local vimfun_start = 'syn keyword vimFuncName contained ' w('\n\n' .. vimfun_start) -eval_fd = io.open(nvimsrcdir .. '/eval.c', 'r') +funcs = mpack.unpack(io.open(funcs_file):read("*all")) local started = 0 -for name, def in pairs(eval.funcs) do +for name, def in pairs(funcs) do if name then if lld.line_length > 850 then w('\n' .. vimfun_start) @@ -122,7 +124,6 @@ for name, def in pairs(eval.funcs) do w(' ' .. name) end end -eval_fd:close() w('\n') syn_fd:close() -- cgit From e536abc1e1f59d1ac012e1be576bf55175e95443 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 20 Jun 2016 14:40:57 +0200 Subject: api: Allow blacklist functions that shouldn't be accesible from eval Blacklist deprecated functions and functions depending on channel_id --- scripts/gendispatch.lua | 1 + scripts/geneval.lua | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/gendispatch.lua b/scripts/gendispatch.lua index 9178996a56..68d4d44e2d 100644 --- a/scripts/gendispatch.lua +++ b/scripts/gendispatch.lua @@ -37,6 +37,7 @@ c_proto = Ct( Cg(Cc(false), 'async') * (fill * Cg((P('FUNC_API_ASYNC') * Cc(true)), 'async') ^ -1) * (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1) * + (fill * Cg((P('FUNC_API_NOEVAL') * Cc(true)), 'noeval') ^ -1) * fill * P(';') ) grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1) diff --git a/scripts/geneval.lua b/scripts/geneval.lua index 75a8bdfab3..a6171f0993 100644 --- a/scripts/geneval.lua +++ b/scripts/geneval.lua @@ -25,11 +25,13 @@ local gperfpipe = io.open(funcsfname .. '.gperf', 'wb') local funcs = require('eval').funcs 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, - } + if not fun.noeval then + funcs['api_'..fun.name] = { + args=#fun.parameters, + func='api_wrapper', + data='&handle_'..fun.name, + } + end end local funcsdata = io.open(funcs_file, 'w') -- cgit From 1c22cab2fd283db49bfdbeaea490747574a36049 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 28 Jun 2016 21:45:19 +0200 Subject: api: consistently use nvim_ prefix and update documentation --- scripts/dispatch_deprecated.lua | 77 +++++++++++++++ scripts/gendispatch.lua | 212 ++++++++++++++++++++++++---------------- scripts/geneval.lua | 2 +- 3 files changed, 206 insertions(+), 85 deletions(-) create mode 100644 scripts/dispatch_deprecated.lua (limited to 'scripts') diff --git a/scripts/dispatch_deprecated.lua b/scripts/dispatch_deprecated.lua new file mode 100644 index 0000000000..f3b299e3fc --- /dev/null +++ b/scripts/dispatch_deprecated.lua @@ -0,0 +1,77 @@ +local deprecated_aliases = { + nvim_buf_line_count="buffer_line_count", + nvim_buf_get_lines="buffer_get_lines", + nvim_buf_set_lines="buffer_set_lines", + nvim_buf_get_var="buffer_get_var", + nvim_buf_set_var="buffer_set_var", + nvim_buf_del_var="buffer_del_var", + nvim_buf_get_option="buffer_get_option", + nvim_buf_set_option="buffer_set_option", + nvim_buf_get_number="buffer_get_number", + nvim_buf_get_name="buffer_get_name", + nvim_buf_set_name="buffer_set_name", + nvim_buf_is_valid="buffer_is_valid", + nvim_buf_get_mark="buffer_get_mark", + nvim_buf_add_highlight="buffer_add_highlight", + nvim_buf_clear_highlight="buffer_clear_highlight", + nvim_tabpage_get_windows="tabpage_get_windows", + nvim_tabpage_get_var="tabpage_get_var", + nvim_tabpage_set_var="tabpage_set_var", + nvim_tabpage_del_var="tabpage_del_var", + nvim_tabpage_get_window="tabpage_get_window", + nvim_tabpage_is_valid="tabpage_is_valid", + nvim_ui_detach="ui_detach", + nvim_ui_try_resize="ui_try_resize", + nvim_command="vim_command", + nvim_feedkeys="vim_feedkeys", + nvim_input="vim_input", + nvim_replace_termcodes="vim_replace_termcodes", + nvim_command_output="vim_command_output", + nvim_eval="vim_eval", + nvim_call_function="vim_call_function", + nvim_strwidth="vim_strwidth", + nvim_list_runtime_paths="vim_list_runtime_paths", + nvim_change_directory="vim_change_directory", + nvim_get_var="vim_get_var", + nvim_set_var="vim_set_var", + nvim_del_var="vim_del_var", + nvim_get_vvar="vim_get_vvar", + nvim_get_option="vim_get_option", + nvim_set_option="vim_set_option", + nvim_out_write="vim_out_write", + nvim_err_write="vim_err_write", + nvim_report_error="vim_report_error", + nvim_get_buffers="vim_get_buffers", + nvim_get_current_buffer="vim_get_current_buffer", + nvim_set_current_buffer="vim_set_current_buffer", + nvim_get_windows="vim_get_windows", + nvim_get_current_window="vim_get_current_window", + nvim_set_current_window="vim_set_current_window", + nvim_get_tabpages="vim_get_tabpages", + nvim_get_current_tabpage="vim_get_current_tabpage", + nvim_set_current_tabpage="vim_set_current_tabpage", + nvim_set_current_line="vim_set_current_line", + nvim_get_current_line="vim_get_current_line", + nvim_del_current_line="vim_del_current_line", + nvim_subscribe="vim_subscribe", + nvim_unsubscribe="vim_unsubscribe", + nvim_name_to_color="vim_name_to_color", + nvim_get_color_map="vim_get_color_map", + nvim_get_api_info="vim_get_api_info", + nvim_win_get_buffer="window_get_buffer", + nvim_win_get_cursor="window_get_cursor", + nvim_win_set_cursor="window_set_cursor", + nvim_win_get_height="window_get_height", + nvim_win_set_height="window_set_height", + nvim_win_get_width="window_get_width", + nvim_win_set_width="window_set_width", + nvim_win_get_var="window_get_var", + nvim_win_set_var="window_set_var", + nvim_win_del_var="window_del_var", + nvim_win_get_option="window_get_option", + nvim_win_set_option="window_set_option", + nvim_win_get_position="window_get_position", + nvim_win_get_tabpage="window_get_tabpage", + nvim_win_is_valid="window_is_valid" +} +return deprecated_aliases diff --git a/scripts/gendispatch.lua b/scripts/gendispatch.lua index 68d4d44e2d..2846aece5d 100644 --- a/scripts/gendispatch.lua +++ b/scripts/gendispatch.lua @@ -42,10 +42,13 @@ c_proto = Ct( ) grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1) --- we need at least 3 arguments since the last two are output files -assert(#arg >= 2) +-- we need at least 4 arguments since the last two are output files +assert(#arg >= 3) functions = {} +local scriptdir = arg[1] +package.path = scriptdir .. '/?.lua;' .. package.path + -- names of all headers relative to the source root (for inclusion in the -- generated file) headers = {} @@ -55,7 +58,7 @@ outputf = arg[#arg-1] mpack_outputf = arg[#arg] -- read each input file, parse and append to the api metadata -for i = 1, #arg - 2 do +for i = 2, #arg - 2 do local full_path = arg[i] local parts = {} for part in string.gmatch(full_path, '[^/]+') do @@ -87,6 +90,45 @@ for i = 1, #arg - 2 do input:close() end +local function shallowcopy(orig) + local copy = {} + for orig_key, orig_value in pairs(orig) do + copy[orig_key] = orig_value + end + return copy +end + +local function startswith(String,Start) + return string.sub(String,1,string.len(Start))==Start +end + +-- Export functions under older deprecated names. +-- These will be removed eventually. +local deprecated_aliases = require("dispatch_deprecated") +for i,f in ipairs(shallowcopy(functions)) do + local ismethod = false + if startswith(f.name, "nvim_buf_") then + ismethod = true + elseif startswith(f.name, "nvim_win_") then + ismethod = true + elseif startswith(f.name, "nvim_tabpage_") then + ismethod = true + elseif not startswith(f.name, "nvim_") then + f.noeval = true + f.deprecated_since = 1 + end + f.method = ismethod + local newname = deprecated_aliases[f.name] + if newname ~= nil then + local newf = shallowcopy(f) + newf.name = newname + newf.impl_name = f.name + newf.noeval = true + newf.deprecated_since = 1 + functions[#functions+1] = newf + end +end + -- start building the output output = io.open(outputf, 'wb') @@ -166,99 +208,101 @@ end -- the real API. for i = 1, #functions do local fn = functions[i] - local args = {} - - 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 - for j = 1, #fn.parameters do - local param = fn.parameters[j] - local converted = 'arg_'..j - output:write('\n '..param[1]..' '..converted..' api_init_'..string.lower(real_type(param[1]))..';') - end - output:write('\n') - output:write('\n if (args.size != '..#fn.parameters..') {') - output:write('\n snprintf(error->msg, sizeof(error->msg), "Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);') - output:write('\n error->set = true;') - output:write('\n goto cleanup;') - output:write('\n }\n') - - -- Validation/conversion for each argument - for j = 1, #fn.parameters do - local converted, convert_arg, param, arg - param = fn.parameters[j] - converted = 'arg_'..j - local rt = real_type(param[1]) - if rt ~= 'Object' then - output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {') - output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..rt:lower()..';') - if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') or rt:match('^Boolean$') then - -- accept nonnegative integers for Booleans, Buffers, Windows and Tabpages - output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer >= 0) {') - output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') - end - output:write('\n } else {') - output:write('\n snprintf(error->msg, sizeof(error->msg), "Wrong type for argument '..j..', expecting '..param[1]..'");') - output:write('\n error->set = true;') - output:write('\n goto cleanup;') - output:write('\n }\n') - else - output:write('\n '..converted..' = args.items['..(j - 1)..'];\n') + if fn.impl_name == nil then + local args = {} + + 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 + for j = 1, #fn.parameters do + local param = fn.parameters[j] + local converted = 'arg_'..j + output:write('\n '..param[1]..' '..converted..' api_init_'..string.lower(real_type(param[1]))..';') end + output:write('\n') + output:write('\n if (args.size != '..#fn.parameters..') {') + output:write('\n snprintf(error->msg, sizeof(error->msg), "Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);') + output:write('\n error->set = true;') + output:write('\n goto cleanup;') + output:write('\n }\n') - args[#args + 1] = converted - end + -- Validation/conversion for each argument + for j = 1, #fn.parameters do + local converted, convert_arg, param, arg + param = fn.parameters[j] + converted = 'arg_'..j + local rt = real_type(param[1]) + if rt ~= 'Object' then + output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {') + output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..rt:lower()..';') + if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') or rt:match('^Boolean$') then + -- accept nonnegative integers for Booleans, Buffers, Windows and Tabpages + output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer >= 0) {') + output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') + end + output:write('\n } else {') + output:write('\n snprintf(error->msg, sizeof(error->msg), "Wrong type for argument '..j..', expecting '..param[1]..'");') + output:write('\n error->set = true;') + output:write('\n goto cleanup;') + output:write('\n }\n') + else + output:write('\n '..converted..' = args.items['..(j - 1)..'];\n') + end - -- function call - local call_args = table.concat(args, ', ') - output:write('\n ') - if fn.return_type ~= 'void' then - -- has a return value, prefix the call with a declaration - output:write(fn.return_type..' rv = ') - end + args[#args + 1] = converted + end - -- write the function name and the opening parenthesis - output:write(fn.name..'(') + -- function call + local call_args = table.concat(args, ', ') + output:write('\n ') + if fn.return_type ~= 'void' then + -- has a return value, prefix the call with a declaration + output:write(fn.return_type..' rv = ') + end - if fn.receives_channel_id then - -- if the function receives the channel id, pass it as first argument - if #args > 0 or fn.can_fail then - output:write('channel_id, '..call_args) + -- write the function name and the opening parenthesis + output:write(fn.name..'(') + + if fn.receives_channel_id then + -- if the function receives the channel id, pass it as first argument + if #args > 0 or fn.can_fail then + output:write('channel_id, '..call_args) + else + output:write('channel_id') + end else - output:write('channel_id') + output:write(call_args) end - else - output:write(call_args) - end - if fn.can_fail then - -- if the function can fail, also pass a pointer to the local error object - if #args > 0 then - output:write(', error);\n') + if fn.can_fail then + -- if the function can fail, also pass a pointer to the local error object + if #args > 0 then + output:write(', error);\n') + else + output:write('error);\n') + end + -- and check for the error + output:write('\n if (error->set) {') + output:write('\n goto cleanup;') + output:write('\n }\n') else - output:write('error);\n') + output:write(');\n') end - -- and check for the error - output:write('\n if (error->set) {') - output:write('\n goto cleanup;') - output:write('\n }\n') - else - output:write(');\n') - end - if fn.return_type ~= 'void' then - output:write('\n ret = '..string.upper(real_type(fn.return_type))..'_OBJ(rv);') - end - -- Now generate the cleanup label for freeing memory allocated for the - -- arguments - output:write('\n\ncleanup:'); + if fn.return_type ~= 'void' then + output:write('\n ret = '..string.upper(real_type(fn.return_type))..'_OBJ(rv);') + end + -- Now generate the cleanup label for freeing memory allocated for the + -- arguments + output:write('\n\ncleanup:'); - for j = 1, #fn.parameters do - local param = fn.parameters[j] - output:write('\n api_free_'..string.lower(real_type(param[1]))..'(arg_'..j..');') + for j = 1, #fn.parameters do + local param = fn.parameters[j] + output:write('\n api_free_'..string.lower(real_type(param[1]))..'(arg_'..j..');') + end + output:write('\n return ret;\n}\n\n'); end - output:write('\n return ret;\n}\n\n'); end -- Generate a function that initializes method names with handler functions @@ -284,7 +328,7 @@ for i = 1, #functions do output:write(' msgpack_rpc_add_method_handler('.. '(String) {.data = "'..fn.name..'", '.. '.size = sizeof("'..fn.name..'") - 1}, '.. - '(MsgpackRpcRequestHandler) {.fn = handle_'.. fn.name.. + '(MsgpackRpcRequestHandler) {.fn = handle_'.. (fn.impl_name or fn.name).. ', .async = '..tostring(fn.async)..'});\n') if #fn.name > max_fname_len then diff --git a/scripts/geneval.lua b/scripts/geneval.lua index a6171f0993..b1ba76296c 100644 --- a/scripts/geneval.lua +++ b/scripts/geneval.lua @@ -26,7 +26,7 @@ local funcs = require('eval').funcs local metadata = mpack.unpack(io.open(arg[3], 'rb'):read("*all")) for i,fun in ipairs(metadata) do if not fun.noeval then - funcs['api_'..fun.name] = { + funcs[fun.name] = { args=#fun.parameters, func='api_wrapper', data='&handle_'..fun.name, -- cgit From acb7c826b3df50bd9825baf3b2ffaaa79c8b80df Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 16 Jul 2016 16:51:56 +0200 Subject: api: fix leak when a api function is incorrectly called with a list. This applies both to msgpack-rpc and eval. --- scripts/gendispatch.lua | 6 ------ 1 file changed, 6 deletions(-) (limited to 'scripts') diff --git a/scripts/gendispatch.lua b/scripts/gendispatch.lua index 2846aece5d..12d6261b5a 100644 --- a/scripts/gendispatch.lua +++ b/scripts/gendispatch.lua @@ -293,14 +293,8 @@ for i = 1, #functions do if fn.return_type ~= 'void' then output:write('\n ret = '..string.upper(real_type(fn.return_type))..'_OBJ(rv);') end - -- Now generate the cleanup label for freeing memory allocated for the - -- arguments output:write('\n\ncleanup:'); - for j = 1, #fn.parameters do - local param = fn.parameters[j] - output:write('\n api_free_'..string.lower(real_type(param[1]))..'(arg_'..j..');') - end output:write('\n return ret;\n}\n\n'); end end -- cgit