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/gendispatch.lua | 334 ---------------------------- scripts/generate_vim_module.lua | 38 ++++ scripts/genmsgpack.lua | 469 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 507 insertions(+), 334 deletions(-) delete mode 100644 scripts/gendispatch.lua create mode 100644 scripts/generate_vim_module.lua create mode 100644 scripts/genmsgpack.lua (limited to 'scripts') diff --git a/scripts/gendispatch.lua b/scripts/gendispatch.lua deleted file mode 100644 index 45a4f4a4de..0000000000 --- a/scripts/gendispatch.lua +++ /dev/null @@ -1,334 +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('_$') -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 -assert(#arg >= 3) -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[#arg-2] --- output h file with packed metadata -funcs_metadata_outputf = arg[#arg-1] --- output metadata mpack file, for use by other build scripts -mpack_outputf = arg[#arg] - --- set of function names, used to detect duplicates -function_names = {} - --- read each input file, parse and append to the api metadata -for i = 2, #arg - 3 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() diff --git a/scripts/generate_vim_module.lua b/scripts/generate_vim_module.lua new file mode 100644 index 0000000000..954f1c38be --- /dev/null +++ b/scripts/generate_vim_module.lua @@ -0,0 +1,38 @@ +assert(#arg == 2) + +module_file = arg[1] +target_file = arg[2] + +module = io.open(module_file, 'r') +target = io.open(target_file, 'w') + +target:write('#include \n\n') +target:write('static const uint8_t vim_module[] = {\n') + +num_bytes = 0 +MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line +target:write(' ') + +increase_num_bytes = function() + num_bytes = num_bytes + 1 + if num_bytes == MAX_NUM_BYTES then + num_bytes = 0 + target:write('\n ') + end +end + +for line in module:lines() do + for i = 1,string.len(line) do + byte = string.byte(line, i) + assert(byte ~= 0) + target:write(string.format(' %3u,', byte)) + increase_num_bytes() + end + target:write(string.format(' %3u,', string.byte('\n', 1))) + increase_num_bytes() +end + +target:write(' 0};\n') + +module:close() +target:close() 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') 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') 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') 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 6b4a51f7ea49a6ef8305831e8d96b2a7cc4ba5f3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 22 Jan 2017 04:34:26 +0300 Subject: scripts: Make generate_vim_module more generic --- scripts/gencharblob.lua | 48 +++++++++++++++++++++++++++++++++++++++++ scripts/generate_vim_module.lua | 38 -------------------------------- 2 files changed, 48 insertions(+), 38 deletions(-) create mode 100644 scripts/gencharblob.lua delete mode 100644 scripts/generate_vim_module.lua (limited to 'scripts') diff --git a/scripts/gencharblob.lua b/scripts/gencharblob.lua new file mode 100644 index 0000000000..d860375e26 --- /dev/null +++ b/scripts/gencharblob.lua @@ -0,0 +1,48 @@ +if arg[1] == '--help' then + print('Usage:') + print(' gencharblob.lua source target varname') + print('') + print('Generates C file with big uint8_t blob.') + print('Blob will be stored in a static const array named varname.') + os.exit() +end + +assert(#arg == 3) + +local source_file = arg[1] +local target_file = arg[2] +local varname = arg[3] + +source = io.open(source_file, 'r') +target = io.open(target_file, 'w') + +target:write('#include \n\n') +target:write(('static const uint8_t %s[] = {\n'):format(varname)) + +num_bytes = 0 +MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line +target:write(' ') + +increase_num_bytes = function() + num_bytes = num_bytes + 1 + if num_bytes == MAX_NUM_BYTES then + num_bytes = 0 + target:write('\n ') + end +end + +for line in source:lines() do + for i = 1,string.len(line) do + byte = string.byte(line, i) + assert(byte ~= 0) + target:write(string.format(' %3u,', byte)) + increase_num_bytes() + end + target:write(string.format(' %3u,', string.byte('\n', 1))) + increase_num_bytes() +end + +target:write(' 0};\n') + +source:close() +target:close() diff --git a/scripts/generate_vim_module.lua b/scripts/generate_vim_module.lua deleted file mode 100644 index 954f1c38be..0000000000 --- a/scripts/generate_vim_module.lua +++ /dev/null @@ -1,38 +0,0 @@ -assert(#arg == 2) - -module_file = arg[1] -target_file = arg[2] - -module = io.open(module_file, 'r') -target = io.open(target_file, 'w') - -target:write('#include \n\n') -target:write('static const uint8_t vim_module[] = {\n') - -num_bytes = 0 -MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line -target:write(' ') - -increase_num_bytes = function() - num_bytes = num_bytes + 1 - if num_bytes == MAX_NUM_BYTES then - num_bytes = 0 - target:write('\n ') - end -end - -for line in module:lines() do - for i = 1,string.len(line) do - byte = string.byte(line, i) - assert(byte ~= 0) - target:write(string.format(' %3u,', byte)) - increase_num_bytes() - end - target:write(string.format(' %3u,', string.byte('\n', 1))) - increase_num_bytes() -end - -target:write(' 0};\n') - -module:close() -target:close() -- cgit From f74322b9a5695d2a3bf31e1da05197d700b94c76 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jan 2017 00:46:58 +0300 Subject: gendeclarations: Save where declaration is comping from --- scripts/gendeclarations.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua index ff69b18ae4..6358ea4778 100755 --- a/scripts/gendeclarations.lua +++ b/scripts/gendeclarations.lua @@ -188,23 +188,29 @@ local footer = [[ local non_static = header local static = header -local filepattern = '^#%a* %d+ "[^"]-/?([^"/]+)"' +local filepattern = '^#%a* (%d+) "[^"]-/?([^"/]+)"' local curfile init = 0 curfile = nil neededfile = fname:match('[^/]+$') +local declline = 0 +local declendpos = 0 while init ~= nil do init = text:find('\n', init) if init == nil then break end init = init + 1 + declline = declline + 1 if text:sub(init, init) == '#' then - file = text:match(filepattern, init) + local line, file = text:match(filepattern, init) if file ~= nil then curfile = file end + declline = tonumber(line) - 1 + elseif init < declendpos then + -- Skipping over declaration elseif curfile == neededfile then s = init e = pattern:match(text, init) @@ -225,13 +231,15 @@ while init ~= nil do declaration = declaration:gsub(' ?(%*+) ?', ' %1') declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1') declaration = declaration:gsub(' $', '') - declaration = declaration .. ';\n' + declaration = declaration .. ';' + declaration = declaration .. ' // ' .. curfile .. ':' .. declline + declaration = declaration .. '\n' if text:sub(s, s + 5) == 'static' then static = static .. declaration else non_static = non_static .. declaration end - init = e + declendpos = e end end end -- cgit From c470fc32a8c96fb153b779489c22b8e86003e9f0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jan 2017 00:52:27 +0300 Subject: gendeclarations: Also save information about directory --- scripts/gendeclarations.lua | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua index 6358ea4778..5d5939f7d1 100755 --- a/scripts/gendeclarations.lua +++ b/scripts/gendeclarations.lua @@ -188,14 +188,15 @@ local footer = [[ local non_static = header local static = header -local filepattern = '^#%a* (%d+) "[^"]-/?([^"/]+)"' +local filepattern = '^#%a* (%d+) "([^"]-)/?([^"/]+)"' local curfile -init = 0 -curfile = nil -neededfile = fname:match('[^/]+$') +local init = 0 +local curfile = nil +local neededfile = fname:match('[^/]+$') local declline = 0 local declendpos = 0 +local curdir = nil while init ~= nil do init = text:find('\n', init) if init == nil then @@ -204,11 +205,17 @@ while init ~= nil do init = init + 1 declline = declline + 1 if text:sub(init, init) == '#' then - local line, file = text:match(filepattern, init) + local line, dir, file = text:match(filepattern, init) if file ~= nil then curfile = file end declline = tonumber(line) - 1 + local curdir_start = dir:find('src/nvim/') + if curdir_start ~= nil then + curdir = dir:sub(curdir_start + #('src/nvim/')) + else + curdir = dir + end elseif init < declendpos then -- Skipping over declaration elseif curfile == neededfile then @@ -232,7 +239,8 @@ while init ~= nil do declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1') declaration = declaration:gsub(' $', '') declaration = declaration .. ';' - declaration = declaration .. ' // ' .. curfile .. ':' .. declline + declaration = declaration .. (' // %s/%s:%u'):format( + curdir, curfile, declline) declaration = declaration .. '\n' if text:sub(s, s + 5) == 'static' then static = static .. declaration -- cgit From 52c7066f4b546419a1838b41e68a5d1650ac498e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jan 2017 01:07:18 +0300 Subject: gendeclarations: Handle case when text did not match --- scripts/gendeclarations.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua index 5d5939f7d1..3f948b91df 100755 --- a/scripts/gendeclarations.lua +++ b/scripts/gendeclarations.lua @@ -208,13 +208,15 @@ while init ~= nil do local line, dir, file = text:match(filepattern, init) if file ~= nil then curfile = file - end - declline = tonumber(line) - 1 - local curdir_start = dir:find('src/nvim/') - if curdir_start ~= nil then - curdir = dir:sub(curdir_start + #('src/nvim/')) + declline = tonumber(line) - 1 + local curdir_start = dir:find('src/nvim/') + if curdir_start ~= nil then + curdir = dir:sub(curdir_start + #('src/nvim/')) + else + curdir = dir + end else - curdir = dir + declline = declline - 1 end elseif init < declendpos then -- Skipping over declaration -- cgit From ae4adcc70735a89bffb110bcf9d5a993b0786c4d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 28 Jan 2017 03:47:15 +0300 Subject: gendeclarations: Make declarations generator work with macros funcs Now it checks functions also after every semicolon and closing figure brace, possibly preceded by whitespaces (tabs and spaces). This should make messing with declarations in macros not needed. --- scripts/gendeclarations.lua | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'scripts') diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua index 3f948b91df..e999e53e4a 100755 --- a/scripts/gendeclarations.lua +++ b/scripts/gendeclarations.lua @@ -69,17 +69,18 @@ local word = branch( right_word ) ) +local inline_comment = concat( + lit('/*'), + any_amount(concat( + neg_look_ahead(lit('*/')), + any_character + )), + lit('*/') +) local spaces = any_amount(branch( s, -- Comments are really handled by preprocessor, so the following is not needed - concat( - lit('/*'), - any_amount(concat( - neg_look_ahead(lit('*/')), - any_character - )), - lit('*/') - ), + inline_comment, concat( lit('//'), any_amount(concat( @@ -110,6 +111,7 @@ local typ = one_or_more(typ_part) local typ_id = two_or_more(typ_part) local arg = typ_id -- argument name is swallowed by typ local pattern = concat( + any_amount(branch(set(' ', '\t'), inline_comment)), typ_id, -- return type with function name spaces, lit('('), @@ -197,17 +199,22 @@ local neededfile = fname:match('[^/]+$') local declline = 0 local declendpos = 0 local curdir = nil +local is_needed_file = false while init ~= nil do - init = text:find('\n', init) + init = text:find('[\n;}]', init) if init == nil then break end + local init_is_nl = text:sub(init, init) == '\n' init = init + 1 - declline = declline + 1 - if text:sub(init, init) == '#' then + if init_is_nl and is_needed_file then + declline = declline + 1 + end + if init_is_nl and text:sub(init, init) == '#' then local line, dir, file = text:match(filepattern, init) if file ~= nil then curfile = file + is_needed_file = (curfile == neededfile) declline = tonumber(line) - 1 local curdir_start = dir:find('src/nvim/') if curdir_start ~= nil then @@ -220,7 +227,7 @@ while init ~= nil do end elseif init < declendpos then -- Skipping over declaration - elseif curfile == neededfile then + elseif is_needed_file then s = init e = pattern:match(text, init) if e ~= nil then @@ -240,11 +247,12 @@ while init ~= nil do declaration = declaration:gsub(' ?(%*+) ?', ' %1') declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1') declaration = declaration:gsub(' $', '') + declaration = declaration:gsub('^ ', '') declaration = declaration .. ';' declaration = declaration .. (' // %s/%s:%u'):format( curdir, curfile, declline) declaration = declaration .. '\n' - if text:sub(s, s + 5) == 'static' then + if declaration:sub(1, 6) == 'static' then static = static .. declaration else non_static = non_static .. declaration -- 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') 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') 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') 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