diff options
Diffstat (limited to 'src/nvim/generators/gen_api_dispatch.lua')
| -rw-r--r-- | src/nvim/generators/gen_api_dispatch.lua | 990 |
1 files changed, 0 insertions, 990 deletions
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua deleted file mode 100644 index 378297d86a..0000000000 --- a/src/nvim/generators/gen_api_dispatch.lua +++ /dev/null @@ -1,990 +0,0 @@ --- Example (manual) invocation: --- --- make --- cp build/nvim_version.lua src/nvim/ --- cd src/nvim --- nvim -l generators/gen_api_dispatch.lua "../../build/src/nvim/auto/api/private/dispatch_wrappers.generated.h" "../../build/src/nvim/auto/api/private/api_metadata.generated.h" "../../build/funcs_metadata.mpack" "../../build/src/nvim/auto/lua_api_c_bindings.generated.h" "../../build/src/nvim/auto/keysets_defs.generated.h" "../../build/ui_metadata.mpack" "../../build/cmake.config/auto/versiondef_git.h" "./api/autocmd.h" "./api/buffer.h" "./api/command.h" "./api/deprecated.h" "./api/extmark.h" "./api/keysets_defs.h" "./api/options.h" "./api/tabpage.h" "./api/ui.h" "./api/vim.h" "./api/vimscript.h" "./api/win_config.h" "./api/window.h" "../../build/include/api/autocmd.h.generated.h" "../../build/include/api/buffer.h.generated.h" "../../build/include/api/command.h.generated.h" "../../build/include/api/deprecated.h.generated.h" "../../build/include/api/extmark.h.generated.h" "../../build/include/api/options.h.generated.h" "../../build/include/api/tabpage.h.generated.h" "../../build/include/api/ui.h.generated.h" "../../build/include/api/vim.h.generated.h" "../../build/include/api/vimscript.h.generated.h" "../../build/include/api/win_config.h.generated.h" "../../build/include/api/window.h.generated.h" - -local mpack = vim.mpack - -local hashy = require 'generators.hashy' - -local pre_args = 7 -assert(#arg >= pre_args) --- output h file with generated dispatch functions (dispatch_wrappers.generated.h) -local dispatch_outputf = arg[1] --- output h file with packed metadata (api_metadata.generated.h) -local api_metadata_outputf = arg[2] --- output metadata mpack file, for use by other build scripts (funcs_metadata.mpack) -local mpack_outputf = arg[3] -local lua_c_bindings_outputf = arg[4] -- lua_api_c_bindings.generated.c -local keysets_outputf = arg[5] -- keysets_defs.generated.h -local ui_metadata_inputf = arg[6] -- ui events metadata -local git_version_inputf = arg[7] -- git version header - -local functions = {} - --- names of all headers relative to the source root (for inclusion in the --- generated file) -local headers = {} - --- set of function names, used to detect duplicates -local function_names = {} - -local c_grammar = require('generators.c_grammar') - -local startswith = vim.startswith - -local function add_function(fn) - local public = startswith(fn.name, 'nvim_') or fn.deprecated_since - if public and not fn.noexport then - functions[#functions + 1] = fn - function_names[fn.name] = true - if - #fn.parameters >= 2 - and fn.parameters[2][1] == 'Array' - and fn.parameters[2][2] == 'uidata' - then - -- function receives the "args" as a parameter - fn.receives_array_args = true - -- remove the args parameter - table.remove(fn.parameters, 2) - end - 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 - if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'lstate' then - fn.has_lua_imp = true - fn.parameters[#fn.parameters] = nil - end - if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'arena' then - fn.receives_arena = true - fn.parameters[#fn.parameters] = nil - end - end -end - -local keysets = {} - -local function add_keyset(val) - local keys = {} - local types = {} - local c_names = {} - local is_set_name = 'is_set__' .. val.keyset_name .. '_' - local has_optional = false - for i, field in ipairs(val.fields) do - local dict_key = field.dict_key or field.name - if field.type ~= 'Object' then - types[dict_key] = field.type - end - if field.name ~= is_set_name and field.type ~= 'OptionalKeys' then - table.insert(keys, dict_key) - if dict_key ~= field.name then - c_names[dict_key] = field.name - end - else - if i > 1 then - error("'is_set__{type}_' must be first if present") - elseif field.name ~= is_set_name then - error(val.keyset_name .. ': name of first key should be ' .. is_set_name) - elseif field.type ~= 'OptionalKeys' then - error("'" .. is_set_name .. "' must have type 'OptionalKeys'") - end - has_optional = true - end - end - table.insert(keysets, { - name = val.keyset_name, - keys = keys, - c_names = c_names, - types = types, - has_optional = has_optional, - }) -end - -local ui_options_text = nil - --- read each input file, parse and append to the api metadata -for i = pre_args + 1, #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 = assert(io.open(full_path, 'rb')) - - local text = input:read('*all') - local tmp = c_grammar.grammar:match(text) - for j = 1, #tmp do - local val = tmp[j] - if val.keyset_name then - add_keyset(val) - elseif val.name then - add_function(val) - end - end - - ui_options_text = ui_options_text or string.match(text, 'ui_ext_names%[][^{]+{([^}]+)}') - 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 - --- Export functions under older deprecated names. --- These will be removed eventually. -local deprecated_aliases = require('api.dispatch_deprecated') -for _, f in ipairs(shallowcopy(functions)) do - local ismethod = false - if startswith(f.name, 'nvim_') then - if startswith(f.name, 'nvim__') or f.name == 'nvim_error_event' then - f.since = -1 - elseif f.since == nil then - print('Function ' .. f.name .. ' lacks since field.\n') - os.exit(1) - end - f.since = tonumber(f.since) - if f.deprecated_since ~= nil then - f.deprecated_since = tonumber(f.deprecated_since) - end - - 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 - f.remote = f.remote_only or not f.lua_only - f.lua = f.lua_only or not f.remote_only - f.eval = (not f.lua_only) and not f.remote_only - else - f.deprecated_since = tonumber(f.deprecated_since) - assert(f.deprecated_since == 1) - f.remote = true - f.since = 0 - 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.lua = false - newf.eval = false - newf.since = 0 - newf.deprecated_since = 1 - functions[#functions + 1] = newf - end -end - --- don't expose internal attributes like "impl_name" in public metadata -local exported_attributes = { 'name', 'return_type', 'method', 'since', 'deprecated_since' } -local exported_functions = {} -for _, f in ipairs(functions) do - if not (startswith(f.name, 'nvim__') or f.name == 'nvim_error_event' or f.name == 'redraw') then - local f_exported = {} - for _, attr in ipairs(exported_attributes) do - f_exported[attr] = f[attr] - end - f_exported.parameters = {} - for i, param in ipairs(f.parameters) do - if param[1] == 'DictOf(LuaRef)' then - param = { 'Dict', param[2] } - elseif startswith(param[1], 'Dict(') then - param = { 'Dict', param[2] } - end - f_exported.parameters[i] = param - end - if startswith(f.return_type, 'Dict(') then - f_exported.return_type = 'Dict' - end - exported_functions[#exported_functions + 1] = f_exported - end -end - -local ui_options = { 'rgb' } -for x in string.gmatch(ui_options_text, '"([a-z][a-z_]+)"') do - table.insert(ui_options, x) -end - -local version = require 'nvim_version' -- `build/nvim_version.lua` file. -local git_version = io.open(git_version_inputf):read '*a' -local version_build = string.match(git_version, '#define NVIM_VERSION_BUILD "([^"]+)"') or vim.NIL - --- serialize the API metadata using msgpack and embed into the resulting --- binary for easy querying by clients -local api_metadata_output = assert(io.open(api_metadata_outputf, 'wb')) -local pieces = {} - --- Naively using mpack.encode({foo=x, bar=y}) will make the build --- "non-reproducible". Emit maps directly as FIXDICT(2) "foo" x "bar" y instead -local function fixdict(num) - if num > 15 then - error 'implement more dict codes' - end - table.insert(pieces, string.char(128 + num)) -end -local function put(item, item2) - table.insert(pieces, mpack.encode(item)) - if item2 ~= nil then - table.insert(pieces, mpack.encode(item2)) - end -end - -fixdict(6) - -put('version') -fixdict(1 + #version) -for _, item in ipairs(version) do - -- NB: all items are mandatory. But any error will be less confusing - -- with placeholder vim.NIL (than invalid mpack data) - local val = item[2] == nil and vim.NIL or item[2] - put(item[1], val) -end -put('build', version_build) - -put('functions', exported_functions) -put('ui_events') -table.insert(pieces, io.open(ui_metadata_inputf, 'rb'):read('*all')) -put('ui_options', ui_options) - -put('error_types') -fixdict(2) -put('Exception', { id = 0 }) -put('Validation', { id = 1 }) - -put('types') -local types = - { { 'Buffer', 'nvim_buf_' }, { 'Window', 'nvim_win_' }, { 'Tabpage', 'nvim_tabpage_' } } -fixdict(#types) -for i, item in ipairs(types) do - put(item[1]) - fixdict(2) - put('id', i - 1) - put('prefix', item[2]) -end - -local packed = table.concat(pieces) -local dump_bin_array = require('generators.dump_bin_array') -dump_bin_array(api_metadata_output, 'packed_api_metadata', packed) -api_metadata_output:close() - --- start building the dispatch wrapper output -local output = assert(io.open(dispatch_outputf, 'wb')) - -local keysets_defs = assert(io.open(keysets_outputf, 'wb')) - --- =========================================================================== --- NEW API FILES MUST GO HERE. --- --- When creating a new API file, you must include it here, --- so that the dispatcher can find the C functions that you are creating! --- =========================================================================== -output:write([[ -#include "nvim/errors.h" -#include "nvim/ex_docmd.h" -#include "nvim/ex_getln.h" -#include "nvim/globals.h" -#include "nvim/log.h" -#include "nvim/map_defs.h" - -#include "nvim/api/autocmd.h" -#include "nvim/api/buffer.h" -#include "nvim/api/command.h" -#include "nvim/api/deprecated.h" -#include "nvim/api/extmark.h" -#include "nvim/api/options.h" -#include "nvim/api/tabpage.h" -#include "nvim/api/ui.h" -#include "nvim/api/vim.h" -#include "nvim/api/vimscript.h" -#include "nvim/api/win_config.h" -#include "nvim/api/window.h" -#include "nvim/ui_client.h" - -]]) - -keysets_defs:write('// IWYU pragma: private, include "nvim/api/private/dispatch.h"\n\n') - -for _, k in ipairs(keysets) do - local neworder, hashfun = hashy.hashy_hash(k.name, k.keys, function(idx) - return k.name .. '_table[' .. idx .. '].str' - end) - - keysets_defs:write('extern KeySetLink ' .. k.name .. '_table[' .. (1 + #neworder) .. '];\n') - - local function typename(type) - if type == 'HLGroupID' then - return 'kObjectTypeInteger' - elseif not type or vim.startswith(type, 'Union') then - return 'kObjectTypeNil' - elseif vim.startswith(type, 'LuaRefOf') then - return 'kObjectTypeLuaRef' - elseif type == 'StringArray' then - return 'kUnpackTypeStringArray' - elseif vim.startswith(type, 'ArrayOf') then - return 'kObjectTypeArray' - else - return 'kObjectType' .. type - end - end - - output:write('KeySetLink ' .. k.name .. '_table[] = {\n') - for i, key in ipairs(neworder) do - local ind = -1 - if k.has_optional then - ind = i - keysets_defs:write('#define KEYSET_OPTIDX_' .. k.name .. '__' .. key .. ' ' .. ind .. '\n') - end - output:write( - ' {"' - .. key - .. '", offsetof(KeyDict_' - .. k.name - .. ', ' - .. (k.c_names[key] or key) - .. '), ' - .. typename(k.types[key]) - .. ', ' - .. ind - .. ', ' - .. (k.types[key] == 'HLGroupID' and 'true' or 'false') - .. '},\n' - ) - end - output:write(' {NULL, 0, kObjectTypeNil, -1, false},\n') - output:write('};\n\n') - - output:write(hashfun) - - output:write([[ -KeySetLink *KeyDict_]] .. k.name .. [[_get_field(const char *str, size_t len) -{ - int hash = ]] .. k.name .. [[_hash(str, len); - if (hash == -1) { - return NULL; - } - return &]] .. k.name .. [[_table[hash]; -} - -]]) -end - -local function real_type(type) - local rv = type - local rmatch = string.match(type, 'Dict%(([_%w]+)%)') - if rmatch then - return 'KeyDict_' .. rmatch - elseif c_grammar.typed_container:match(rv) then - if rv:match('Array') then - rv = 'Array' - else - rv = 'Dict' - end - end - 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. -for i = 1, #functions do - local fn = functions[i] - if fn.impl_name == nil and fn.remote then - local args = {} - - output:write( - 'Object handle_' .. fn.name .. '(uint64_t channel_id, Array args, Arena* arena, Error *error)' - ) - output:write('\n{') - output:write('\n#ifdef NVIM_LOG_DEBUG') - output:write('\n DLOG("RPC: ch %" PRIu64 ": invoke ' .. fn.name .. '", channel_id);') - output:write('\n#endif') - 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 rt = real_type(param[1]) - local converted = 'arg_' .. j - output:write('\n ' .. rt .. ' ' .. converted .. ';') - end - output:write('\n') - if not fn.receives_array_args then - output:write('\n if (args.size != ' .. #fn.parameters .. ') {') - output:write( - '\n api_set_error(error, kErrorTypeException, \ - "Wrong number of arguments: expecting ' - .. #fn.parameters - .. ' but got %zu", args.size);' - ) - output:write('\n goto cleanup;') - output:write('\n }\n') - end - - -- Validation/conversion for each argument - for j = 1, #fn.parameters do - local converted, param - param = fn.parameters[j] - converted = 'arg_' .. j - local rt = real_type(param[1]) - if rt == 'Object' then - output:write('\n ' .. converted .. ' = args.items[' .. (j - 1) .. '];\n') - elseif rt:match('^KeyDict_') then - converted = '&' .. converted - output:write('\n if (args.items[' .. (j - 1) .. '].type == kObjectTypeDict) {') --luacheck: ignore 631 - output:write('\n memset(' .. converted .. ', 0, sizeof(*' .. converted .. '));') -- TODO: neeeee - output:write( - '\n if (!api_dict_to_keydict(' - .. converted - .. ', ' - .. rt - .. '_get_field, args.items[' - .. (j - 1) - .. '].data.dict, error)) {' - ) - output:write('\n goto cleanup;') - output:write('\n }') - output:write( - '\n } else if (args.items[' - .. (j - 1) - .. '].type == kObjectTypeArray && args.items[' - .. (j - 1) - .. '].data.array.size == 0) {' - ) --luacheck: ignore 631 - output:write('\n memset(' .. converted .. ', 0, sizeof(*' .. converted .. '));') - - output:write('\n } else {') - output:write( - '\n api_set_error(error, kErrorTypeException, \ - "Wrong type for argument ' - .. j - .. ' when calling ' - .. fn.name - .. ', expecting ' - .. param[1] - .. '");' - ) - output:write('\n goto cleanup;') - output:write('\n }\n') - else - 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.' - .. 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 - 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 - if rt:match('^Float$') then - -- accept integers for Floats - output:write('\n } else if (args.items[' .. (j - 1) .. '].type == kObjectTypeInteger) {') - output:write( - '\n ' .. converted .. ' = (Float)args.items[' .. (j - 1) .. '].data.integer;' - ) - end - -- accept empty lua tables as empty dictionaries - if rt:match('^Dict') then - output:write( - '\n } else if (args.items[' - .. (j - 1) - .. '].type == kObjectTypeArray && args.items[' - .. (j - 1) - .. '].data.array.size == 0) {' - ) --luacheck: ignore 631 - output:write('\n ' .. converted .. ' = (Dict)ARRAY_DICT_INIT;') - end - output:write('\n } else {') - output:write( - '\n api_set_error(error, kErrorTypeException, \ - "Wrong type for argument ' - .. j - .. ' when calling ' - .. fn.name - .. ', expecting ' - .. param[1] - .. '");' - ) - output:write('\n goto cleanup;') - output:write('\n }\n') - end - args[#args + 1] = converted - end - - if fn.textlock then - output:write('\n if (text_locked()) {') - output:write('\n api_set_error(error, kErrorTypeException, "%s", get_text_locked_msg());') - output:write('\n goto cleanup;') - output:write('\n }\n') - elseif fn.textlock_allow_cmdwin then - output:write('\n if (textlock != 0 || expr_map_locked()) {') - output:write('\n api_set_error(error, kErrorTypeException, "%s", e_textlock);') - output:write('\n goto cleanup;') - output:write('\n }\n') - end - - -- function call - 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 .. '(') - - local call_args = {} - if fn.receives_channel_id then - table.insert(call_args, 'channel_id') - end - - if fn.receives_array_args then - table.insert(call_args, 'args') - end - - for _, a in ipairs(args) do - table.insert(call_args, a) - end - - if fn.receives_arena then - table.insert(call_args, 'arena') - end - - if fn.has_lua_imp then - table.insert(call_args, 'NULL') - end - - if fn.can_fail then - table.insert(call_args, 'error') - end - - output:write(table.concat(call_args, ', ')) - output:write(');\n') - - if fn.can_fail then - -- if the function can fail, also pass a pointer to the local error object - -- and check for the error - output:write('\n if (ERROR_SET(error)) {') - output:write('\n goto cleanup;') - output:write('\n }\n') - end - - local ret_type = real_type(fn.return_type) - if string.match(ret_type, '^KeyDict_') then - local table = string.sub(ret_type, 9) .. '_table' - output:write( - '\n ret = DICT_OBJ(api_keydict_to_dict(&rv, ' - .. table - .. ', ARRAY_SIZE(' - .. table - .. '), arena));' - ) - elseif ret_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 - -local remote_fns = {} -for _, fn in ipairs(functions) do - if fn.remote then - remote_fns[fn.name] = fn - end -end -remote_fns.redraw = { impl_name = 'ui_client_redraw', fast = true } - -local names = vim.tbl_keys(remote_fns) -table.sort(names) -local hashorder, hashfun = hashy.hashy_hash('msgpack_rpc_get_handler_for', names, function(idx) - return 'method_handlers[' .. idx .. '].name' -end) - -output:write('const MsgpackRpcRequestHandler method_handlers[] = {\n') -for n, name in ipairs(hashorder) do - local fn = remote_fns[name] - fn.handler_id = n - 1 - output:write( - ' { .name = "' - .. name - .. '", .fn = handle_' - .. (fn.impl_name or fn.name) - .. ', .fast = ' - .. tostring(fn.fast) - .. ', .ret_alloc = ' - .. tostring(not not fn.ret_alloc) - .. '},\n' - ) -end -output:write('};\n\n') -output:write(hashfun) - -output:close() - -functions.keysets = keysets -local mpack_output = assert(io.open(mpack_outputf, 'wb')) -mpack_output:write(mpack.encode(functions)) -mpack_output:close() - -local function include_headers(output_handle, headers_to_include) - for i = 1, #headers_to_include do - if headers_to_include[i]:sub(-12) ~= '.generated.h' then - output_handle:write('\n#include "nvim/' .. headers_to_include[i] .. '"') - end - end -end - -local function write_shifted_output(str, ...) - str = str:gsub('\n ', '\n') - str = str:gsub('^ ', '') - str = str:gsub(' +$', '') - output:write(string.format(str, ...)) -end - --- start building lua output -output = assert(io.open(lua_c_bindings_outputf, 'wb')) - -include_headers(output, headers) -output:write('\n') - -local lua_c_functions = {} - -local function process_function(fn) - local lua_c_function_name = ('nlua_api_%s'):format(fn.name) - write_shifted_output( - [[ - - static int %s(lua_State *lstate) - { - Error err = ERROR_INIT; - Arena arena = ARENA_EMPTY; - char *err_param = 0; - if (lua_gettop(lstate) != %i) { - api_set_error(&err, kErrorTypeValidation, "Expected %i argument%s"); - goto exit_0; - } - ]], - 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, - } - - if not fn.fast then - write_shifted_output( - [[ - if (!nlua_is_deferred_safe()) { - return luaL_error(lstate, e_fast_api_disabled, "%s"); - } - ]], - fn.name - ) - end - - if fn.textlock then - write_shifted_output([[ - if (text_locked()) { - api_set_error(&err, kErrorTypeException, "%%s", get_text_locked_msg()); - goto exit_0; - } - ]]) - elseif fn.textlock_allow_cmdwin then - write_shifted_output([[ - if (textlock != 0 || expr_map_locked()) { - api_set_error(&err, kErrorTypeException, "%%s", e_textlock); - goto exit_0; - } - ]]) - end - - local cparams = '' - local free_code = {} - for j = #fn.parameters, 1, -1 do - local param = fn.parameters[j] - local cparam = string.format('arg%u', j) - local param_type = real_type(param[1]) - local extra = param_type == 'Dict' and 'false, ' or '' - local arg_free_code = '' - if param[1] == 'Object' then - extra = 'true, ' - arg_free_code = ' api_luarefs_free_object(' .. cparam .. ');' - elseif param[1] == 'DictOf(LuaRef)' then - extra = 'true, ' - arg_free_code = ' api_luarefs_free_dict(' .. cparam .. ');' - elseif param[1] == 'LuaRef' then - arg_free_code = ' api_free_luaref(' .. cparam .. ');' - end - local errshift = 0 - local seterr = '' - if string.match(param_type, '^KeyDict_') then - write_shifted_output( - [[ - %s %s = KEYDICT_INIT; - nlua_pop_keydict(lstate, &%s, %s_get_field, &err_param, &arena, &err); - ]], - param_type, - cparam, - cparam, - param_type - ) - cparam = '&' .. cparam - errshift = 1 -- free incomplete dict on error - arg_free_code = ' api_luarefs_free_keydict(' - .. cparam - .. ', ' - .. string.sub(param_type, 9) - .. '_table);' - else - write_shifted_output( - [[ - const %s %s = nlua_pop_%s(lstate, %s&arena, &err);]], - param[1], - cparam, - param_type, - extra - ) - seterr = '\n err_param = "' .. param[2] .. '";' - end - - write_shifted_output([[ - - if (ERROR_SET(&err)) {]] .. seterr .. [[ - - goto exit_%u; - } - - ]], #fn.parameters - j + errshift) - free_code[#free_code + 1] = arg_free_code - cparams = cparam .. ', ' .. cparams - end - if fn.receives_channel_id then - cparams = 'LUA_INTERNAL_CALL, ' .. cparams - end - if fn.receives_arena then - cparams = cparams .. '&arena, ' - end - - if fn.has_lua_imp then - cparams = cparams .. 'lstate, ' - end - - if fn.can_fail then - cparams = cparams .. '&err' - else - cparams = cparams:gsub(', $', '') - end - 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 and not string.match(real_type(fn.parameters[1][1]), '^KeyDict_') then - free_at_exit_code = free_at_exit_code .. ('\n%s'):format(code) - else - free_at_exit_code = free_at_exit_code .. ('\nexit_%u:\n%s'):format(rev_i, code) - end - end - local err_throw_code = [[ - -exit_0: - arena_mem_free(arena_finish(&arena)); - if (ERROR_SET(&err)) { - luaL_where(lstate, 1); - if (err_param) { - lua_pushstring(lstate, "Invalid '"); - lua_pushstring(lstate, err_param); - lua_pushstring(lstate, "': "); - } - lua_pushstring(lstate, err.msg); - api_clear_error(&err); - lua_concat(lstate, err_param ? 5 : 2); - return lua_error(lstate); - } -]] - local return_type - if fn.return_type ~= 'void' then - if fn.return_type:match('^ArrayOf') then - return_type = 'Array' - else - return_type = fn.return_type - end - local free_retval = '' - if fn.ret_alloc then - free_retval = ' api_free_' .. return_type:lower() .. '(ret);' - end - write_shifted_output(' %s ret = %s(%s);\n', fn.return_type, fn.name, cparams) - - local ret_type = real_type(fn.return_type) - local ret_mode = (ret_type == 'Object') and '&' or '' - if fn.has_lua_imp then - -- only push onto the Lua stack if we haven't already - write_shifted_output( - [[ - if (lua_gettop(lstate) == 0) { - nlua_push_%s(lstate, %sret, kNluaPushSpecial | kNluaPushFreeRefs); - } - ]], - return_type, - ret_mode - ) - elseif string.match(ret_type, '^KeyDict_') then - write_shifted_output( - ' nlua_push_keydict(lstate, &ret, %s_table);\n', - string.sub(ret_type, 9) - ) - else - local special = (fn.since ~= nil and fn.since < 11) - write_shifted_output( - ' nlua_push_%s(lstate, %sret, %s | kNluaPushFreeRefs);\n', - return_type, - ret_mode, - special and 'kNluaPushSpecial' or '0' - ) - end - - -- NOTE: we currently assume err_throw needs nothing from arena - write_shifted_output( - [[ - %s - %s - %s - return 1; - ]], - free_retval, - free_at_exit_code, - err_throw_code - ) - else - write_shifted_output( - [[ - %s(%s); - %s - %s - return 0; - ]], - fn.name, - cparams, - free_at_exit_code, - err_throw_code - ) - end - write_shifted_output([[ - } - ]]) -end - -for _, fn in ipairs(functions) do - if fn.lua or fn.name:sub(1, 4) == '_vim' then - process_function(fn) - end -end - -output:write(string.format( - [[ -void nlua_add_api_functions(lua_State *lstate) -{ - 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() -keysets_defs:close() |