neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

typval.h (12154B)


      1 #pragma once
      2 
      3 #include <assert.h>
      4 #include <stdbool.h>
      5 #include <stddef.h>
      6 #include <stdint.h>
      7 #include <string.h>
      8 
      9 #include "nvim/eval/typval_defs.h"  // IWYU pragma: keep
     10 #include "nvim/gettext_defs.h"
     11 #include "nvim/hashtab.h"
     12 #include "nvim/lib/queue_defs.h"
     13 #include "nvim/macros_defs.h"
     14 #include "nvim/mbyte_defs.h"  // IWYU pragma: keep
     15 #include "nvim/message.h"
     16 #include "nvim/types_defs.h"
     17 
     18 #include "eval/typval.h.inline.generated.h"
     19 
     20 // In a hashtab item "hi_key" points to "di_key" in a dictitem.
     21 // This avoids adding a pointer to the hashtab item.
     22 
     23 /// Convert a hashitem pointer to a dictitem pointer
     24 #define TV_DICT_HI2DI(hi) \
     25  ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key)))
     26 
     27 /// Increase reference count for a given list
     28 ///
     29 /// Does nothing for NULL lists.
     30 ///
     31 /// @param[in,out]  l  List to modify.
     32 static inline void tv_list_ref(list_T *const l)
     33  FUNC_ATTR_ALWAYS_INLINE
     34 {
     35  if (l == NULL) {
     36    return;
     37  }
     38  l->lv_refcount++;
     39 }
     40 
     41 /// Set a list as the return value.  Increments the reference count.
     42 ///
     43 /// @param[out]  tv  Object to receive the list
     44 /// @param[in,out]  l  List to pass to the object
     45 static inline void tv_list_set_ret(typval_T *const tv, list_T *const l)
     46  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1)
     47 {
     48  tv->v_type = VAR_LIST;
     49  tv->vval.v_list = l;
     50  tv_list_ref(l);
     51 }
     52 
     53 /// Get list lock status
     54 ///
     55 /// Returns VAR_FIXED for NULL lists.
     56 ///
     57 /// @param[in]  l  List to check.
     58 static inline VarLockStatus tv_list_locked(const list_T *const l)
     59  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
     60 {
     61  if (l == NULL) {
     62    return VAR_FIXED;
     63  }
     64  return l->lv_lock;
     65 }
     66 
     67 /// Set list lock status
     68 ///
     69 /// May only “set” VAR_FIXED for NULL lists.
     70 ///
     71 /// @param[out]  l  List to modify.
     72 /// @param[in]  lock  New lock status.
     73 static inline void tv_list_set_lock(list_T *const l, const VarLockStatus lock)
     74 {
     75  if (l == NULL) {
     76    assert(lock == VAR_FIXED);
     77    return;
     78  }
     79  l->lv_lock = lock;
     80 }
     81 
     82 /// Set list copyID
     83 ///
     84 /// Does not expect NULL list, be careful.
     85 ///
     86 /// @param[out]  l  List to modify.
     87 /// @param[in]  copyid  New copyID.
     88 static inline void tv_list_set_copyid(list_T *const l, const int copyid)
     89  FUNC_ATTR_NONNULL_ALL
     90 {
     91  l->lv_copyID = copyid;
     92 }
     93 
     94 /// Get the number of items in a list
     95 ///
     96 /// @param[in]  l  List to check.
     97 static inline int tv_list_len(const list_T *const l)
     98  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
     99 {
    100  if (l == NULL) {
    101    return 0;
    102  }
    103  return l->lv_len;
    104 }
    105 
    106 /// Get list copyID
    107 ///
    108 /// Does not expect NULL list, be careful.
    109 ///
    110 /// @param[in]  l  List to check.
    111 static inline int tv_list_copyid(const list_T *const l)
    112  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
    113 {
    114  return l->lv_copyID;
    115 }
    116 
    117 /// Get latest list copy
    118 ///
    119 /// Gets lv_copylist field assigned by tv_list_copy() earlier.
    120 ///
    121 /// Does not expect NULL list, be careful.
    122 ///
    123 /// @param[in]  l  List to check.
    124 static inline list_T *tv_list_latest_copy(const list_T *const l)
    125  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
    126 {
    127  return l->lv_copylist;
    128 }
    129 
    130 /// Normalize index: that is, return either -1 or non-negative index
    131 ///
    132 /// @param[in]  l  List to index. Used to get length.
    133 /// @param[in]  n  List index, possibly negative.
    134 ///
    135 /// @return -1 or list index in range [0, tv_list_len(l)).
    136 static inline int tv_list_uidx(const list_T *const l, int n)
    137  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    138 {
    139  // Negative index is relative to the end.
    140  if (n < 0) {
    141    n += tv_list_len(l);
    142  }
    143 
    144  // Check for index out of range.
    145  if (n < 0 || n >= tv_list_len(l)) {
    146    return -1;
    147  }
    148  return n;
    149 }
    150 
    151 /// Check whether list has watchers
    152 ///
    153 /// E.g. is referenced by a :for loop.
    154 ///
    155 /// @param[in]  l  List to check.
    156 ///
    157 /// @return true if there are watchers, false otherwise.
    158 static inline bool tv_list_has_watchers(const list_T *const l)
    159  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    160 {
    161  return l && l->lv_watch;
    162 }
    163 
    164 /// Get first list item
    165 ///
    166 /// @param[in]  l  List to get item from.
    167 ///
    168 /// @return List item or NULL in case of an empty list.
    169 static inline listitem_T *tv_list_first(const list_T *const l)
    170  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    171 {
    172  if (l == NULL) {
    173    return NULL;
    174  }
    175  return l->lv_first;
    176 }
    177 
    178 /// Get last list item
    179 ///
    180 /// @param[in]  l  List to get item from.
    181 ///
    182 /// @return List item or NULL in case of an empty list.
    183 static inline listitem_T *tv_list_last(const list_T *const l)
    184  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    185 {
    186  if (l == NULL) {
    187    return NULL;
    188  }
    189  return l->lv_last;
    190 }
    191 
    192 /// Set a dictionary as the return value
    193 ///
    194 /// @param[out]  tv  Object to receive the dictionary
    195 /// @param[in,out]  d  Dictionary to pass to the object
    196 static inline void tv_dict_set_ret(typval_T *const tv, dict_T *const d)
    197  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1)
    198 {
    199  tv->v_type = VAR_DICT;
    200  tv->vval.v_dict = d;
    201  if (d != NULL) {
    202    d->dv_refcount++;
    203  }
    204 }
    205 
    206 /// Get the number of items in a Dictionary
    207 ///
    208 /// @param[in]  d  Dictionary to check.
    209 static inline long tv_dict_len(const dict_T *const d)
    210  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    211 {
    212  if (d == NULL) {
    213    return 0;
    214  }
    215  return (long)d->dv_hashtab.ht_used;
    216 }
    217 
    218 /// Check if dictionary is watched
    219 ///
    220 /// @param[in]  d  Dictionary to check.
    221 ///
    222 /// @return true if there is at least one watcher.
    223 static inline bool tv_dict_is_watched(const dict_T *const d)
    224  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    225 {
    226  return d && !QUEUE_EMPTY(&d->watchers);
    227 }
    228 
    229 /// Set a blob as the return value.
    230 ///
    231 /// Increments the reference count.
    232 ///
    233 /// @param[out]  tv  Object to receive the blob.
    234 /// @param[in,out]  b  Blob to pass to the object.
    235 static inline void tv_blob_set_ret(typval_T *const tv, blob_T *const b)
    236  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1)
    237 {
    238  tv->v_type = VAR_BLOB;
    239  tv->vval.v_blob = b;
    240  if (b != NULL) {
    241    b->bv_refcount++;
    242  }
    243 }
    244 
    245 /// Get the length of the data in the blob, in bytes.
    246 ///
    247 /// @param[in]  b  Blob to check.
    248 static inline int tv_blob_len(const blob_T *const b)
    249  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    250 {
    251  if (b == NULL) {
    252    return 0;
    253  }
    254  return b->bv_ga.ga_len;
    255 }
    256 
    257 /// Get the byte at index `idx` in the blob.
    258 ///
    259 /// @param[in]  b  Blob to index. Cannot be NULL.
    260 /// @param[in]  idx  Index in a blob. Must be valid.
    261 ///
    262 /// @return Byte value at the given index.
    263 static inline uint8_t tv_blob_get(const blob_T *const b, int idx)
    264  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
    265 {
    266  return ((uint8_t *)b->bv_ga.ga_data)[idx];
    267 }
    268 
    269 /// Store the byte `c` at index `idx` in the blob.
    270 ///
    271 /// @param[in]  b  Blob to index. Cannot be NULL.
    272 /// @param[in]  idx  Index in a blob. Must be valid.
    273 /// @param[in]  c  Value to store.
    274 static inline void tv_blob_set(blob_T *const blob, int idx, uint8_t c)
    275  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
    276 {
    277  ((uint8_t *)blob->bv_ga.ga_data)[idx] = c;
    278 }
    279 
    280 /// Initialize Vimscript object
    281 ///
    282 /// Initializes to unlocked VAR_UNKNOWN object.
    283 ///
    284 /// @param[out]  tv  Object to initialize.
    285 static inline void tv_init(typval_T *const tv)
    286 {
    287  if (tv != NULL) {
    288    memset(tv, 0, sizeof(*tv));
    289  }
    290 }
    291 
    292 /// Empty string
    293 ///
    294 /// Needed for hack which allows not allocating empty string and still not
    295 /// crashing when freeing it.
    296 extern const char *const tv_empty_string;
    297 
    298 /// Specifies that free_unref_items() function has (not) been entered
    299 extern bool tv_in_free_unref_items;
    300 
    301 /// Iterate over a list
    302 ///
    303 /// @param  modifier  Modifier: expected to be const or nothing, volatile should
    304 ///                   also work if you have any uses for the volatile list.
    305 /// @param[in]  l  List to iterate over.
    306 /// @param  li  Name of the variable with current listitem_T entry.
    307 /// @param  code  Cycle body.
    308 #define TV_LIST_ITER_MOD(modifier, l, li, code) \
    309  do { \
    310    modifier list_T *const l_ = (l); \
    311    if (l_ != NULL) { \
    312      for (modifier listitem_T *li = l_->lv_first; \
    313           li != NULL; li = li->li_next) { \
    314        code \
    315      } \
    316    } \
    317  } while (0)
    318 
    319 /// Iterate over a list
    320 ///
    321 /// To be used when you need to modify list or values you iterate over, use
    322 /// #TV_LIST_ITER_CONST if you don’t.
    323 ///
    324 /// @param[in]  l  List to iterate over.
    325 /// @param  li  Name of the variable with current listitem_T entry.
    326 /// @param  code  Cycle body.
    327 #define TV_LIST_ITER(l, li, code) \
    328  TV_LIST_ITER_MOD( , l, li, code)
    329 
    330 /// Iterate over a list
    331 ///
    332 /// To be used when you don’t need to modify list or values you iterate over,
    333 /// use #TV_LIST_ITER if you do.
    334 ///
    335 /// @param[in]  l  List to iterate over.
    336 /// @param  li  Name of the variable with current listitem_T entry.
    337 /// @param  code  Cycle body.
    338 #define TV_LIST_ITER_CONST(l, li, code) \
    339  TV_LIST_ITER_MOD(const, l, li, code)
    340 
    341 // Below macros are macros to avoid duplicating code for functionally identical
    342 // const and non-const function variants.
    343 
    344 /// Get typval_T out of list item
    345 ///
    346 /// @param[in]  li  List item to get typval_T from, must not be NULL.
    347 ///
    348 /// @return Pointer to typval_T.
    349 #define TV_LIST_ITEM_TV(li) (&(li)->li_tv)
    350 
    351 /// Get next list item given the current one
    352 ///
    353 /// @param[in]  l  List to get item from.
    354 /// @param[in]  li  List item to get typval_T from.
    355 ///
    356 /// @return Pointer to the next item or NULL.
    357 #define TV_LIST_ITEM_NEXT(l, li) ((li)->li_next)
    358 
    359 /// Get previous list item given the current one
    360 ///
    361 /// @param[in]  l  List to get item from.
    362 /// @param[in]  li  List item to get typval_T from.
    363 ///
    364 /// @return Pointer to the previous item or NULL.
    365 #define TV_LIST_ITEM_PREV(l, li) ((li)->li_prev)
    366 // List argument is not used currently, but it is a must for lists implemented
    367 // as a pair (size(in list), array) without terminator - basically for lists on
    368 // top of kvec.
    369 
    370 /// Iterate over a dictionary
    371 ///
    372 /// @param[in]  d  Dictionary to iterate over.
    373 /// @param  di  Name of the variable with current dictitem_T entry.
    374 /// @param  code  Cycle body.
    375 #define TV_DICT_ITER(d, di, code) \
    376  HASHTAB_ITER(&(d)->dv_hashtab, di##hi_, { \
    377    { \
    378      dictitem_T *const di = TV_DICT_HI2DI(di##hi_); \
    379      { \
    380        code \
    381      } \
    382    } \
    383  })
    384 
    385 /// Get the float value
    386 ///
    387 /// Raises an error if object is not number or floating-point.
    388 ///
    389 /// @param[in]  tv  Vimscript object to get value from.
    390 /// @param[out]  ret_f  Location where resulting float is stored.
    391 ///
    392 /// @return true in case of success, false if tv is not a number or float.
    393 static inline bool tv_get_float_chk(const typval_T *const tv, float_T *const ret_f)
    394  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
    395 {
    396  if (tv->v_type == VAR_FLOAT) {
    397    *ret_f = tv->vval.v_float;
    398    return true;
    399  }
    400  if (tv->v_type == VAR_NUMBER) {
    401    *ret_f = (float_T)tv->vval.v_number;
    402    return true;
    403  }
    404  semsg("%s", _("E808: Number or Float required"));
    405  return false;
    406 }
    407 
    408 /// Compute the `DictWatcher` address from a QUEUE node.
    409 ///
    410 /// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer
    411 /// arithmetic).
    412 static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q)
    413  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
    414    FUNC_ATTR_NO_SANITIZE_ADDRESS FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
    415 {
    416  return QUEUE_DATA(q, DictWatcher, node);
    417 }
    418 
    419 /// Check whether given typval_T contains a function
    420 ///
    421 /// That is, whether it contains VAR_FUNC or VAR_PARTIAL.
    422 ///
    423 /// @param[in]  tv  Typval to check.
    424 ///
    425 /// @return True if it is a function or a partial, false otherwise.
    426 static inline bool tv_is_func(const typval_T tv)
    427  FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST
    428 {
    429  return tv.v_type == VAR_FUNC || tv.v_type == VAR_PARTIAL;
    430 }
    431 
    432 /// Specify that argument needs to be translated
    433 ///
    434 /// Used for size_t length arguments to avoid calling gettext() and strlen()
    435 /// unless needed.
    436 #define TV_TRANSLATE (SIZE_MAX)
    437 
    438 /// Specify that argument is a NUL-terminated C string
    439 ///
    440 /// Used for size_t length arguments to avoid calling strlen() unless needed.
    441 #define TV_CSTRING (SIZE_MAX - 1)
    442 
    443 #ifdef UNIT_TESTING
    444 // Do not use enum constants, see commit message.
    445 EXTERN const size_t kTVCstring INIT( = TV_CSTRING);
    446 EXTERN const size_t kTVTranslate INIT( = TV_TRANSLATE);
    447 #endif
    448 
    449 #include "eval/typval.h.generated.h"