neovim

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

converter.c (11166B)


      1 #include <assert.h>
      2 #include <lauxlib.h>
      3 #include <stdbool.h>
      4 #include <stddef.h>
      5 #include <stdint.h>
      6 
      7 #include "klib/kvec.h"
      8 #include "nvim/api/private/converter.h"
      9 #include "nvim/api/private/defs.h"
     10 #include "nvim/api/private/helpers.h"
     11 #include "nvim/assert_defs.h"
     12 #include "nvim/eval/decode.h"
     13 #include "nvim/eval/typval.h"
     14 #include "nvim/eval/typval_defs.h"
     15 #include "nvim/eval/userfunc.h"
     16 #include "nvim/lua/executor.h"
     17 #include "nvim/memory.h"
     18 #include "nvim/memory_defs.h"
     19 #include "nvim/types_defs.h"
     20 #include "nvim/vim_defs.h"
     21 
     22 /// Helper structure for vim_to_object
     23 typedef struct {
     24  kvec_withinit_t(Object, 2) stack;  ///< Object stack.
     25  Arena *arena;  ///< arena where objects will be allocated
     26  bool reuse_strdata;
     27 } EncodedData;
     28 
     29 #include "api/private/converter.c.generated.h"
     30 
     31 #define TYPVAL_ENCODE_ALLOW_SPECIALS false
     32 #define TYPVAL_ENCODE_CHECK_BEFORE
     33 
     34 #define TYPVAL_ENCODE_CONV_NIL(tv) \
     35  kvi_push(edata->stack, NIL)
     36 
     37 #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
     38  kvi_push(edata->stack, BOOLEAN_OBJ((Boolean)(num)))
     39 
     40 #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
     41  kvi_push(edata->stack, INTEGER_OBJ((Integer)(num)))
     42 
     43 #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER
     44 
     45 #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
     46  kvi_push(edata->stack, FLOAT_OBJ((Float)(flt)))
     47 
     48 static Object typval_cbuf_to_obj(EncodedData *edata, const char *data, size_t len)
     49 {
     50  if (edata->reuse_strdata) {
     51    return STRING_OBJ(cbuf_as_string((char *)(len ? data : ""), len));
     52  } else {
     53    return CBUF_TO_ARENA_OBJ(edata->arena, data, len);
     54  }
     55 }
     56 
     57 #define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \
     58  do { \
     59    const size_t len_ = (size_t)(len); \
     60    const char *const str_ = (str); \
     61    assert(len_ == 0 || str_ != NULL); \
     62    kvi_push(edata->stack, typval_cbuf_to_obj(edata, str_, len_)); \
     63  } while (0)
     64 
     65 #define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING
     66 
     67 #define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
     68  TYPVAL_ENCODE_CONV_NIL(tv)
     69 
     70 #define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
     71  do { \
     72    const size_t len_ = (size_t)(len); \
     73    const blob_T *const blob_ = (blob); \
     74    kvi_push(edata->stack, typval_cbuf_to_obj(edata, len_ ? blob_->bv_ga.ga_data : "", len_)); \
     75  } while (0)
     76 
     77 #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix) \
     78  do { \
     79    const char *const fun_ = (fun); \
     80    ufunc_T *fp; \
     81    if (fun_ != NULL && (fp = find_func(fun_)) != NULL && fp->uf_flags & FC_LUAREF) { \
     82      kvi_push(edata->stack, LUAREF_OBJ(api_new_luaref(fp->uf_luaref))); \
     83    } else { \
     84      TYPVAL_ENCODE_CONV_NIL(tv); \
     85    } \
     86    goto typval_encode_stop_converting_one_item; \
     87  } while (0)
     88 
     89 #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
     90 #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
     91 #define TYPVAL_ENCODE_CONV_FUNC_END(tv)
     92 
     93 #define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
     94  kvi_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 })))
     95 
     96 #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
     97  kvi_push(edata->stack, DICT_OBJ(((Dict) { .capacity = 0, .size = 0 })))
     98 
     99 static inline void typval_encode_list_start(EncodedData *const edata, const size_t len)
    100  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
    101 {
    102  kvi_push(edata->stack, ARRAY_OBJ(arena_array(edata->arena, len)));
    103 }
    104 
    105 #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
    106  typval_encode_list_start(edata, (size_t)(len))
    107 
    108 #define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
    109 
    110 static inline void typval_encode_between_list_items(EncodedData *const edata)
    111  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
    112 {
    113  Object item = kv_pop(edata->stack);
    114  Object *const list = &kv_last(edata->stack);
    115  assert(list->type == kObjectTypeArray);
    116  assert(list->data.array.size < list->data.array.capacity);
    117  ADD_C(list->data.array, item);
    118 }
    119 
    120 #define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
    121  typval_encode_between_list_items(edata)
    122 
    123 static inline void typval_encode_list_end(EncodedData *const edata)
    124  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
    125 {
    126  typval_encode_between_list_items(edata);
    127 #ifndef NDEBUG
    128  const Object *const list = &kv_last(edata->stack);
    129  assert(list->data.array.size == list->data.array.capacity);
    130 #endif
    131 }
    132 
    133 #define TYPVAL_ENCODE_CONV_LIST_END(tv) \
    134  typval_encode_list_end(edata)
    135 
    136 static inline void typval_encode_dict_start(EncodedData *const edata, const size_t len)
    137  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
    138 {
    139  kvi_push(edata->stack, DICT_OBJ(arena_dict(edata->arena, len)));
    140 }
    141 
    142 #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
    143  typval_encode_dict_start(edata, (size_t)(len))
    144 
    145 #define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
    146 
    147 #define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair)
    148 
    149 static inline void typval_encode_after_key(EncodedData *const edata)
    150  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
    151 {
    152  Object key = kv_pop(edata->stack);
    153  Object *const dict = &kv_last(edata->stack);
    154  assert(dict->type == kObjectTypeDict);
    155  assert(dict->data.dict.size < dict->data.dict.capacity);
    156  if (key.type == kObjectTypeString) {
    157    dict->data.dict.items[dict->data.dict.size].key
    158      = key.data.string;
    159  } else {
    160    dict->data.dict.items[dict->data.dict.size].key
    161      = STATIC_CSTR_AS_STRING("__INVALID_KEY__");
    162  }
    163 }
    164 
    165 #define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \
    166  typval_encode_after_key(edata)
    167 
    168 static inline void typval_encode_between_dict_items(EncodedData *const edata)
    169  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
    170 {
    171  Object val = kv_pop(edata->stack);
    172  Object *const dict = &kv_last(edata->stack);
    173  assert(dict->type == kObjectTypeDict);
    174  assert(dict->data.dict.size < dict->data.dict.capacity);
    175  dict->data.dict.items[dict->data.dict.size++].value = val;
    176 }
    177 
    178 #define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
    179  typval_encode_between_dict_items(edata)
    180 
    181 static inline void typval_encode_dict_end(EncodedData *const edata)
    182  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
    183 {
    184  typval_encode_between_dict_items(edata);
    185 #ifndef NDEBUG
    186  const Object *const dict = &kv_last(edata->stack);
    187  assert(dict->data.dict.size == dict->data.dict.capacity);
    188 #endif
    189 }
    190 
    191 #define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
    192  typval_encode_dict_end(edata)
    193 
    194 #define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
    195  TYPVAL_ENCODE_CONV_NIL(val)
    196 
    197 #define TYPVAL_ENCODE_SCOPE static
    198 #define TYPVAL_ENCODE_NAME object
    199 #define TYPVAL_ENCODE_FIRST_ARG_TYPE EncodedData *const
    200 #define TYPVAL_ENCODE_FIRST_ARG_NAME edata
    201 #include "nvim/eval/typval_encode.c.h"
    202 
    203 #undef TYPVAL_ENCODE_SCOPE
    204 #undef TYPVAL_ENCODE_NAME
    205 #undef TYPVAL_ENCODE_FIRST_ARG_TYPE
    206 #undef TYPVAL_ENCODE_FIRST_ARG_NAME
    207 
    208 #undef TYPVAL_ENCODE_CONV_STRING
    209 #undef TYPVAL_ENCODE_CONV_STR_STRING
    210 #undef TYPVAL_ENCODE_CONV_EXT_STRING
    211 #undef TYPVAL_ENCODE_CONV_BLOB
    212 #undef TYPVAL_ENCODE_CONV_NUMBER
    213 #undef TYPVAL_ENCODE_CONV_FLOAT
    214 #undef TYPVAL_ENCODE_CONV_FUNC_START
    215 #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
    216 #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
    217 #undef TYPVAL_ENCODE_CONV_FUNC_END
    218 #undef TYPVAL_ENCODE_CONV_EMPTY_LIST
    219 #undef TYPVAL_ENCODE_CONV_LIST_START
    220 #undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
    221 #undef TYPVAL_ENCODE_CONV_EMPTY_DICT
    222 #undef TYPVAL_ENCODE_CHECK_BEFORE
    223 #undef TYPVAL_ENCODE_CONV_NIL
    224 #undef TYPVAL_ENCODE_CONV_BOOL
    225 #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
    226 #undef TYPVAL_ENCODE_CONV_DICT_START
    227 #undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
    228 #undef TYPVAL_ENCODE_CONV_DICT_END
    229 #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
    230 #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
    231 #undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
    232 #undef TYPVAL_ENCODE_CONV_LIST_END
    233 #undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
    234 #undef TYPVAL_ENCODE_CONV_RECURSE
    235 #undef TYPVAL_ENCODE_ALLOW_SPECIALS
    236 
    237 /// Convert a vim object to an `Object` instance, recursively converting
    238 /// Arrays/Dictionaries.
    239 ///
    240 /// @param obj The source object
    241 /// @param arena if NULL, use direct allocation
    242 /// @param reuse_strdata when true, don't copy string data to Arena but reference
    243 ///                      typval strings directly. takes no effect when arena is
    244 ///                      NULL
    245 /// @return The converted value
    246 Object vim_to_object(typval_T *obj, Arena *arena, bool reuse_strdata)
    247 {
    248  EncodedData edata;
    249  kvi_init(edata.stack);
    250  edata.arena = arena;
    251  edata.reuse_strdata = reuse_strdata;
    252  const int evo_ret = encode_vim_to_object(&edata, obj, "vim_to_object argument");
    253  (void)evo_ret;
    254  assert(evo_ret == OK);
    255  Object ret = kv_A(edata.stack, 0);
    256  assert(kv_size(edata.stack) == 1);
    257  kvi_destroy(edata.stack);
    258  return ret;
    259 }
    260 
    261 /// Converts from type Object to a Vimscript value.
    262 ///
    263 /// @param obj  Object to convert from.
    264 /// @param tv   Conversion result is placed here. On failure member v_type is
    265 ///             set to VAR_UNKNOWN (no allocation was made for this variable).
    266 /// @param err  Error object.
    267 void object_to_vim(Object obj, typval_T *tv, Error *err)
    268 {
    269  object_to_vim_take_luaref(&obj, tv, false, err);
    270 }
    271 
    272 /// same as object_to_vim but consumes all luarefs (nested) in `obj`
    273 ///
    274 /// useful when `obj` is allocated on an arena
    275 void object_to_vim_take_luaref(Object *obj, typval_T *tv, bool take_luaref, Error *err)
    276 {
    277  tv->v_type = VAR_UNKNOWN;
    278  tv->v_lock = VAR_UNLOCKED;
    279 
    280  switch (obj->type) {
    281  case kObjectTypeNil:
    282    tv->v_type = VAR_SPECIAL;
    283    tv->vval.v_special = kSpecialVarNull;
    284    break;
    285 
    286  case kObjectTypeBoolean:
    287    tv->v_type = VAR_BOOL;
    288    tv->vval.v_bool = obj->data.boolean ? kBoolVarTrue : kBoolVarFalse;
    289    break;
    290 
    291  case kObjectTypeBuffer:
    292  case kObjectTypeWindow:
    293  case kObjectTypeTabpage:
    294  case kObjectTypeInteger:
    295    STATIC_ASSERT(sizeof(obj->data.integer) <= sizeof(varnumber_T),
    296                  "Integer size must be <= Vimscript number size");
    297    tv->v_type = VAR_NUMBER;
    298    tv->vval.v_number = (varnumber_T)obj->data.integer;
    299    break;
    300 
    301  case kObjectTypeFloat:
    302    tv->v_type = VAR_FLOAT;
    303    tv->vval.v_float = obj->data.floating;
    304    break;
    305 
    306  case kObjectTypeString: {
    307    String s = obj->data.string;
    308    *tv = decode_string(s.data, s.size, false, false);
    309    break;
    310  }
    311 
    312  case kObjectTypeArray: {
    313    list_T *const list = tv_list_alloc((ptrdiff_t)obj->data.array.size);
    314 
    315    for (uint32_t i = 0; i < obj->data.array.size; i++) {
    316      typval_T li_tv;
    317      object_to_vim_take_luaref(&obj->data.array.items[i], &li_tv, take_luaref, err);
    318      tv_list_append_owned_tv(list, li_tv);
    319    }
    320    tv_list_ref(list);
    321 
    322    tv->v_type = VAR_LIST;
    323    tv->vval.v_list = list;
    324    break;
    325  }
    326 
    327  case kObjectTypeDict: {
    328    dict_T *const dict = tv_dict_alloc();
    329 
    330    for (uint32_t i = 0; i < obj->data.dict.size; i++) {
    331      KeyValuePair *item = &obj->data.dict.items[i];
    332      String key = item->key;
    333      dictitem_T *const di = tv_dict_item_alloc(key.data);
    334      object_to_vim_take_luaref(&item->value, &di->di_tv, take_luaref, err);
    335      tv_dict_add(dict, di);
    336    }
    337    dict->dv_refcount++;
    338 
    339    tv->v_type = VAR_DICT;
    340    tv->vval.v_dict = dict;
    341    break;
    342  }
    343 
    344  case kObjectTypeLuaRef: {
    345    LuaRef ref = obj->data.luaref;
    346    if (take_luaref) {
    347      obj->data.luaref = LUA_NOREF;
    348    } else {
    349      ref = api_new_luaref(ref);
    350    }
    351    char *name = register_luafunc(ref);
    352    tv->v_type = VAR_FUNC;
    353    tv->vval.v_string = xstrdup(name);
    354    break;
    355  }
    356  }
    357 }