#ifndef NVIM_EVAL_TYPVAL_H #define NVIM_EVAL_TYPVAL_H #include #include #include #include #include #include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/gettext.h" #include "nvim/hashtab.h" #include "nvim/lib/queue.h" #include "nvim/macros.h" #include "nvim/mbyte_defs.h" #include "nvim/message.h" #include "nvim/pos.h" // for linenr_T #include "nvim/types.h" #ifdef LOG_LIST_ACTIONS # include "nvim/memory.h" #endif /// Type used for VimL VAR_NUMBER values typedef int64_t varnumber_T; typedef uint64_t uvarnumber_T; /// Type used for VimL VAR_FLOAT values typedef double float_T; /// Refcount for dict or list that should not be freed enum { DO_NOT_FREE_CNT = (INT_MAX / 2), }; /// Additional values for tv_list_alloc() len argument enum ListLenSpecials { /// List length is not known in advance /// /// To be used when there is neither a way to know how many elements will be /// needed nor are any educated guesses. kListLenUnknown = -1, /// List length *should* be known, but is actually not /// /// All occurrences of this value should be eventually removed. This is for /// the case when the only reason why list length is not known is that it /// would be hard to code without refactoring, but refactoring is needed. kListLenShouldKnow = -2, /// List length may be known in advance, but it requires too much effort /// /// To be used when it looks impractical to determine list length. kListLenMayKnow = -3, }; /// Maximal possible value of varnumber_T variable #define VARNUMBER_MAX INT64_MAX #define UVARNUMBER_MAX UINT64_MAX /// Minimal possible value of varnumber_T variable #define VARNUMBER_MIN INT64_MIN /// %d printf format specifier for varnumber_T #define PRIdVARNUMBER PRId64 typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; typedef struct partial_S partial_T; typedef struct blobvar_S blob_T; typedef struct ufunc ufunc_T; typedef enum { kCallbackNone = 0, kCallbackFuncref, kCallbackPartial, kCallbackLua, } CallbackType; typedef struct { union { char *funcref; partial_T *partial; LuaRef luaref; } data; CallbackType type; } Callback; #define CALLBACK_INIT { .type = kCallbackNone } #define CALLBACK_NONE ((Callback)CALLBACK_INIT) /// Structure holding dictionary watcher typedef struct dict_watcher { Callback callback; char *key_pattern; size_t key_pattern_len; QUEUE node; bool busy; // prevent recursion if the dict is changed in the callback bool needs_free; } DictWatcher; /// Bool variable values typedef enum { kBoolVarFalse, ///< v:false kBoolVarTrue, ///< v:true } BoolVarValue; /// Special variable values typedef enum { kSpecialVarNull, ///< v:null } SpecialVarValue; /// Variable lock status for typval_T.v_lock typedef enum { VAR_UNLOCKED = 0, ///< Not locked. VAR_LOCKED = 1, ///< User lock, can be unlocked. VAR_FIXED = 2, ///< Locked forever. } VarLockStatus; /// VimL variable types, for use in typval_T.v_type typedef enum { VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. VAR_NUMBER, ///< Number, .v_number is used. VAR_STRING, ///< String, .v_string is used. VAR_FUNC, ///< Function reference, .v_string is used as function name. VAR_LIST, ///< List, .v_list is used. VAR_DICT, ///< Dictionary, .v_dict is used. VAR_FLOAT, ///< Floating-point value, .v_float is used. VAR_BOOL, ///< true, false VAR_SPECIAL, ///< Special value (null), .v_special ///< is used. VAR_PARTIAL, ///< Partial, .v_partial is used. VAR_BLOB, ///< Blob, .v_blob is used. } VarType; /// Structure that holds an internal variable value typedef struct { VarType v_type; ///< Variable type. VarLockStatus v_lock; ///< Variable lock status. union typval_vval_union { varnumber_T v_number; ///< Number, for VAR_NUMBER. BoolVarValue v_bool; ///< Bool value, for VAR_BOOL SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. float_T v_float; ///< Floating-point number, for VAR_FLOAT. char *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. list_T *v_list; ///< List for VAR_LIST, can be NULL. dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. partial_T *v_partial; ///< Closure: function with args. blob_T *v_blob; ///< Blob for VAR_BLOB, can be NULL. } vval; ///< Actual value. } typval_T; /// Values for (struct dictvar_S).dv_scope typedef enum { VAR_NO_SCOPE = 0, ///< Not a scope dictionary. VAR_SCOPE = 1, ///< Scope dictionary which requires prefix (a:, v:, …). VAR_DEF_SCOPE = 2, ///< Scope dictionary which may be accessed without prefix ///< (l:, g:). } ScopeType; /// Structure to hold an item of a list typedef struct listitem_S listitem_T; struct listitem_S { listitem_T *li_next; ///< Next item in list. listitem_T *li_prev; ///< Previous item in list. typval_T li_tv; ///< Item value. }; /// Structure used by those that are using an item in a list typedef struct listwatch_S listwatch_T; struct listwatch_S { listitem_T *lw_item; ///< Item being watched. listwatch_T *lw_next; ///< Next watcher. }; /// Structure to hold info about a list /// Order of members is optimized to reduce padding. struct listvar_S { listitem_T *lv_first; ///< First item, NULL if none. listitem_T *lv_last; ///< Last item, NULL if none. listwatch_T *lv_watch; ///< First watcher, NULL if none. listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx". list_T *lv_copylist; ///< Copied list used by deepcopy(). list_T *lv_used_next; ///< next list in used lists list. list_T *lv_used_prev; ///< Previous list in used lists list. int lv_refcount; ///< Reference count. int lv_len; ///< Number of items. int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx]. int lv_copyID; ///< ID used by deepcopy(). VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED. LuaRef lua_table_ref; }; // Static list with 10 items. Use tv_list_init_static10() to initialize. typedef struct { list_T sl_list; // must be first listitem_T sl_items[10]; } staticList10_T; #define TV_LIST_STATIC10_INIT { \ .sl_list = { \ .lv_first = NULL, \ .lv_last = NULL, \ .lv_refcount = 0, \ .lv_len = 0, \ .lv_watch = NULL, \ .lv_idx_item = NULL, \ .lv_lock = VAR_FIXED, \ .lv_used_next = NULL, \ .lv_used_prev = NULL, \ }, \ } #define TV_DICTITEM_STRUCT(...) \ struct { \ typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ uint8_t di_flags; /* Flags. */ \ char_u di_key[__VA_ARGS__]; /* Key value. */ \ } /// Structure to hold a scope dictionary /// /// @warning Must be compatible with dictitem_T. /// /// For use in find_var_in_ht to pretend that it found dictionary item when it /// finds scope dictionary. typedef TV_DICTITEM_STRUCT(1) ScopeDictDictItem; /// Structure to hold an item of a Dictionary /// /// @warning Must be compatible with ScopeDictDictItem. /// /// Also used for a variable. typedef TV_DICTITEM_STRUCT() dictitem_T; /// Flags for dictitem_T.di_flags typedef enum { DI_FLAGS_RO = 1, ///< Read-only value DI_FLAGS_RO_SBX = 2, ///< Value, read-only in the sandbox DI_FLAGS_FIX = 4, ///< Fixed value: cannot be :unlet or remove()d. DI_FLAGS_LOCK = 8, ///< Locked value. DI_FLAGS_ALLOC = 16, ///< Separately allocated. } DictItemFlags; /// Structure representing a Dictionary struct dictvar_S { VarLockStatus dv_lock; ///< Whole dictionary lock status. ScopeType dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if ///< dictionary represents a scope (i.e. g:, l: …). int dv_refcount; ///< Reference count. int dv_copyID; ///< ID used when recursivery traversing a value. hashtab_T dv_hashtab; ///< Hashtab containing all items. dict_T *dv_copydict; ///< Copied dict used by deepcopy(). dict_T *dv_used_next; ///< Next dictionary in used dictionaries list. dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list. QUEUE watchers; ///< Dictionary key watchers set by user code. LuaRef lua_table_ref; }; /// Structure to hold info about a Blob struct blobvar_S { garray_T bv_ga; ///< Growarray with the data. int bv_refcount; ///< Reference count. VarLockStatus bv_lock; ///< VAR_UNLOCKED, VAR_LOCKED, VAR_FIXED. }; /// Type used for script ID typedef int scid_T; /// Format argument for scid_T #define PRIdSCID "d" // SCript ConteXt (SCTX): identifies a script line. // When sourcing a script "sc_lnum" is zero, "sourcing_lnum" is the current // line number. When executing a user function "sc_lnum" is the line where the // function was defined, "sourcing_lnum" is the line number inside the // function. When stored with a function, mapping, option, etc. "sc_lnum" is // the line number in the script "sc_sid". typedef struct { scid_T sc_sid; // script ID int sc_seq; // sourcing sequence number linenr_T sc_lnum; // line number } sctx_T; /// Maximum number of function arguments #define MAX_FUNC_ARGS 20 /// Short variable name length #define VAR_SHORT_LEN 20 /// Number of fixed variables used for arguments #define FIXVAR_CNT 12 /// Callback interface for C function reference> /// Used for managing functions that were registered with |register_cfunc| typedef int (*cfunc_T)(int argcount, typval_T *argvars, typval_T *rettv, void *state); // NOLINT /// Callback to clear cfunc_T and any associated state. typedef void (*cfunc_free_T)(void *state); // Structure to hold info for a function that is currently being executed. typedef struct funccall_S funccall_T; struct funccall_S { ufunc_T *func; ///< Function being called. int linenr; ///< Next line to be executed. int returned; ///< ":return" used. /// Fixed variables for arguments. TV_DICTITEM_STRUCT(VAR_SHORT_LEN + 1) fixvar[FIXVAR_CNT]; dict_T l_vars; ///< l: local function variables. ScopeDictDictItem l_vars_var; ///< Variable for l: scope. dict_T l_avars; ///< a: argument variables. ScopeDictDictItem l_avars_var; ///< Variable for a: scope. list_T l_varlist; ///< List for a:000. listitem_T l_listitems[MAX_FUNC_ARGS]; ///< List items for a:000. typval_T *rettv; ///< Return value. linenr_T breakpoint; ///< Next line with breakpoint or zero. int dbg_tick; ///< Debug_tick when breakpoint was set. int level; ///< Top nesting level of executed function. proftime_T prof_child; ///< Time spent in a child. funccall_T *caller; ///< Calling function or NULL; or next funccal in ///< list pointed to by previous_funccal. int fc_refcount; ///< Number of user functions that reference this funccall. int fc_copyID; ///< CopyID used for garbage collection. garray_T fc_funcs; ///< List of ufunc_T* which keep a reference to "func". }; /// Structure to hold info for a user function. struct ufunc { int uf_varargs; ///< variable nr of arguments int uf_flags; int uf_calls; ///< nr of active calls bool uf_cleared; ///< func_clear() was already called garray_T uf_args; ///< arguments garray_T uf_def_args; ///< default argument expressions garray_T uf_lines; ///< function lines int uf_profiling; ///< true when func is being profiled int uf_prof_initialized; // Managing cfuncs cfunc_T uf_cb; ///< C function extension callback cfunc_free_T uf_cb_free; ///< C function extension free callback void *uf_cb_state; ///< State of C function extension. // Profiling the function as a whole. int uf_tm_count; ///< nr of calls proftime_T uf_tm_total; ///< time spent in function + children proftime_T uf_tm_self; ///< time spent in function itself proftime_T uf_tm_children; ///< time spent in children this call // Profiling the function per line. int *uf_tml_count; ///< nr of times line was executed proftime_T *uf_tml_total; ///< time spent in a line + children proftime_T *uf_tml_self; ///< time spent in a line itself proftime_T uf_tml_start; ///< start time for current line proftime_T uf_tml_children; ///< time spent in children for this line proftime_T uf_tml_wait; ///< start wait time for current line int uf_tml_idx; ///< index of line being timed; -1 if none int uf_tml_execed; ///< line being timed was executed sctx_T uf_script_ctx; ///< SCTX where function was defined, ///< used for s: variables int uf_refcount; ///< reference count, see func_name_refcount() funccall_T *uf_scoped; ///< l: local variables for closure char_u *uf_name_exp; ///< if "uf_name[]" starts with SNR the name with ///< "" as a string, otherwise NULL char_u uf_name[]; ///< Name of function (actual size equals name); ///< can start with 123_ ///< ( is K_SPECIAL KS_EXTRA KE_SNR) }; struct partial_S { int pt_refcount; ///< Reference count. char_u *pt_name; ///< Function name; when NULL use pt_func->name. ufunc_T *pt_func; ///< Function pointer; when NULL lookup function with ///< pt_name. bool pt_auto; ///< When true the partial was created by using dict.member ///< in handle_subscript(). int pt_argc; ///< Number of arguments. typval_T *pt_argv; ///< Arguments in allocated array. dict_T *pt_dict; ///< Dict for "self". }; /// Structure used for explicit stack while garbage collecting hash tables typedef struct ht_stack_S { hashtab_T *ht; struct ht_stack_S *prev; } ht_stack_T; /// Structure used for explicit stack while garbage collecting lists typedef struct list_stack_S { list_T *list; struct list_stack_S *prev; } list_stack_T; /// Structure representing one list item, used for sort array. typedef struct { listitem_T *item; ///< Sorted list item. int idx; ///< Sorted list item index. } ListSortItem; typedef int (*ListSorter)(const void *, const void *); #ifdef LOG_LIST_ACTIONS /// List actions log entry typedef struct { uintptr_t l; ///< List log entry belongs to. uintptr_t li1; ///< First list item log entry belongs to, if applicable. uintptr_t li2; ///< Second list item log entry belongs to, if applicable. int len; ///< List length when log entry was created. const char *action; ///< Logged action. } ListLogEntry; typedef struct list_log ListLog; /// List actions log struct list_log { ListLog *next; ///< Next chunk or NULL. size_t capacity; ///< Number of entries in current chunk. size_t size; ///< Current chunk size. ListLogEntry entries[]; ///< Actual log entries. }; extern ListLog *list_log_first; ///< First list log chunk, NULL if missing extern ListLog *list_log_last; ///< Last list log chunk static inline ListLog *list_log_alloc(const size_t size) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT; /// Allocate a new log chunk and update globals /// /// @param[in] size Number of entries in a new chunk. /// /// @return [allocated] Newly allocated chunk. static inline ListLog *list_log_new(const size_t size) { ListLog *ret = xmalloc(offsetof(ListLog, entries) + size * sizeof(ret->entries[0])); ret->size = 0; ret->capacity = size; ret->next = NULL; if (list_log_first == NULL) { list_log_first = ret; } else { list_log_last->next = ret; } list_log_last = ret; return ret; } static inline void list_log(const list_T *const l, const listitem_T *const li1, const listitem_T *const li2, const char *const action) REAL_FATTR_ALWAYS_INLINE; /// Add new entry to log /// /// If last chunk was filled it uses twice as much memory to allocate the next /// chunk. /// /// @param[in] l List to which entry belongs. /// @param[in] li1 List item 1. /// @param[in] li2 List item 2, often used for integers and not list items. /// @param[in] action Logged action. static inline void list_log(const list_T *const l, const listitem_T *const li1, const listitem_T *const li2, const char *const action) { ListLog *tgt; if (list_log_first == NULL) { tgt = list_log_new(128); } else if (list_log_last->size == list_log_last->capacity) { tgt = list_log_new(list_log_last->capacity * 2); } else { tgt = list_log_last; } tgt->entries[tgt->size++] = (ListLogEntry) { .l = (uintptr_t)l, .li1 = (uintptr_t)li1, .li2 = (uintptr_t)li2, .len = (l == NULL ? 0 : l->lv_len), .action = action, }; } #else # define list_log(...) # define list_write_log(...) # define list_free_log() #endif // In a hashtab item "hi_key" points to "di_key" in a dictitem. // This avoids adding a pointer to the hashtab item. /// Convert a hashitem pointer to a dictitem pointer #define TV_DICT_HI2DI(hi) \ ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) static inline void tv_list_ref(list_T *const l) REAL_FATTR_ALWAYS_INLINE; /// Increase reference count for a given list /// /// Does nothing for NULL lists. /// /// @param[in,out] l List to modify. static inline void tv_list_ref(list_T *const l) { if (l == NULL) { return; } l->lv_refcount++; } static inline void tv_list_set_ret(typval_T *const tv, list_T *const l) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); /// Set a list as the return value. Increments the reference count. /// /// @param[out] tv Object to receive the list /// @param[in,out] l List to pass to the object static inline void tv_list_set_ret(typval_T *const tv, list_T *const l) { tv->v_type = VAR_LIST; tv->vval.v_list = l; tv_list_ref(l); } static inline VarLockStatus tv_list_locked(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get list lock status /// /// Returns VAR_FIXED for NULL lists. /// /// @param[in] l List to check. static inline VarLockStatus tv_list_locked(const list_T *const l) { if (l == NULL) { return VAR_FIXED; } return l->lv_lock; } /// Set list lock status /// /// May only “set” VAR_FIXED for NULL lists. /// /// @param[out] l List to modify. /// @param[in] lock New lock status. static inline void tv_list_set_lock(list_T *const l, const VarLockStatus lock) { if (l == NULL) { assert(lock == VAR_FIXED); return; } l->lv_lock = lock; } /// Set list copyID /// /// Does not expect NULL list, be careful. /// /// @param[out] l List to modify. /// @param[in] copyid New copyID. static inline void tv_list_set_copyid(list_T *const l, const int copyid) FUNC_ATTR_NONNULL_ALL { l->lv_copyID = copyid; } static inline int tv_list_len(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list /// /// @param[in] l List to check. static inline int tv_list_len(const list_T *const l) { list_log(l, NULL, NULL, "len"); if (l == NULL) { return 0; } return l->lv_len; } static inline int tv_list_copyid(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; /// Get list copyID /// /// Does not expect NULL list, be careful. /// /// @param[in] l List to check. static inline int tv_list_copyid(const list_T *const l) { return l->lv_copyID; } static inline list_T *tv_list_latest_copy(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; /// Get latest list copy /// /// Gets lv_copylist field assigned by tv_list_copy() earlier. /// /// Does not expect NULL list, be careful. /// /// @param[in] l List to check. static inline list_T *tv_list_latest_copy(const list_T *const l) { return l->lv_copylist; } static inline int tv_list_uidx(const list_T *const l, int n) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Normalize index: that is, return either -1 or non-negative index /// /// @param[in] l List to index. Used to get length. /// @param[in] n List index, possibly negative. /// /// @return -1 or list index in range [0, tv_list_len(l)). static inline int tv_list_uidx(const list_T *const l, int n) { // Negative index is relative to the end. if (n < 0) { n += tv_list_len(l); } // Check for index out of range. if (n < 0 || n >= tv_list_len(l)) { return -1; } return n; } static inline bool tv_list_has_watchers(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Check whether list has watchers /// /// E.g. is referenced by a :for loop. /// /// @param[in] l List to check. /// /// @return true if there are watchers, false otherwise. static inline bool tv_list_has_watchers(const list_T *const l) { return l && l->lv_watch; } static inline listitem_T *tv_list_first(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get first list item /// /// @param[in] l List to get item from. /// /// @return List item or NULL in case of an empty list. static inline listitem_T *tv_list_first(const list_T *const l) { if (l == NULL) { list_log(l, NULL, NULL, "first"); return NULL; } list_log(l, l->lv_first, NULL, "first"); return l->lv_first; } static inline listitem_T *tv_list_last(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get last list item /// /// @param[in] l List to get item from. /// /// @return List item or NULL in case of an empty list. static inline listitem_T *tv_list_last(const list_T *const l) { if (l == NULL) { list_log(l, NULL, NULL, "last"); return NULL; } list_log(l, l->lv_last, NULL, "last"); return l->lv_last; } static inline void tv_dict_set_ret(typval_T *const tv, dict_T *const d) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); /// Set a dictionary as the return value /// /// @param[out] tv Object to receive the dictionary /// @param[in,out] d Dictionary to pass to the object static inline void tv_dict_set_ret(typval_T *const tv, dict_T *const d) { tv->v_type = VAR_DICT; tv->vval.v_dict = d; if (d != NULL) { d->dv_refcount++; } } static inline long tv_dict_len(const dict_T *const d) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a Dictionary /// /// @param[in] d Dictionary to check. static inline long tv_dict_len(const dict_T *const d) { if (d == NULL) { return 0L; } return (long)d->dv_hashtab.ht_used; } static inline bool tv_dict_is_watched(const dict_T *const d) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Check if dictionary is watched /// /// @param[in] d Dictionary to check. /// /// @return true if there is at least one watcher. static inline bool tv_dict_is_watched(const dict_T *const d) { return d && !QUEUE_EMPTY(&d->watchers); } static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1); /// Set a blob as the return value. /// /// Increments the reference count. /// /// @param[out] tv Object to receive the blob. /// @param[in,out] b Blob to pass to the object. static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b) { tv->v_type = VAR_BLOB; tv->vval.v_blob = b; if (b != NULL) { b->bv_refcount++; } } static inline int tv_blob_len(const blob_T *const b) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the length of the data in the blob, in bytes. /// /// @param[in] b Blob to check. static inline int tv_blob_len(const blob_T *const b) { if (b == NULL) { return 0; } return b->bv_ga.ga_len; } static inline char_u tv_blob_get(const blob_T *const b, int idx) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; /// Get the byte at index `idx` in the blob. /// /// @param[in] b Blob to index. Cannot be NULL. /// @param[in] idx Index in a blob. Must be valid. /// /// @return Byte value at the given index. static inline char_u tv_blob_get(const blob_T *const b, int idx) { return ((char_u *)b->bv_ga.ga_data)[idx]; } static inline void tv_blob_set(blob_T *const b, int idx, char_u c) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; /// Store the byte `c` at index `idx` in the blob. /// /// @param[in] b Blob to index. Cannot be NULL. /// @param[in] idx Index in a blob. Must be valid. /// @param[in] c Value to store. static inline void tv_blob_set(blob_T *const b, int idx, char_u c) { ((char_u *)b->bv_ga.ga_data)[idx] = c; } /// Initialize VimL object /// /// Initializes to unlocked VAR_UNKNOWN object. /// /// @param[out] tv Object to initialize. static inline void tv_init(typval_T *const tv) { if (tv != NULL) { memset(tv, 0, sizeof(*tv)); } } #define TV_INITIAL_VALUE \ ((typval_T) { \ .v_type = VAR_UNKNOWN, \ .v_lock = VAR_UNLOCKED, \ }) /// Empty string /// /// Needed for hack which allows not allocating empty string and still not /// crashing when freeing it. extern const char *const tv_empty_string; /// Specifies that free_unref_items() function has (not) been entered extern bool tv_in_free_unref_items; /// Iterate over a list /// /// @param modifier Modifier: expected to be const or nothing, volatile should /// also work if you have any uses for the volatile list. /// @param[in] l List to iterate over. /// @param li Name of the variable with current listitem_T entry. /// @param code Cycle body. #define _TV_LIST_ITER_MOD(modifier, l, li, code) \ do { \ modifier list_T *const l_ = (l); \ list_log(l_, NULL, NULL, "iter" #modifier); \ if (l_ != NULL) { \ for (modifier listitem_T *li = l_->lv_first; \ li != NULL; li = li->li_next) { \ code \ } \ } \ } while (0) /// Iterate over a list /// /// To be used when you need to modify list or values you iterate over, use /// #TV_LIST_ITER_CONST if you don’t. /// /// @param[in] l List to iterate over. /// @param li Name of the variable with current listitem_T entry. /// @param code Cycle body. #define TV_LIST_ITER(l, li, code) \ _TV_LIST_ITER_MOD( , l, li, code) /// Iterate over a list /// /// To be used when you don’t need to modify list or values you iterate over, /// use #TV_LIST_ITER if you do. /// /// @param[in] l List to iterate over. /// @param li Name of the variable with current listitem_T entry. /// @param code Cycle body. #define TV_LIST_ITER_CONST(l, li, code) \ _TV_LIST_ITER_MOD(const, l, li, code) // Below macros are macros to avoid duplicating code for functionally identical // const and non-const function variants. /// Get typval_T out of list item /// /// @param[in] li List item to get typval_T from, must not be NULL. /// /// @return Pointer to typval_T. #define TV_LIST_ITEM_TV(li) (&(li)->li_tv) /// Get next list item given the current one /// /// @param[in] l List to get item from. /// @param[in] li List item to get typval_T from. /// /// @return Pointer to the next item or NULL. #define TV_LIST_ITEM_NEXT(l, li) ((li)->li_next) /// Get previous list item given the current one /// /// @param[in] l List to get item from. /// @param[in] li List item to get typval_T from. /// /// @return Pointer to the previous item or NULL. #define TV_LIST_ITEM_PREV(l, li) ((li)->li_prev) // List argument is not used currently, but it is a must for lists implemented // as a pair (size(in list), array) without terminator - basically for lists on // top of kvec. /// Iterate over a dictionary /// /// @param[in] d Dictionary to iterate over. /// @param di Name of the variable with current dictitem_T entry. /// @param code Cycle body. #define TV_DICT_ITER(d, di, code) \ HASHTAB_ITER(&(d)->dv_hashtab, di##hi_, { \ { \ dictitem_T *const di = TV_DICT_HI2DI(di##hi_); \ { \ code \ } \ } \ }) static inline bool tv_get_float_chk(const typval_T *const tv, float_T *const ret_f) REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; // FIXME circular dependency, cannot import message.h. bool semsg(const char *const fmt, ...); /// Get the float value /// /// Raises an error if object is not number or floating-point. /// /// @param[in] tv VimL object to get value from. /// @param[out] ret_f Location where resulting float is stored. /// /// @return true in case of success, false if tv is not a number or float. static inline bool tv_get_float_chk(const typval_T *const tv, float_T *const ret_f) { if (tv->v_type == VAR_FLOAT) { *ret_f = tv->vval.v_float; return true; } if (tv->v_type == VAR_NUMBER) { *ret_f = (float_T)tv->vval.v_number; return true; } semsg("%s", _("E808: Number or Float required")); return false; } static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE; /// Compute the `DictWatcher` address from a QUEUE node. /// /// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer /// arithmetic). static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) { return QUEUE_DATA(q, DictWatcher, node); } static inline bool tv_is_func(const typval_T tv) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST; /// Check whether given typval_T contains a function /// /// That is, whether it contains VAR_FUNC or VAR_PARTIAL. /// /// @param[in] tv Typval to check. /// /// @return True if it is a function or a partial, false otherwise. static inline bool tv_is_func(const typval_T tv) { return tv.v_type == VAR_FUNC || tv.v_type == VAR_PARTIAL; } /// Specify that argument needs to be translated /// /// Used for size_t length arguments to avoid calling gettext() and strlen() /// unless needed. #define TV_TRANSLATE (SIZE_MAX) /// Specify that argument is a NUL-terminated C string /// /// Used for size_t length arguments to avoid calling strlen() unless needed. #define TV_CSTRING (SIZE_MAX - 1) #ifdef UNIT_TESTING // Do not use enum constants, see commit message. EXTERN const size_t kTVCstring INIT(= TV_CSTRING); EXTERN const size_t kTVTranslate INIT(= TV_TRANSLATE); #endif #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif #endif // NVIM_EVAL_TYPVAL_H