From e7bbd8256b8c701205389be431bbafd8743c72a9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 4 Mar 2016 21:55:28 +0300 Subject: eval: Add luaeval function No tests yet, no documentation update, no :lua* stuff, no vim module. converter.c should also work with typval_T, not Object. Known problem: luaeval("1", {}) results in PANIC: unprotected error in call to Lua API (attempt to index a nil value) Ref #3823 --- scripts/genmsgpack.lua | 469 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 469 insertions(+) create mode 100644 scripts/genmsgpack.lua (limited to 'scripts/genmsgpack.lua') diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua new file mode 100644 index 0000000000..dd3caab5e4 --- /dev/null +++ b/scripts/genmsgpack.lua @@ -0,0 +1,469 @@ +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('_$') +num = R('09') +alpha = letter + num +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_SINCE(') * C(num ^ 1)) * P(')'), 'since') ^ -1) * + (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) + +-- we need at least 4 arguments since the last two are output files +if arg[1] == '--help' then + print('Usage: genmsgpack.lua args') + print('Args: 1: source directory') + print(' 2: dispatch output file (dispatch_wrappers.generated.h)') + print(' 3: functions metadata output file (funcs_metadata.generated.h)') + print(' 4: API metadata output file (api_metadata.mpack)') + print(' 5: lua C bindings output file (msgpack_lua_c_bindings.generated.c)') + print(' rest: C files where API functions are defined') +end +assert(#arg >= 4) +functions = {} + +local nvimsrcdir = arg[1] +package.path = nvimsrcdir .. '/?.lua;' .. package.path + +-- names of all headers relative to the source root (for inclusion in the +-- generated file) +headers = {} + +-- output h file with generated dispatch functions +dispatch_outputf = arg[2] +-- output h file with packed metadata +funcs_metadata_outputf = arg[3] +-- output metadata mpack file, for use by other build scripts +mpack_outputf = arg[4] +lua_c_bindings_outputf = arg[5] + +-- set of function names, used to detect duplicates +function_names = {} + +-- read each input file, parse and append to the api metadata +for i = 6, #arg 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] + function_names[fn.name] = true + 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 + +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("api.dispatch_deprecated") +for i,f in ipairs(shallowcopy(functions)) do + local ismethod = false + if startswith(f.name, "nvim_") then + if f.since == nil then + print("Function "..f.name.." lacks since field.\n") + os.exit(1) + end + f.since = tonumber(f.since) + 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 + end + else + f.noeval = true + f.since = 0 + f.deprecated_since = 1 + end + f.method = ismethod + local newname = deprecated_aliases[f.name] + if newname ~= nil then + if function_names[newname] then + -- duplicate + print("Function "..f.name.." has deprecated alias\n" + ..newname.." which has a separate implementation.\n".. + "Please remove it from src/nvim/api/dispatch_deprecated.lua") + os.exit(1) + end + local newf = shallowcopy(f) + newf.name = newname + if newname == "ui_try_resize" then + -- The return type was incorrectly set to Object in 0.1.5. + -- Keep it that way for clients that rely on this. + newf.return_type = "Object" + end + newf.impl_name = f.name + newf.noeval = true + newf.since = 0 + newf.deprecated_since = 1 + functions[#functions+1] = newf + end +end + +-- don't expose internal attributes like "impl_name" in public metadata +exported_attributes = {'name', 'parameters', 'return_type', 'method', + 'since', 'deprecated_since'} +exported_functions = {} +for _,f in ipairs(functions) do + local f_exported = {} + for _,attr in ipairs(exported_attributes) do + f_exported[attr] = f[attr] + end + exported_functions[#exported_functions+1] = f_exported +end + + +funcs_metadata_output = io.open(funcs_metadata_outputf, 'wb') +funcs_metadata_output:write([[ +static const uint8_t funcs_metadata[] = { +]]) + +-- serialize the API metadata using msgpack and embed into the resulting +-- binary for easy querying by clients +packed_exported_functions = mpack.pack(exported_functions) +for i = 1, #packed_exported_functions do + funcs_metadata_output:write(string.byte(packed_exported_functions, i)..', ') + if i % 10 == 0 then + funcs_metadata_output:write('\n ') + end +end +funcs_metadata_output:write([[ +}; +]]) +funcs_metadata_output:close() + +-- start building the dispatch wrapper output +output = io.open(dispatch_outputf, 'wb') + +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] + if fn.impl_name == nil then + local args = {} + + output:write('Object handle_'..fn.name..'(uint64_t channel_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..';') + 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 + if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') then + -- Buffer, Window, and Tabpage have a specific type, but are stored in integer + output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..' && args.items['..(j - 1)..'].data.integer >= 0) {') + output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') + else + output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {') + output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..rt:lower()..';') + end + 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 + + 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 + output:write('\n\ncleanup:'); + + output:write('\n return ret;\n}\n\n'); + end +end + +-- Generate a function that initializes method names with handler functions +output:write([[ +void msgpack_rpc_init_method_table(void) +{ + methods = map_new(String, MsgpackRpcRequestHandler)(); + +]]) + +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.impl_name or fn.name).. + ', .async = '..tostring(fn.async)..'});\n') + +end + +output:write('\n}\n\n') +output:close() + +mpack_output = io.open(mpack_outputf, 'wb') +mpack_output:write(mpack.pack(functions)) +mpack_output:close() + +local function include_headers(output, headers) + for i = 1, #headers do + if headers[i]:sub(-12) ~= '.generated.h' then + output:write('\n#include "nvim/'..headers[i]..'"') + end + end +end + +local function write_shifted_output(output, str) + str = str:gsub('\n ', '\n') + str = str:gsub('^ ', '') + str = str:gsub(' +$', '') + output:write(str) +end + +-- start building lua output +output = io.open(lua_c_bindings_outputf, 'wb') + +output:write([[ +#include +#include +#include + +#include "nvim/func_attr.h" +#include "nvim/api/private/defs.h" +#include "nvim/viml/executor/converter.h" +]]) +include_headers(output, headers) +output:write('\n') + +lua_c_functions = {} + +local function process_function(fn) + lua_c_function_name = ('nlua_msgpack_%s'):format(fn.name) + write_shifted_output(output, string.format([[ + + static int %s(lua_State *lstate) + { + Error err = {.set = false}; + ]], lua_c_function_name)) + lua_c_functions[#lua_c_functions + 1] = { + binding=lua_c_function_name, + api=fn.name + } + cparams = '' + for j, param in ipairs(fn.parameters) do + cparam = string.format('arg%u', j) + if param[1]:match('^ArrayOf') then + param_type = 'Array' + else + param_type = param[1] + end + write_shifted_output(output, string.format([[ + %s %s = nlua_pop_%s(lstate, &err); + if (err.set) { + lua_pushstring(lstate, err.msg); + return lua_error(lstate); + } + ]], param[1], cparam, param_type)) + cparams = cparams .. cparam .. ', ' + end + if fn.receives_channel_id then + cparams = 'INTERNAL_CALL, ' .. cparams + end + if fn.can_fail then + cparams = cparams .. '&err' + else + cparams = cparams:gsub(', $', '') + end + local name = fn.impl_name or fn.name + if fn.return_type ~= 'void' then + if fn.return_type:match('^ArrayOf') then + return_type = 'Array' + else + return_type = fn.return_type + end + write_shifted_output(output, string.format([[ + %s ret = %s(%s); + if (err.set) { + lua_pushstring(lstate, err.msg); + return lua_error(lstate); + } + nlua_push_%s(lstate, ret); + return 1; + ]], fn.return_type, name, cparams, return_type)) + else + write_shifted_output(output, string.format([[ + %s(%s); + if (err.set) { + lua_pushstring(lstate, err.msg); + return lua_error(lstate); + } + return 0; + ]], name, cparams)) + end + write_shifted_output(output, [[ + } + ]]) +end + +for _, fn in ipairs(functions) do + if not fn.noeval then + process_function(fn) + end +end + +output:write(string.format([[ +void nlua_add_api_functions(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + lua_createtable(lstate, 0, %u); +]], #lua_c_functions)) +for _, func in ipairs(lua_c_functions) do + output:write(string.format([[ + lua_pushcfunction(lstate, &%s); + lua_setfield(lstate, -2, "%s"); + ]], func.binding, func.api)) +end +output:write([[ + lua_setfield(lstate, -2, "api"); +} +]]) + +output:close() -- cgit From ba2f615cd40d5d809d1a141c7b098e3bd22ff7bb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 16 Jul 2016 02:26:04 +0300 Subject: functests: Test for error conditions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During testing found the following bugs: 1. msgpack-gen.lua script is completely unprepared for Float values either in return type or in arguments. Specifically: 1. At the time of writing relevant code FLOAT_OBJ did not exist as well as FLOATING_OBJ, but it would be used by msgpack-gen.lua should return type be Float. I added FLOATING_OBJ macros later because did not know that msgpack-gen.lua uses these _OBJ macros, otherwise it would be FLOAT_OBJ. 2. msgpack-gen.lua should use .data.floating in place of .data.float. But it did not expect that .data subattribute may have name different from lowercased type name. 2. vim_replace_termcodes returned its argument as-is if it receives an empty string (as well as _vim_id*() functions did). But if something in returned argument lives in an allocated memory such action will cause double free: once when freeing arguments, then when freeing return value. It did not cause problems yet because msgpack bindings return empty string as {NULL, 0} and nothing was actually allocated. 3. New code in msgpack-gen.lua popped arguments in reversed order, making lua bindings’ signatures be different from API ones. --- scripts/genmsgpack.lua | 58 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 18 deletions(-) (limited to 'scripts/genmsgpack.lua') diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua index dd3caab5e4..d47d637548 100644 --- a/scripts/genmsgpack.lua +++ b/scripts/genmsgpack.lua @@ -216,6 +216,14 @@ local function real_type(type) return rv end +local function attr_name(rt) + if rt == 'Float' then + return 'floating' + else + return rt:lower() + end +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. @@ -253,7 +261,7 @@ for i = 1, #functions do output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') else output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {') - output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..rt:lower()..';') + output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..attr_name(rt)..';') end 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 @@ -368,6 +376,7 @@ output:write([[ #include "nvim/func_attr.h" #include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" #include "nvim/viml/executor/converter.h" ]]) include_headers(output, headers) @@ -382,27 +391,35 @@ local function process_function(fn) static int %s(lua_State *lstate) { Error err = {.set = false}; - ]], lua_c_function_name)) + if (lua_gettop(lstate) != %i) { + api_set_error(&err, Validation, "Expected %i argument%s"); + lua_pushstring(lstate, err.msg); + return lua_error(lstate); + } + ]], lua_c_function_name, #fn.parameters, #fn.parameters, + (#fn.parameters == 1) and '' or 's')) lua_c_functions[#lua_c_functions + 1] = { binding=lua_c_function_name, api=fn.name } - cparams = '' - for j, param in ipairs(fn.parameters) do + local cparams = '' + local free_code = {} + for j = #fn.parameters,1,-1 do + param = fn.parameters[j] cparam = string.format('arg%u', j) - if param[1]:match('^ArrayOf') then - param_type = 'Array' - else - param_type = param[1] - end + param_type = real_type(param[1]) + lc_param_type = param_type:lower() write_shifted_output(output, string.format([[ - %s %s = nlua_pop_%s(lstate, &err); + const %s %s = nlua_pop_%s(lstate, &err); + if (err.set) { + %s lua_pushstring(lstate, err.msg); return lua_error(lstate); } - ]], param[1], cparam, param_type)) - cparams = cparams .. cparam .. ', ' + ]], param[1], cparam, param_type, table.concat(free_code, '\n '))) + free_code[#free_code + 1] = ('api_free_%s(%s);'):format(lc_param_type, cparam) + cparams = cparam .. ', ' .. cparams end if fn.receives_channel_id then cparams = 'INTERNAL_CALL, ' .. cparams @@ -412,7 +429,7 @@ local function process_function(fn) else cparams = cparams:gsub(', $', '') end - local name = fn.impl_name or fn.name + free_at_exit_code = table.concat(free_code, '\n ') if fn.return_type ~= 'void' then if fn.return_type:match('^ArrayOf') then return_type = 'Array' @@ -420,23 +437,27 @@ local function process_function(fn) return_type = fn.return_type end write_shifted_output(output, string.format([[ - %s ret = %s(%s); + const %s ret = %s(%s); + %s if (err.set) { lua_pushstring(lstate, err.msg); return lua_error(lstate); } nlua_push_%s(lstate, ret); + api_free_%s(ret); return 1; - ]], fn.return_type, name, cparams, return_type)) + ]], fn.return_type, fn.name, cparams, free_at_exit_code, return_type, + return_type:lower())) else write_shifted_output(output, string.format([[ %s(%s); + %s if (err.set) { lua_pushstring(lstate, err.msg); return lua_error(lstate); } return 0; - ]], name, cparams)) + ]], fn.name, cparams, free_at_exit_code)) end write_shifted_output(output, [[ } @@ -457,11 +478,12 @@ void nlua_add_api_functions(lua_State *lstate) ]], #lua_c_functions)) for _, func in ipairs(lua_c_functions) do output:write(string.format([[ + lua_pushcfunction(lstate, &%s); - lua_setfield(lstate, -2, "%s"); - ]], func.binding, func.api)) + lua_setfield(lstate, -2, "%s");]], func.binding, func.api)) end output:write([[ + lua_setfield(lstate, -2, "api"); } ]]) -- cgit From 5c1b9a0d2af86461f56f0d27ed275456921f6187 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 20 Jan 2017 23:06:22 +0300 Subject: api: Reserve more numbers for internal calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reasoning; currently INTERNAL_CALL is mostly used to determine whether it is needed to deal with NL-used-as-NUL problem. This code is useful for nvim_… API calls done from VimL, but not for API calls done from lua, yet lua needs to supply something as channel_id. --- scripts/genmsgpack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/genmsgpack.lua') diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua index d47d637548..aed56b29eb 100644 --- a/scripts/genmsgpack.lua +++ b/scripts/genmsgpack.lua @@ -422,7 +422,7 @@ local function process_function(fn) cparams = cparam .. ', ' .. cparams end if fn.receives_channel_id then - cparams = 'INTERNAL_CALL, ' .. cparams + cparams = 'LUA_INTERNAL_CALL, ' .. cparams end if fn.can_fail then cparams = cparams .. '&err' -- cgit From 600bee9d4fa22ab914175a9edf82bb3503f47cda Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 21 Jan 2017 00:59:13 +0300 Subject: genmsgpack: Include error source in error messages --- scripts/genmsgpack.lua | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'scripts/genmsgpack.lua') diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua index aed56b29eb..2a7cb4bacf 100644 --- a/scripts/genmsgpack.lua +++ b/scripts/genmsgpack.lua @@ -393,8 +393,7 @@ local function process_function(fn) Error err = {.set = false}; if (lua_gettop(lstate) != %i) { api_set_error(&err, Validation, "Expected %i argument%s"); - lua_pushstring(lstate, err.msg); - return lua_error(lstate); + return luaL_error(lstate, "%%s", err.msg); } ]], lua_c_function_name, #fn.parameters, #fn.parameters, (#fn.parameters == 1) and '' or 's')) @@ -414,8 +413,7 @@ local function process_function(fn) if (err.set) { %s - lua_pushstring(lstate, err.msg); - return lua_error(lstate); + return luaL_error(lstate, "%%s", err.msg); } ]], param[1], cparam, param_type, table.concat(free_code, '\n '))) free_code[#free_code + 1] = ('api_free_%s(%s);'):format(lc_param_type, cparam) @@ -440,8 +438,7 @@ local function process_function(fn) const %s ret = %s(%s); %s if (err.set) { - lua_pushstring(lstate, err.msg); - return lua_error(lstate); + return luaL_error(lstate, "%%s", err.msg); } nlua_push_%s(lstate, ret); api_free_%s(ret); @@ -453,8 +450,7 @@ local function process_function(fn) %s(%s); %s if (err.set) { - lua_pushstring(lstate, err.msg); - return lua_error(lstate); + return luaL_error(lstate, "%%s", err.msg); } return 0; ]], fn.name, cparams, free_at_exit_code)) @@ -465,7 +461,7 @@ local function process_function(fn) end for _, fn in ipairs(functions) do - if not fn.noeval then + if not fn.noeval or fn.name:sub(1, 4) == '_vim' then process_function(fn) end end -- cgit From f98a3d85ed2f34a62300097fd30b393a3b3be393 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 11 Apr 2017 01:09:36 +0300 Subject: lua: Move files from src/nvim/viml/executor to src/nvim/lua --- scripts/genmsgpack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/genmsgpack.lua') diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua index 2a7cb4bacf..c3b3f42618 100644 --- a/scripts/genmsgpack.lua +++ b/scripts/genmsgpack.lua @@ -377,7 +377,7 @@ output:write([[ #include "nvim/func_attr.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/viml/executor/converter.h" +#include "nvim/lua/converter.h" ]]) include_headers(output, headers) output:write('\n') -- cgit From 7d0fc179e689ca12f647166b8687928a7a65e380 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Tue, 11 Apr 2017 23:56:18 +0300 Subject: genmsgpack: Do not export functions with __ --- scripts/genmsgpack.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'scripts/genmsgpack.lua') diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua index c3b3f42618..9018ac8576 100644 --- a/scripts/genmsgpack.lua +++ b/scripts/genmsgpack.lua @@ -127,7 +127,9 @@ local deprecated_aliases = require("api.dispatch_deprecated") for i,f in ipairs(shallowcopy(functions)) do local ismethod = false if startswith(f.name, "nvim_") then - if f.since == nil then + if startswith(f.name, "nvim__") then + f.since = -1 + elseif f.since == nil then print("Function "..f.name.." lacks since field.\n") os.exit(1) end @@ -174,11 +176,13 @@ exported_attributes = {'name', 'parameters', 'return_type', 'method', 'since', 'deprecated_since'} exported_functions = {} for _,f in ipairs(functions) do - local f_exported = {} - for _,attr in ipairs(exported_attributes) do - f_exported[attr] = f[attr] + if not startswith(f.name, "nvim__") then + local f_exported = {} + for _,attr in ipairs(exported_attributes) do + f_exported[attr] = f[attr] + end + exported_functions[#exported_functions+1] = f_exported end - exported_functions[#exported_functions+1] = f_exported end -- cgit From 577befef9729ba711eece923a495fff51f73d324 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 8 May 2017 20:54:09 +0300 Subject: generators: Do not leak error messages text --- scripts/genmsgpack.lua | 51 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) (limited to 'scripts/genmsgpack.lua') diff --git a/scripts/genmsgpack.lua b/scripts/genmsgpack.lua index fff5f9c7ed..86a051fb4c 100644 --- a/scripts/genmsgpack.lua +++ b/scripts/genmsgpack.lua @@ -401,7 +401,7 @@ local function process_function(fn) Error err = ERROR_INIT; if (lua_gettop(lstate) != %i) { api_set_error(&err, kErrorTypeValidation, "Expected %i argument%s"); - return luaL_error(lstate, "%%s", err.msg); + goto exit_0; } ]], lua_c_function_name, #fn.parameters, #fn.parameters, (#fn.parameters == 1) and '' or 's')) @@ -420,11 +420,11 @@ local function process_function(fn) const %s %s = nlua_pop_%s(lstate, &err); if (ERROR_SET(&err)) { - %s - return luaL_error(lstate, "%%s", err.msg); + goto exit_%u; } - ]], param[1], cparam, param_type, table.concat(free_code, '\n '))) - free_code[#free_code + 1] = ('api_free_%s(%s);'):format(lc_param_type, cparam) + ]], param[1], cparam, param_type, #fn.parameters - j)) + free_code[#free_code + 1] = ('api_free_%s(%s);'):format( + lc_param_type, cparam) cparams = cparam .. ', ' .. cparams end if fn.receives_channel_id then @@ -435,7 +435,28 @@ local function process_function(fn) else cparams = cparams:gsub(', $', '') end - free_at_exit_code = table.concat(free_code, '\n ') + local free_at_exit_code = '' + for i = 1, #free_code do + local rev_i = #free_code - i + 1 + local code = free_code[rev_i] + if i == 1 then + free_at_exit_code = free_at_exit_code .. ('\n %s'):format(code) + else + free_at_exit_code = free_at_exit_code .. ('\n exit_%u:\n %s'):format( + rev_i, code) + end + end + local err_throw_code = [[ + + exit_0: + if (ERROR_SET(&err)) { + luaL_where(lstate, 1); + lua_pushstring(lstate, err.msg); + api_clear_error(&err); + lua_concat(lstate, 2); + return lua_error(lstate); + } + ]] if fn.return_type ~= 'void' then if fn.return_type:match('^ArrayOf') then return_type = 'Array' @@ -444,24 +465,20 @@ local function process_function(fn) end write_shifted_output(output, string.format([[ const %s ret = %s(%s); - %s - if (ERROR_SET(&err)) { - return luaL_error(lstate, "%%s", err.msg); - } nlua_push_%s(lstate, ret); api_free_%s(ret); + %s + %s return 1; - ]], fn.return_type, fn.name, cparams, free_at_exit_code, return_type, - return_type:lower())) + ]], fn.return_type, fn.name, cparams, return_type, return_type:lower(), + free_at_exit_code, err_throw_code)) else write_shifted_output(output, string.format([[ %s(%s); - %s - if (ERROR_SET(&err)) { - return luaL_error(lstate, "%%s", err.msg); - } + %s + %s return 0; - ]], fn.name, cparams, free_at_exit_code)) + ]], fn.name, cparams, free_at_exit_code, err_throw_code)) end write_shifted_output(output, [[ } -- cgit