diff options
author | bfredl <bjorn.linse@gmail.com> | 2024-02-12 20:40:27 +0100 |
---|---|---|
committer | bfredl <bjorn.linse@gmail.com> | 2024-02-15 10:42:06 +0100 |
commit | d60412b18e4e21f301baa2ac3f3fb7be89655e4b (patch) | |
tree | 2459531b83b937b6678ee7141badc27d0a331c29 | |
parent | 0a51e7626a95a068c7bb00d0da28a701fed758da (diff) | |
download | rneovim-d60412b18e4e21f301baa2ac3f3fb7be89655e4b.tar.gz rneovim-d60412b18e4e21f301baa2ac3f3fb7be89655e4b.tar.bz2 rneovim-d60412b18e4e21f301baa2ac3f3fb7be89655e4b.zip |
refactor(eval): use arena when converting typvals to Object
Note: this contains two _temporary_ changes which can be reverted
once the Arena vs no-Arena distinction in API wrappers has been removed.
Both nlua_push_Object and object_to_vim_take_luaref() has been changed
to take the object argument as a pointer. This is not going to be
necessary once these are only used with arena (or not at all) allocated
Objects.
The object_to_vim() variant which leaves luaref untouched might need to
stay for a little longer.
-rw-r--r-- | src/nvim/api/buffer.c | 15 | ||||
-rw-r--r-- | src/nvim/api/command.c | 32 | ||||
-rw-r--r-- | src/nvim/api/deprecated.c | 32 | ||||
-rw-r--r-- | src/nvim/api/private/converter.c | 95 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 11 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.h | 1 | ||||
-rw-r--r-- | src/nvim/api/tabpage.c | 8 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 8 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 29 | ||||
-rw-r--r-- | src/nvim/api/vimscript.c | 16 | ||||
-rw-r--r-- | src/nvim/api/window.c | 15 | ||||
-rw-r--r-- | src/nvim/context.c | 18 | ||||
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 42 | ||||
-rw-r--r-- | src/nvim/generators/gen_api_dispatch.lua | 13 | ||||
-rw-r--r-- | src/nvim/lua/converter.c | 18 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 6 | ||||
-rw-r--r-- | src/nvim/main.c | 2 | ||||
-rw-r--r-- | src/nvim/mapping.c | 13 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 6 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/helpers.c | 23 | ||||
-rw-r--r-- | src/nvim/terminal.c | 6 | ||||
-rw-r--r-- | test/unit/api/private_helpers_spec.lua | 8 |
23 files changed, 227 insertions, 192 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 993e290b2d..110154f8de 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -869,7 +869,7 @@ Integer nvim_buf_get_offset(Buffer buffer, Integer index, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_buf_get_var(Buffer buffer, String name, Error *err) +Object nvim_buf_get_var(Buffer buffer, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -878,7 +878,7 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err) return (Object)OBJECT_INIT; } - return dict_get_value(buf->b_vars, name, err); + return dict_get_value(buf->b_vars, name, arena, err); } /// Gets a changed tick of a buffer @@ -957,7 +957,7 @@ void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err) return; } - dict_set_var(buf->b_vars, name, value, false, false, err); + dict_set_var(buf->b_vars, name, value, false, false, NULL, err); } /// Removes a buffer-scoped (b:) variable @@ -974,7 +974,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err) return; } - dict_set_var(buf->b_vars, name, NIL, true, false, err); + dict_set_var(buf->b_vars, name, NIL, true, false, NULL, err); } /// Gets the full file name for the buffer @@ -1175,7 +1175,7 @@ Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col, /// uppercase/file mark set in another buffer. /// @see |nvim_buf_set_mark()| /// @see |nvim_buf_del_mark()| -ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) +ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; @@ -1205,8 +1205,9 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) pos = fm->mark; } - ADD(rv, INTEGER_OBJ(pos.lnum)); - ADD(rv, INTEGER_OBJ(pos.col)); + rv = arena_array(arena, 2); + ADD_C(rv, INTEGER_OBJ(pos.lnum)); + ADD_C(rv, INTEGER_OBJ(pos.col)); return rv; } diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 0ac3d42231..a21c9e70a7 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -305,7 +305,7 @@ end: /// - output: (boolean, default false) Whether to return command output. /// @param[out] err Error details, if any. /// @return Command output (non-error, non-shell |:!|) if `output` is true, else empty string. -String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error *err) +String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena *arena, Error *err) FUNC_API_SINCE(10) { exarg_T ea; @@ -343,7 +343,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error goto end; }); - cmdname = string_to_cstr(cmd->cmd); + cmdname = arena_string(arena, cmd->cmd).data; ea.cmd = cmdname; char *p = find_ex_command(&ea, NULL); @@ -352,9 +352,8 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error // autocommands defined, trigger the matching autocommands. if (p != NULL && ea.cmdidx == CMD_SIZE && ASCII_ISUPPER(*ea.cmd) && has_event(EVENT_CMDUNDEFINED)) { - p = xstrdup(cmdname); + p = arena_string(arena, cmd->cmd).data; int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, true, NULL); - xfree(p); // If the autocommands did something and didn't cause an error, try // finding the command again. p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd; @@ -383,28 +382,31 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error if (HAS_KEY(cmd, cmd, args)) { // Process all arguments. Convert non-String arguments to String and check if String arguments // have non-whitespace characters. + args = arena_array(arena, cmd->args.size); for (size_t i = 0; i < cmd->args.size; i++) { Object elem = cmd->args.items[i]; char *data_str; switch (elem.type) { case kObjectTypeBoolean: - data_str = xcalloc(2, sizeof(char)); + data_str = arena_alloc(arena, 2, false); data_str[0] = elem.data.boolean ? '1' : '0'; data_str[1] = '\0'; + ADD_C(args, CSTR_AS_OBJ(data_str)); break; case kObjectTypeBuffer: case kObjectTypeWindow: case kObjectTypeTabpage: case kObjectTypeInteger: - data_str = xcalloc(NUMBUFLEN, sizeof(char)); + data_str = arena_alloc(arena, NUMBUFLEN, false); snprintf(data_str, NUMBUFLEN, "%" PRId64, elem.data.integer); + ADD_C(args, CSTR_AS_OBJ(data_str)); break; case kObjectTypeString: VALIDATE_EXP(!string_iswhite(elem.data.string), "command arg", "non-whitespace", NULL, { goto end; }); - data_str = string_to_cstr(elem.data.string); + ADD_C(args, elem); break; default: VALIDATE_EXP(false, "command arg", "valid type", api_typename(elem.type), { @@ -412,8 +414,6 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error }); break; } - - ADD(args, CSTR_AS_OBJ(data_str)); } bool argc_valid; @@ -666,26 +666,20 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error } if (opts->output && capture_local.ga_len > 1) { - retv = (String){ - .data = capture_local.ga_data, - .size = (size_t)capture_local.ga_len, - }; + // TODO(bfredl): if there are more cases like this we might want custom xfree-list in arena + retv = CBUF_TO_ARENA_STR(arena, capture_local.ga_data, (size_t)capture_local.ga_len); // redir usually (except :echon) prepends a newline. if (retv.data[0] == '\n') { - memmove(retv.data, retv.data + 1, retv.size - 1); - retv.data[retv.size - 1] = '\0'; - retv.size = retv.size - 1; + retv.data++; + retv.size--; } - goto end; } clear_ga: if (opts->output) { ga_clear(&capture_local); } end: - api_free_array(args); xfree(cmdline); - xfree(cmdname); xfree(ea.args); xfree(ea.arglens); diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index c9a6036b8f..9b8cacd7c2 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -362,7 +362,7 @@ void buffer_set_line_slice(Buffer buffer, Integer start, Integer end, Boolean in /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. -Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) +Object buffer_set_var(Buffer buffer, String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -371,7 +371,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) return NIL; } - return dict_set_var(buf->b_vars, name, value, false, true, err); + return dict_set_var(buf->b_vars, name, value, false, true, arena, err); } /// Removes a buffer-scoped (b:) variable @@ -382,7 +382,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Old value -Object buffer_del_var(Buffer buffer, String name, Error *err) +Object buffer_del_var(Buffer buffer, String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -391,7 +391,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err) return NIL; } - return dict_set_var(buf->b_vars, name, NIL, true, true, err); + return dict_set_var(buf->b_vars, name, NIL, true, true, arena, err); } /// Sets a window-scoped (w:) variable @@ -406,7 +406,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err) /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. -Object window_set_var(Window window, String name, Object value, Error *err) +Object window_set_var(Window window, String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -415,7 +415,7 @@ Object window_set_var(Window window, String name, Object value, Error *err) return NIL; } - return dict_set_var(win->w_vars, name, value, false, true, err); + return dict_set_var(win->w_vars, name, value, false, true, arena, err); } /// Removes a window-scoped (w:) variable @@ -426,7 +426,7 @@ Object window_set_var(Window window, String name, Object value, Error *err) /// @param name variable name /// @param[out] err Error details, if any /// @return Old value -Object window_del_var(Window window, String name, Error *err) +Object window_del_var(Window window, String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -435,7 +435,7 @@ Object window_del_var(Window window, String name, Error *err) return NIL; } - return dict_set_var(win->w_vars, name, NIL, true, true, err); + return dict_set_var(win->w_vars, name, NIL, true, true, arena, err); } /// Sets a tab-scoped (t:) variable @@ -450,7 +450,7 @@ Object window_del_var(Window window, String name, Error *err) /// /// @warning It may return nil if there was no previous value /// or if previous value was `v:null`. -Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) +Object tabpage_set_var(Tabpage tabpage, String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -459,7 +459,7 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) return NIL; } - return dict_set_var(tab->tp_vars, name, value, false, true, err); + return dict_set_var(tab->tp_vars, name, value, false, true, arena, err); } /// Removes a tab-scoped (t:) variable @@ -470,7 +470,7 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Old value -Object tabpage_del_var(Tabpage tabpage, String name, Error *err) +Object tabpage_del_var(Tabpage tabpage, String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -479,7 +479,7 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err) return NIL; } - return dict_set_var(tab->tp_vars, name, NIL, true, true, err); + return dict_set_var(tab->tp_vars, name, NIL, true, true, arena, err); } /// @deprecated @@ -487,18 +487,18 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err) /// @warning May return nil if there was no previous value /// OR if previous value was `v:null`. /// @return Old value or nil if there was no previous value. -Object vim_set_var(String name, Object value, Error *err) +Object vim_set_var(String name, Object value, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { - return dict_set_var(&globvardict, name, value, false, true, err); + return dict_set_var(&globvardict, name, value, false, true, arena, err); } /// @deprecated /// @see nvim_del_var -Object vim_del_var(String name, Error *err) +Object vim_del_var(String name, Arena *arena, Error *err) FUNC_API_DEPRECATED_SINCE(1) { - return dict_set_var(&globvardict, name, NIL, true, true, err); + return dict_set_var(&globvardict, name, NIL, true, true, arena, err); } static int64_t convert_index(int64_t index) diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index e7b8934c97..a70ef1e50b 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -19,6 +19,8 @@ /// Helper structure for vim_to_object typedef struct { kvec_withinit_t(Object, 2) stack; ///< Object stack. + Arena *arena; ///< arena where objects will be allocated + bool reuse_strdata; } EncodedData; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -41,12 +43,21 @@ typedef struct { #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ kvi_push(edata->stack, FLOAT_OBJ((Float)(flt))) +static Object typval_cbuf_to_obj(EncodedData *edata, const char *data, size_t len) +{ + if (edata->reuse_strdata) { + return STRING_OBJ(cbuf_as_string((char *)(len ? data : ""), len)); + } else { + return CBUF_TO_ARENA_OBJ(edata->arena, data, len); + } +} + #define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ do { \ const size_t len_ = (size_t)(len); \ const char *const str_ = (str); \ assert(len_ == 0 || str_ != NULL); \ - kvi_push(edata->stack, STRING_OBJ(cbuf_to_string((len_ ? str_ : ""), len_))); \ + kvi_push(edata->stack, typval_cbuf_to_obj(edata, str_, len_)); \ } while (0) #define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING @@ -58,10 +69,7 @@ typedef struct { do { \ const size_t len_ = (size_t)(len); \ const blob_T *const blob_ = (blob); \ - kvi_push(edata->stack, STRING_OBJ(((String) { \ - .data = len_ != 0 ? xmemdupz(blob_->bv_ga.ga_data, len_) : xstrdup(""), \ - .size = len_ \ - }))); \ + kvi_push(edata->stack, typval_cbuf_to_obj(edata, len_ ? blob_->bv_ga.ga_data : "", len_)); \ } while (0) #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ @@ -90,11 +98,7 @@ typedef struct { static inline void typval_encode_list_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - kvi_push(edata->stack, ARRAY_OBJ(((Array) { - .capacity = len, - .size = 0, - .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.array.items)), - }))); + kvi_push(edata->stack, ARRAY_OBJ(arena_array(edata->arena, len))); } #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ @@ -109,7 +113,7 @@ static inline void typval_encode_between_list_items(EncodedData *const edata) Object *const list = &kv_last(edata->stack); assert(list->type == kObjectTypeArray); assert(list->data.array.size < list->data.array.capacity); - list->data.array.items[list->data.array.size++] = item; + ADD_C(list->data.array, item); } #define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \ @@ -131,11 +135,7 @@ static inline void typval_encode_list_end(EncodedData *const edata) static inline void typval_encode_dict_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - kvi_push(edata->stack, DICTIONARY_OBJ(((Dictionary) { - .capacity = len, - .size = 0, - .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.dictionary.items)), - }))); + kvi_push(edata->stack, DICTIONARY_OBJ(arena_dict(edata->arena, len))); } #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ @@ -156,9 +156,8 @@ static inline void typval_encode_after_key(EncodedData *const edata) dict->data.dictionary.items[dict->data.dictionary.size].key = key.data.string; } else { - api_free_object(key); dict->data.dictionary.items[dict->data.dictionary.size].key - = STATIC_CSTR_TO_STRING("__INVALID_KEY__"); + = STATIC_CSTR_AS_STRING("__INVALID_KEY__"); } } @@ -233,17 +232,22 @@ static inline void typval_encode_dict_end(EncodedData *const edata) #undef TYPVAL_ENCODE_CONV_RECURSE #undef TYPVAL_ENCODE_ALLOW_SPECIALS -/// Convert a vim object to an `Object` instance, recursively expanding +/// Convert a vim object to an `Object` instance, recursively converting /// Arrays/Dictionaries. /// /// @param obj The source object +/// @param arena if NULL, use direct allocation +/// @param reuse_strdata when true, don't copy string data to Arena but reference +/// typval strings directly. takes no effect when arena is +/// NULL /// @return The converted value -Object vim_to_object(typval_T *obj) +Object vim_to_object(typval_T *obj, Arena *arena, bool reuse_strdata) { EncodedData edata; kvi_init(edata.stack); - const int evo_ret = encode_vim_to_object(&edata, obj, - "vim_to_object argument"); + edata.arena = arena; + edata.reuse_strdata = reuse_strdata; + const int evo_ret = encode_vim_to_object(&edata, obj, "vim_to_object argument"); (void)evo_ret; assert(evo_ret == OK); Object ret = kv_A(edata.stack, 0); @@ -260,10 +264,18 @@ Object vim_to_object(typval_T *obj) /// @param err Error object. void object_to_vim(Object obj, typval_T *tv, Error *err) { + object_to_vim_take_luaref(&obj, tv, false, err); +} + +/// same as object_to_vim but consumes all luarefs (nested) in `obj` +/// +/// useful when `obj` is allocated on an arena +void object_to_vim_take_luaref(Object *obj, typval_T *tv, bool take_luaref, Error *err) +{ tv->v_type = VAR_UNKNOWN; tv->v_lock = VAR_UNLOCKED; - switch (obj.type) { + switch (obj->type) { case kObjectTypeNil: tv->v_type = VAR_SPECIAL; tv->vval.v_special = kSpecialVarNull; @@ -271,41 +283,40 @@ void object_to_vim(Object obj, typval_T *tv, Error *err) case kObjectTypeBoolean: tv->v_type = VAR_BOOL; - tv->vval.v_bool = obj.data.boolean ? kBoolVarTrue : kBoolVarFalse; + tv->vval.v_bool = obj->data.boolean ? kBoolVarTrue : kBoolVarFalse; break; case kObjectTypeBuffer: case kObjectTypeWindow: case kObjectTypeTabpage: case kObjectTypeInteger: - STATIC_ASSERT(sizeof(obj.data.integer) <= sizeof(varnumber_T), + STATIC_ASSERT(sizeof(obj->data.integer) <= sizeof(varnumber_T), "Integer size must be <= Vimscript number size"); tv->v_type = VAR_NUMBER; - tv->vval.v_number = (varnumber_T)obj.data.integer; + tv->vval.v_number = (varnumber_T)obj->data.integer; break; case kObjectTypeFloat: tv->v_type = VAR_FLOAT; - tv->vval.v_float = obj.data.floating; + tv->vval.v_float = obj->data.floating; break; case kObjectTypeString: tv->v_type = VAR_STRING; - if (obj.data.string.data == NULL) { + if (obj->data.string.data == NULL) { tv->vval.v_string = NULL; } else { - tv->vval.v_string = xmemdupz(obj.data.string.data, - obj.data.string.size); + tv->vval.v_string = xmemdupz(obj->data.string.data, + obj->data.string.size); } break; case kObjectTypeArray: { - list_T *const list = tv_list_alloc((ptrdiff_t)obj.data.array.size); + list_T *const list = tv_list_alloc((ptrdiff_t)obj->data.array.size); - for (uint32_t i = 0; i < obj.data.array.size; i++) { - Object item = obj.data.array.items[i]; + for (uint32_t i = 0; i < obj->data.array.size; i++) { typval_T li_tv; - object_to_vim(item, &li_tv, err); + object_to_vim_take_luaref(&obj->data.array.items[i], &li_tv, take_luaref, err); tv_list_append_owned_tv(list, li_tv); } tv_list_ref(list); @@ -318,11 +329,11 @@ void object_to_vim(Object obj, typval_T *tv, Error *err) case kObjectTypeDictionary: { dict_T *const dict = tv_dict_alloc(); - for (uint32_t i = 0; i < obj.data.dictionary.size; i++) { - KeyValuePair item = obj.data.dictionary.items[i]; - String key = item.key; + for (uint32_t i = 0; i < obj->data.dictionary.size; i++) { + KeyValuePair *item = &obj->data.dictionary.items[i]; + String key = item->key; dictitem_T *const di = tv_dict_item_alloc(key.data); - object_to_vim(item.value, &di->di_tv, err); + object_to_vim_take_luaref(&item->value, &di->di_tv, take_luaref, err); tv_dict_add(dict, di); } dict->dv_refcount++; @@ -333,7 +344,13 @@ void object_to_vim(Object obj, typval_T *tv, Error *err) } case kObjectTypeLuaRef: { - char *name = register_luafunc(api_new_luaref(obj.data.luaref)); + LuaRef ref = obj->data.luaref; + if (take_luaref) { + obj->data.luaref = LUA_NOREF; + } else { + ref = api_new_luaref(ref); + } + char *name = register_luafunc(ref); tv->v_type = VAR_FUNC; tv->vval.v_string = xstrdup(name); break; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7bf0d87603..1446683b0c 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -175,7 +175,7 @@ bool try_end(Error *err) /// @param dict The vimscript dict /// @param key The key /// @param[out] err Details of an error that may have occurred -Object dict_get_value(dict_T *dict, String key, Error *err) +Object dict_get_value(dict_T *dict, String key, Arena *arena, Error *err) { dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); @@ -184,7 +184,7 @@ Object dict_get_value(dict_T *dict, String key, Error *err) return (Object)OBJECT_INIT; } - return vim_to_object(&di->di_tv); + return vim_to_object(&di->di_tv, arena, true); } dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err) @@ -221,7 +221,8 @@ dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err) /// @param retval If true the old value will be converted and returned. /// @param[out] err Details of an error that may have occurred /// @return The old value if `retval` is true and the key was present, else NIL -Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retval, Error *err) +Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retval, Arena *arena, + Error *err) { Object rv = OBJECT_INIT; dictitem_T *di = dict_check_writable(dict, key, del, err); @@ -244,7 +245,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva } // Return the old value if (retval) { - rv = vim_to_object(&di->di_tv); + rv = vim_to_object(&di->di_tv, arena, false); } // Delete the entry tv_dict_item_remove(dict, di); @@ -265,7 +266,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva } else { // Return the old value if (retval) { - rv = vim_to_object(&di->di_tv); + rv = vim_to_object(&di->di_tv, arena, false); } bool type_error = false; if (dict == &vimvardict diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 11abb8f801..395c5a9d1f 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -35,6 +35,7 @@ #define CSTR_TO_ARENA_STR(arena, s) arena_string(arena, cstr_as_string(s)) #define CSTR_TO_ARENA_OBJ(arena, s) STRING_OBJ(CSTR_TO_ARENA_STR(arena, s)) #define CBUF_TO_ARENA_STR(arena, s, len) arena_string(arena, cbuf_as_string((char *)(s), len)) +#define CBUF_TO_ARENA_OBJ(arena, s, len) STRING_OBJ(CBUF_TO_ARENA_STR(arena, s, len)) #define BUFFER_OBJ(s) ((Object) { \ .type = kObjectTypeBuffer, \ diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index fadc03b3e5..109075df8e 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -49,7 +49,7 @@ ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err) +Object nvim_tabpage_get_var(Tabpage tabpage, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -58,7 +58,7 @@ Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err) return (Object)OBJECT_INIT; } - return dict_get_value(tab->tp_vars, name, err); + return dict_get_value(tab->tp_vars, name, arena, err); } /// Sets a tab-scoped (t:) variable @@ -76,7 +76,7 @@ void nvim_tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err return; } - dict_set_var(tab->tp_vars, name, value, false, false, err); + dict_set_var(tab->tp_vars, name, value, false, false, NULL, err); } /// Removes a tab-scoped (t:) variable @@ -93,7 +93,7 @@ void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err) return; } - dict_set_var(tab->tp_vars, name, NIL, true, false, err); + dict_set_var(tab->tp_vars, name, NIL, true, false, NULL, err); } /// Gets the current window in a tabpage diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index c7280253c2..140b520cb5 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -1029,12 +1029,12 @@ static Array translate_contents(UI *ui, Array contents, Arena *arena) if (attr) { Dictionary rgb_attrs = arena_dict(arena, HLATTRS_DICT_SIZE); hlattrs2dict(&rgb_attrs, NULL, syn_attr2entry(attr), ui->rgb, false); - ADD(new_item, DICTIONARY_OBJ(rgb_attrs)); + ADD_C(new_item, DICTIONARY_OBJ(rgb_attrs)); } else { - ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + ADD_C(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); } - ADD(new_item, item.items[1]); - ADD(new_contents, ARRAY_OBJ(new_item)); + ADD_C(new_item, item.items[1]); + ADD_C(new_contents, ARRAY_OBJ(new_item)); } return new_contents; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 769537ac98..b7cb14867d 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -690,7 +690,7 @@ void nvim_del_current_line(Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_get_var(String name, Error *err) +Object nvim_get_var(String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { dictitem_T *di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size); @@ -704,7 +704,7 @@ Object nvim_get_var(String name, Error *err) VALIDATE((di != NULL), "Key not found: %s", name.data, { return (Object)OBJECT_INIT; }); - return vim_to_object(&di->di_tv); + return vim_to_object(&di->di_tv, arena, true); } /// Sets a global (g:) variable. @@ -715,7 +715,7 @@ Object nvim_get_var(String name, Error *err) void nvim_set_var(String name, Object value, Error *err) FUNC_API_SINCE(1) { - dict_set_var(&globvardict, name, value, false, false, err); + dict_set_var(&globvardict, name, value, false, false, NULL, err); } /// Removes a global (g:) variable. @@ -725,7 +725,7 @@ void nvim_set_var(String name, Object value, Error *err) void nvim_del_var(String name, Error *err) FUNC_API_SINCE(1) { - dict_set_var(&globvardict, name, NIL, true, false, err); + dict_set_var(&globvardict, name, NIL, true, false, NULL, err); } /// Gets a v: variable. @@ -733,10 +733,10 @@ void nvim_del_var(String name, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_get_vvar(String name, Error *err) +Object nvim_get_vvar(String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { - return dict_get_value(&vimvardict, name, err); + return dict_get_value(&vimvardict, name, arena, err); } /// Sets a v: variable, if it is not readonly. @@ -747,7 +747,7 @@ Object nvim_get_vvar(String name, Error *err) void nvim_set_vvar(String name, Object value, Error *err) FUNC_API_SINCE(6) { - dict_set_var(&vimvardict, name, value, false, false, err); + dict_set_var(&vimvardict, name, value, false, false, NULL, err); } /// Echo a message. @@ -1370,7 +1370,7 @@ Dictionary nvim_get_color_map(Arena *arena) /// @param[out] err Error details, if any /// /// @return map of global |context|. -Dictionary nvim_get_context(Dict(context) *opts, Error *err) +Dictionary nvim_get_context(Dict(context) *opts, Arena *arena, Error *err) FUNC_API_SINCE(6) { Array types = ARRAY_DICT_INIT; @@ -1406,7 +1406,7 @@ Dictionary nvim_get_context(Dict(context) *opts, Error *err) Context ctx = CONTEXT_INIT; ctx_save(&ctx, int_types); - Dictionary dict = ctx_to_dict(&ctx); + Dictionary dict = ctx_to_dict(&ctx, arena); ctx_free(&ctx); return dict; } @@ -2065,7 +2065,7 @@ Boolean nvim_del_mark(String name, Error *err) /// not set. /// @see |nvim_buf_set_mark()| /// @see |nvim_del_mark()| -Array nvim_get_mark(String name, Dict(empty) *opts, Error *err) +Array nvim_get_mark(String name, Dict(empty) *opts, Arena *arena, Error *err) FUNC_API_SINCE(8) { Array rv = ARRAY_DICT_INIT; @@ -2113,10 +2113,11 @@ Array nvim_get_mark(String name, Dict(empty) *opts, Error *err) col = pos.col; } - ADD(rv, INTEGER_OBJ(row)); - ADD(rv, INTEGER_OBJ(col)); - ADD(rv, INTEGER_OBJ(bufnr)); - ADD(rv, CSTR_TO_OBJ(filename)); + rv = arena_array(arena, 4); + ADD_C(rv, INTEGER_OBJ(row)); + ADD_C(rv, INTEGER_OBJ(col)); + ADD_C(rv, INTEGER_OBJ(bufnr)); + ADD_C(rv, CSTR_TO_ARENA_OBJ(arena, filename)); if (allocated) { xfree(filename); diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 56541bb8b8..949356acc6 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -148,7 +148,7 @@ void nvim_command(String command, Error *err) /// @param expr Vimscript expression string /// @param[out] err Error details, if any /// @return Evaluation result or expanded object -Object nvim_eval(String expr, Error *err) +Object nvim_eval(String expr, Arena *arena, Error *err) FUNC_API_SINCE(1) { static int recursive = 0; // recursion depth @@ -179,7 +179,7 @@ Object nvim_eval(String expr, Error *err) api_set_error(err, kErrorTypeException, "Failed to evaluate expression: '%.*s'", 256, expr.data); } else { - rv = vim_to_object(&rettv); + rv = vim_to_object(&rettv, arena, false); } } @@ -196,7 +196,7 @@ Object nvim_eval(String expr, Error *err) /// @param self `self` dict, or NULL for non-dict functions /// @param[out] err Error details, if any /// @return Result of the function call -static Object _call_function(String fn, Array args, dict_T *self, Error *err) +static Object _call_function(String fn, Array args, dict_T *self, Arena *arena, Error *err) { static int recursive = 0; // recursion depth Object rv = OBJECT_INIT; @@ -239,7 +239,7 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) }); if (!ERROR_SET(err)) { - rv = vim_to_object(&rettv); + rv = vim_to_object(&rettv, arena, false); } tv_clear(&rettv); @@ -260,10 +260,10 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err) /// @param args Function arguments packed in an Array /// @param[out] err Error details, if any /// @return Result of the function call -Object nvim_call_function(String fn, Array args, Error *err) +Object nvim_call_function(String fn, Array args, Arena *arena, Error *err) FUNC_API_SINCE(1) { - return _call_function(fn, args, NULL, err); + return _call_function(fn, args, NULL, arena, err); } /// Calls a Vimscript |Dictionary-function| with the given arguments. @@ -275,7 +275,7 @@ Object nvim_call_function(String fn, Array args, Error *err) /// @param args Function arguments packed in an Array /// @param[out] err Error details, if any /// @return Result of the function call -Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err) +Object nvim_call_dict_function(Object dict, String fn, Array args, Arena *arena, Error *err) FUNC_API_SINCE(4) { Object rv = OBJECT_INIT; @@ -337,7 +337,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err) goto end; } - rv = _call_function(fn, args, self_dict, err); + rv = _call_function(fn, args, self_dict, arena, err); end: if (mustfree) { tv_clear(&rettv); diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 93c9dfa049..ed1ad5b583 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -237,7 +237,7 @@ void nvim_win_set_width(Window window, Integer width, Error *err) /// @param name Variable name /// @param[out] err Error details, if any /// @return Variable value -Object nvim_win_get_var(Window window, String name, Error *err) +Object nvim_win_get_var(Window window, String name, Arena *arena, Error *err) FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -246,7 +246,7 @@ Object nvim_win_get_var(Window window, String name, Error *err) return (Object)OBJECT_INIT; } - return dict_get_value(win->w_vars, name, err); + return dict_get_value(win->w_vars, name, arena, err); } /// Sets a window-scoped (w:) variable @@ -264,7 +264,7 @@ void nvim_win_set_var(Window window, String name, Object value, Error *err) return; } - dict_set_var(win->w_vars, name, value, false, false, err); + dict_set_var(win->w_vars, name, value, false, false, NULL, err); } /// Removes a window-scoped (w:) variable @@ -281,7 +281,7 @@ void nvim_win_del_var(Window window, String name, Error *err) return; } - dict_set_var(win->w_vars, name, NIL, true, false, err); + dict_set_var(win->w_vars, name, NIL, true, false, NULL, err); } /// Gets the window position in display cells. First position is zero. @@ -289,15 +289,16 @@ void nvim_win_del_var(Window window, String name, Error *err) /// @param window Window handle, or 0 for current window /// @param[out] err Error details, if any /// @return (row, col) tuple with the window position -ArrayOf(Integer, 2) nvim_win_get_position(Window window, Error *err) +ArrayOf(Integer, 2) nvim_win_get_position(Window window, Arena *arena, Error *err) FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); if (win) { - ADD(rv, INTEGER_OBJ(win->w_winrow)); - ADD(rv, INTEGER_OBJ(win->w_wincol)); + rv = arena_array(arena, 2); + ADD_C(rv, INTEGER_OBJ(win->w_winrow)); + ADD_C(rv, INTEGER_OBJ(win->w_wincol)); } return rv; diff --git a/src/nvim/context.c b/src/nvim/context.c index 7ce906b244..95e2618f62 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -296,7 +296,7 @@ static inline void ctx_restore_funcs(Context *ctx) /// @param[in] sbuf msgpack_sbuffer to convert. /// /// @return readfile()-style array representation of "sbuf". -static inline Array sbuf_to_array(msgpack_sbuffer sbuf) +static inline Array sbuf_to_array(msgpack_sbuffer sbuf, Arena *arena) { list_T *const list = tv_list_alloc(kListLenMayKnow); tv_list_append_string(list, "", 0); @@ -310,7 +310,7 @@ static inline Array sbuf_to_array(msgpack_sbuffer sbuf) .vval.v_list = list }; - Array array = vim_to_object(&list_tv).data.array; + Array array = vim_to_object(&list_tv, arena, false).data.array; tv_clear(&list_tv); return array; } @@ -346,18 +346,18 @@ static inline msgpack_sbuffer array_to_sbuf(Array array, Error *err) /// @param[in] ctx Context to convert. /// /// @return Dictionary representing "ctx". -Dictionary ctx_to_dict(Context *ctx) +Dictionary ctx_to_dict(Context *ctx, Arena *arena) FUNC_ATTR_NONNULL_ALL { assert(ctx != NULL); - Dictionary rv = ARRAY_DICT_INIT; + Dictionary rv = arena_dict(arena, 5); - PUT(rv, "regs", ARRAY_OBJ(sbuf_to_array(ctx->regs))); - PUT(rv, "jumps", ARRAY_OBJ(sbuf_to_array(ctx->jumps))); - PUT(rv, "bufs", ARRAY_OBJ(sbuf_to_array(ctx->bufs))); - PUT(rv, "gvars", ARRAY_OBJ(sbuf_to_array(ctx->gvars))); - PUT(rv, "funcs", ARRAY_OBJ(copy_array(ctx->funcs, NULL))); + PUT_C(rv, "regs", ARRAY_OBJ(sbuf_to_array(ctx->regs, arena))); + PUT_C(rv, "jumps", ARRAY_OBJ(sbuf_to_array(ctx->jumps, arena))); + PUT_C(rv, "bufs", ARRAY_OBJ(sbuf_to_array(ctx->bufs, arena))); + PUT_C(rv, "gvars", ARRAY_OBJ(sbuf_to_array(ctx->gvars, arena))); + PUT_C(rv, "funcs", ARRAY_OBJ(copy_array(ctx->funcs, arena))); return rv; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a4da582dca..4734e46362 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1355,7 +1355,7 @@ Object eval_foldtext(win_T *wp) retval = STRING_OBJ(NULL_STRING); } else { if (tv.v_type == VAR_LIST) { - retval = vim_to_object(&tv); + retval = vim_to_object(&tv, NULL, false); } else { retval = STRING_OBJ(cstr_to_string(tv_get_string(&tv))); } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 726b9ce758..28365e16df 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -359,15 +359,15 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) MsgpackRpcRequestHandler handler = *fptr.api_handler; - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, false)); } Error err = ERROR_INIT; - Arena res_arena = ARENA_EMPTY; - Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err); + Object result = handler.fn(VIML_INTERNAL_CALL, args, &arena, &err); if (ERROR_SET(&err)) { semsg_multiline(e_api_error, err.msg); @@ -377,12 +377,10 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) object_to_vim(result, rettv, &err); end: - api_free_array(args); - if (handler.arena_return) { - arena_mem_free(arena_finish(&res_arena)); - } else { + if (!handler.arena_return) { api_free_object(result); } + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); } @@ -1037,10 +1035,11 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Dictionary ctx_dict = ctx_to_dict(ctx); + Arena arena = ARENA_EMPTY; + Dictionary ctx_dict = ctx_to_dict(ctx, &arena); Error err = ERROR_INIT; object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err); - api_free_dictionary(ctx_dict); + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); } @@ -1108,7 +1107,8 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const int save_did_emsg = did_emsg; did_emsg = false; - Dictionary dict = vim_to_object(&argvars[0]).data.dictionary; + Arena arena = ARENA_EMPTY; + Dictionary dict = vim_to_object(&argvars[0], &arena, true).data.dictionary; Context tmp = CONTEXT_INIT; Error err = ERROR_INIT; ctx_from_dict(dict, &tmp, &err); @@ -1121,7 +1121,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) *ctx = tmp; } - api_free_dictionary(dict); + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); did_emsg = save_did_emsg; } @@ -6883,16 +6883,17 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, true)); } bool ok = rpc_send_event((uint64_t)argvars[0].vval.v_number, tv_get_string(&argvars[1]), args); - api_free_array(args); + arena_mem_free(arena_finish(&arena)); if (!ok) { semsg(_(e_invarg2), "Channel doesn't exist"); @@ -6922,10 +6923,11 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, true)); } sctx_T save_current_sctx; @@ -6961,7 +6963,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) ArenaMem res_mem = NULL; Object result = rpc_send_call(chan_id, method, args, &res_mem, &err); - api_free_array(args); + arena_mem_free(arena_finish(&arena)); if (l_provider_call_nesting) { current_sctx = save_current_sctx; @@ -8868,10 +8870,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) Error err = ERROR_INIT; // deprecated: use 'channel' buffer option dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ((Integer)chan->id), false, false, &err); + INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err); api_clear_error(&err); dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), - INTEGER_OBJ(pid), false, false, &err); + INTEGER_OBJ(pid), false, false, NULL, &err); api_clear_error(&err); channel_incref(chan); diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index c16ea29a01..90ed90d2bc 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -845,15 +845,17 @@ exit_0: 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(string.format( [[ if (lua_gettop(lstate) == 0) { - nlua_push_%s(lstate, ret, true); + nlua_push_%s(lstate, %sret, true); } ]], - return_type + return_type, + ret_mode )) elseif string.match(ret_type, '^KeyDict_') then write_shifted_output( @@ -862,7 +864,12 @@ exit_0: ) else local special = (fn.since ~= nil and fn.since < 11) - write_shifted_output(' nlua_push_%s(lstate, ret, %s);\n', return_type, tostring(special)) + write_shifted_output( + ' nlua_push_%s(lstate, %sret, %s);\n', + return_type, + ret_mode, + tostring(special) + ) end -- NOTE: we currently assume err_throw needs nothing from arena diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 423d2fa775..0b7f58cec6 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -719,7 +719,7 @@ void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special } for (size_t i = 0; i < dict.size; i++) { nlua_push_String(lstate, dict.items[i].key, special); - nlua_push_Object(lstate, dict.items[i].value, special); + nlua_push_Object(lstate, &dict.items[i].value, special); lua_rawset(lstate, -3); } } @@ -732,7 +732,7 @@ void nlua_push_Array(lua_State *lstate, const Array array, bool special) { lua_createtable(lstate, (int)array.size, 0); for (size_t i = 0; i < array.size; i++) { - nlua_push_Object(lstate, array.items[i], special); + nlua_push_Object(lstate, &array.items[i], special); lua_rawseti(lstate, -2, (int)i + 1); } } @@ -753,10 +753,10 @@ GENERATE_INDEX_FUNCTION(Tabpage) /// Convert given Object to lua value /// /// Leaves converted value on top of the stack. -void nlua_push_Object(lua_State *lstate, const Object obj, bool special) +void nlua_push_Object(lua_State *lstate, Object *obj, bool special) FUNC_ATTR_NONNULL_ALL { - switch (obj.type) { + switch (obj->type) { case kObjectTypeNil: if (special) { lua_pushnil(lstate); @@ -765,12 +765,14 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) } break; case kObjectTypeLuaRef: { - nlua_pushref(lstate, obj.data.luaref); + nlua_pushref(lstate, obj->data.luaref); + api_free_luaref(obj->data.luaref); + obj->data.luaref = LUA_NOREF; break; } #define ADD_TYPE(type, data_key) \ case kObjectType##type: { \ - nlua_push_##type(lstate, obj.data.data_key, special); \ + nlua_push_##type(lstate, obj->data.data_key, special); \ break; \ } ADD_TYPE(Boolean, boolean) @@ -782,7 +784,7 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) #undef ADD_TYPE #define ADD_REMOTE_TYPE(type) \ case kObjectType##type: { \ - nlua_push_##type(lstate, (type)obj.data.integer, special); \ + nlua_push_##type(lstate, (type)obj->data.integer, special); \ break; \ } ADD_REMOTE_TYPE(Buffer) @@ -1378,7 +1380,7 @@ void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table) lua_pushstring(L, field->str); if (field->type == kObjectTypeNil) { - nlua_push_Object(L, *(Object *)mem, false); + nlua_push_Object(L, (Object *)mem, false); } else if (field->type == kObjectTypeInteger || field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) { lua_pushinteger(L, *(Integer *)mem); diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index be55bde202..db44288b16 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1260,7 +1260,7 @@ static int nlua_rpc(lua_State *lstate, bool request) ArenaMem res_mem = NULL; Object result = rpc_send_call(chan_id, name, args, &res_mem, &err); if (!ERROR_SET(&err)) { - nlua_push_Object(lstate, result, false); + nlua_push_Object(lstate, &result, false); arena_mem_free(res_mem); } } else { @@ -1563,7 +1563,7 @@ Object nlua_exec(const String str, const Array args, LuaRetMode mode, Arena *are } for (size_t i = 0; i < args.size; i++) { - nlua_push_Object(lstate, args.items[i], false); + nlua_push_Object(lstate, &args.items[i], false); } if (nlua_pcall(lstate, (int)args.size, 1)) { @@ -1610,7 +1610,7 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode, nargs++; } for (size_t i = 0; i < args.size; i++) { - nlua_push_Object(lstate, args.items[i], false); + nlua_push_Object(lstate, &args.items[i], false); } if (nlua_pcall(lstate, nargs, 1)) { diff --git a/src/nvim/main.c b/src/nvim/main.c index d712a45b81..1c4457ed9a 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1102,7 +1102,7 @@ static void command_line_scan(mparm_T *parmp) } Object md = DICTIONARY_OBJ(api_metadata()); - msgpack_rpc_from_object(md, p); + msgpack_rpc_from_object(&md, p); msgpack_packer_free(p); const int ff_ret = file_flush(&fp); diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 59022e8be6..963f65148d 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -22,6 +22,7 @@ #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" +#include "nvim/eval/userfunc.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_session.h" #include "nvim/garray.h" @@ -2305,13 +2306,13 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) LuaRef rhs_lua = LUA_NOREF; dictitem_T *callback_di = tv_dict_find(d, S_LEN("callback")); if (callback_di != NULL) { - Object callback_obj = vim_to_object(&callback_di->di_tv); - if (callback_obj.type == kObjectTypeLuaRef && callback_obj.data.luaref != LUA_NOREF) { - rhs_lua = callback_obj.data.luaref; - orig_rhs = ""; - callback_obj.data.luaref = LUA_NOREF; + if (callback_di->di_tv.v_type == VAR_FUNC) { + ufunc_T *fp = find_func(callback_di->di_tv.vval.v_string); + if (fp != NULL && (fp->uf_flags & FC_LUAREF)) { + rhs_lua = api_new_luaref(fp->uf_luaref); + orig_rhs = ""; + } } - api_free_object(callback_obj); } if (lhs == NULL || lhsraw == NULL || orig_rhs == NULL) { emsg(_(e_entries_missing_in_mapset_dict_argument)); diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 2224403fa2..b411854387 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -449,7 +449,7 @@ static void request_event(void **argv) e->type, e->request_id, &error, - result, + &result, &out_buffer)); } if (!handler.arena_return) { @@ -538,7 +538,7 @@ static void send_error(Channel *chan, MsgpackRpcRequestHandler handler, MessageT type, id, &e, - NIL, + &NIL, &out_buffer)); api_clear_error(&e); } @@ -669,7 +669,7 @@ static WBuffer *serialize_request(uint64_t channel_id, uint32_t request_id, cons } static WBuffer *serialize_response(uint64_t channel_id, MsgpackRpcRequestHandler handler, - MessageType type, uint32_t response_id, Error *err, Object arg, + MessageType type, uint32_t response_id, Error *err, Object *arg, msgpack_sbuffer *sbuffer) { msgpack_packer pac; diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 3162269df6..6caf50d319 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -10,6 +10,7 @@ #include "msgpack/pack.h" #include "nvim/api/private/helpers.h" #include "nvim/assert_defs.h" +#include "nvim/lua/executor.h" #include "nvim/memory.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/types_defs.h" @@ -309,34 +310,40 @@ static void msgpack_rpc_from_handle(ObjectType type, Integer o, msgpack_packer * } typedef struct { - const Object *aobj; + Object *aobj; bool container; size_t idx; } APIToMPObjectStackItem; /// Convert type used by Nvim API to msgpack type. /// +/// consumes (frees) any luaref inside `result`, even though they are not used +/// (just represented as NIL) +/// /// @param[in] result Object to convert. /// @param[out] res Structure that defines where conversion results are saved. /// /// @return true in case of success, false otherwise. -void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) +void msgpack_rpc_from_object(Object *result, msgpack_packer *const res) FUNC_ATTR_NONNULL_ARG(2) { kvec_withinit_t(APIToMPObjectStackItem, 2) stack = KV_INITIAL_VALUE; kvi_init(stack); - kvi_push(stack, ((APIToMPObjectStackItem) { &result, false, 0 })); + kvi_push(stack, ((APIToMPObjectStackItem) { result, false, 0 })); while (kv_size(stack)) { APIToMPObjectStackItem cur = kv_last(stack); STATIC_ASSERT(kObjectTypeWindow == kObjectTypeBuffer + 1 && kObjectTypeTabpage == kObjectTypeWindow + 1, "Buffer, window and tabpage enum items are in order"); switch (cur.aobj->type) { - case kObjectTypeNil: case kObjectTypeLuaRef: // TODO(bfredl): could also be an error. Though kObjectTypeLuaRef // should only appear when the caller has opted in to handle references, // see nlua_pop_Object. + api_free_luaref(cur.aobj->data.luaref); + cur.aobj->data.luaref = LUA_NOREF; + FALLTHROUGH; + case kObjectTypeNil: msgpack_pack_nil(res); break; case kObjectTypeBoolean: @@ -415,7 +422,7 @@ void msgpack_rpc_from_array(Array result, msgpack_packer *res) msgpack_pack_array(res, result.size); for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_object(result.items[i], res); + msgpack_rpc_from_object(&result.items[i], res); } } @@ -426,7 +433,7 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) for (size_t i = 0; i < result.size; i++) { msgpack_rpc_from_string(result.items[i].key, res); - msgpack_rpc_from_object(result.items[i].value, res); + msgpack_rpc_from_object(&result.items[i].value, res); } } @@ -447,9 +454,9 @@ void msgpack_rpc_serialize_request(uint32_t request_id, const String method, Arr } /// Serializes a msgpack-rpc response -void msgpack_rpc_serialize_response(uint32_t response_id, Error *err, Object arg, +void msgpack_rpc_serialize_response(uint32_t response_id, Error *err, Object *arg, msgpack_packer *pac) - FUNC_ATTR_NONNULL_ARG(2, 4) + FUNC_ATTR_NONNULL_ALL { msgpack_pack_array(pac, 4); msgpack_pack_int(pac, 1); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 499f31454e..5e023c10d1 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -343,7 +343,6 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) if (name) { int dummy; RgbValue color_val = name_to_color(name, &dummy); - xfree(name); if (color_val != -1) { VTermColor color; @@ -1003,6 +1002,7 @@ static void buf_set_term_title(buf_T *buf, const char *title, size_t len) STRING_OBJ(((String){ .data = (char *)title, .size = len })), false, false, + NULL, &err); api_clear_error(&err); status_redraw_buf(buf); @@ -1883,10 +1883,10 @@ static char *get_config_string(char *key) { Error err = ERROR_INIT; // Only called from terminal_open where curbuf->terminal is the context. - Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), &err); + Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), NULL, &err); api_clear_error(&err); if (obj.type == kObjectTypeNil) { - obj = dict_get_value(&globvardict, cstr_as_string(key), &err); + obj = dict_get_value(&globvardict, cstr_as_string(key), NULL, &err); api_clear_error(&err); } if (obj.type == kObjectTypeString) { diff --git a/test/unit/api/private_helpers_spec.lua b/test/unit/api/private_helpers_spec.lua index 80ca9ddc5b..9843bd5c9e 100644 --- a/test/unit/api/private_helpers_spec.lua +++ b/test/unit/api/private_helpers_spec.lua @@ -22,7 +22,7 @@ local api = cimport('./src/nvim/api/private/helpers.h', './src/nvim/api/private/ describe('vim_to_object', function() local vim_to_object = function(l) - return obj2lua(api.vim_to_object(lua2typvalt(l))) + return obj2lua(api.vim_to_object(lua2typvalt(l), nil, false)) end local different_output_test = function(name, input, output) @@ -92,13 +92,13 @@ describe('vim_to_object', function() itp('outputs empty list for NULL list', function() local tt = typvalt('VAR_LIST', { v_list = NULL }) eq(nil, tt.vval.v_list) - eq({ [type_key] = list_type }, obj2lua(api.vim_to_object(tt))) + eq({ [type_key] = list_type }, obj2lua(api.vim_to_object(tt, nil, false))) end) itp('outputs empty dict for NULL dict', function() local tt = typvalt('VAR_DICT', { v_dict = NULL }) eq(nil, tt.vval.v_dict) - eq({}, obj2lua(api.vim_to_object(tt))) + eq({}, obj2lua(api.vim_to_object(tt, nil, false))) end) itp('regression: partials in a list', function() @@ -113,6 +113,6 @@ describe('vim_to_object', function() } local list = lua2typvalt(llist) eq(llist, typvalt2lua(list)) - eq({ nil_value, {} }, obj2lua(api.vim_to_object(list))) + eq({ nil_value, {} }, obj2lua(api.vim_to_object(list, nil, false))) end) end) |