typval_encode.c.h (33679B)
1 // uncrustify:off 2 3 /// @file eval/typval_encode.c.h 4 /// 5 /// Contains set of macros used to convert (possibly recursive) typval_T into 6 /// something else. For these macros to work the following macros must be 7 /// defined: 8 9 /// @def TYPVAL_ENCODE_CHECK_BEFORE 10 /// @brief Macro used before any specific CONV function 11 /// 12 /// can be used for a common check, like flushing a buffer if necessary 13 14 /// @def TYPVAL_ENCODE_CONV_NIL 15 /// @brief Macros used to convert NIL value 16 /// 17 /// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS 18 /// is false) and `v:null`. 19 /// 20 /// @param tv Pointer to typval where value is stored. May not be NULL. May 21 /// point to special dictionary. 22 23 /// @def TYPVAL_ENCODE_CONV_BOOL 24 /// @brief Macros used to convert boolean value 25 /// 26 /// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS 27 /// is false) and `v:true`/`v:false`. 28 /// 29 /// @param tv Pointer to typval where value is stored. May not be NULL. May 30 /// point to a special dictionary. 31 /// @param num Boolean value to convert. Value is an expression which 32 /// evaluates to some integer. 33 34 /// @def TYPVAL_ENCODE_CONV_NUMBER 35 /// @brief Macros used to convert integer 36 /// 37 /// @param tv Pointer to typval where value is stored. May not be NULL. May 38 /// point to a special dictionary. 39 /// @param num Integer to convert, must accept both varnumber_T and int64_t. 40 41 /// @def TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER 42 /// @brief Macros used to convert unsigned integer 43 /// 44 /// Not used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be 45 /// defined. 46 /// 47 /// @param tv Pointer to typval where value is stored. May not be NULL. Points 48 /// to a special dictionary. 49 /// @param num Integer to convert, must accept uint64_t. 50 51 /// @def TYPVAL_ENCODE_CONV_FLOAT 52 /// @brief Macros used to convert floating-point number 53 /// 54 /// @param tv Pointer to typval where value is stored. May not be NULL. May 55 /// point to a special dictionary. 56 /// @param flt Number to convert, must accept float_T. 57 58 /// @def TYPVAL_ENCODE_CONV_STRING 59 /// @brief Macros used to convert plain string 60 /// 61 /// Is used to convert VAR_STRING objects as well as BIN strings represented as 62 /// special dictionary. 63 /// 64 /// @param tv Pointer to typval where value is stored. May not be NULL. May 65 /// point to a special dictionary. 66 /// @param buf String to convert. Is a char[] buffer, not NUL-terminated. 67 /// @param len String length. 68 69 /// @def TYPVAL_ENCODE_CONV_STR_STRING 70 /// @brief Like #TYPVAL_ENCODE_CONV_STRING, but for STR strings 71 /// 72 /// Is used to convert dictionary keys and STR strings represented as special 73 /// dictionaries. 74 /// 75 /// @param tv Pointer to typval where value is stored. May be NULL. May 76 /// point to a special dictionary. 77 /// @param buf String to convert. Is a char[] buffer, not NUL-terminated. 78 /// @param len String length. 79 80 /// @def TYPVAL_ENCODE_CONV_EXT_STRING 81 /// @brief Macros used to convert EXT string 82 /// 83 /// Is used to convert EXT strings represented as special dictionaries. Never 84 /// actually used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be 85 /// defined. 86 /// 87 /// @param tv Pointer to typval where value is stored. May not be NULL. Points 88 /// to a special dictionary. 89 /// @param buf String to convert. Is a char[] buffer, not NUL-terminated. 90 /// @param len String length. 91 /// @param type EXT type. 92 93 /// @def TYPVAL_ENCODE_CONV_BLOB 94 /// @brief Macros used to convert a blob 95 /// 96 /// @param tv Pointer to typval where value is stored. May not be NULL. 97 /// @param blob Pointer to the blob to convert. 98 /// @param len Blob length. 99 100 /// @def TYPVAL_ENCODE_CONV_FUNC_START 101 /// @brief Macros used when starting to convert a funcref or a partial 102 /// 103 /// @param tv Pointer to typval where value is stored. May not be NULL. 104 /// @param fun Function name. May be NULL. 105 /// @param prefix Prefix for converting to a string. 106 107 /// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS 108 /// @brief Macros used before starting to convert partial arguments 109 /// 110 /// @param tv Pointer to typval where value is stored. May not be NULL. 111 /// @param len Number of arguments. Zero for absent arguments or when 112 /// converting a funcref. 113 114 /// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF 115 /// @brief Macros used before starting to convert self dictionary 116 /// 117 /// @param tv Pointer to typval where value is stored. May not be NULL. 118 /// @param len Number of arguments. May be zero for empty dictionary or -1 for 119 /// missing self dictionary, also when converting function 120 /// reference. 121 122 /// @def TYPVAL_ENCODE_CONV_FUNC_END 123 /// @brief Macros used after converting a funcref or a partial 124 /// 125 /// @param tv Pointer to typval where value is stored. May not be NULL. 126 127 /// @def TYPVAL_ENCODE_CONV_EMPTY_LIST 128 /// @brief Macros used to convert an empty list 129 /// 130 /// @param tv Pointer to typval where value is stored. May not be NULL. 131 132 /// @def TYPVAL_ENCODE_CONV_EMPTY_DICT 133 /// @brief Macros used to convert an empty dictionary 134 /// 135 /// @param tv Pointer to typval where value is stored. May be NULL. May 136 /// point to a special dictionary. 137 /// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR 138 /// (for dictionaries represented as special lists). 139 140 /// @def TYPVAL_ENCODE_CONV_LIST_START 141 /// @brief Macros used before starting to convert non-empty list 142 /// 143 /// @param tv Pointer to typval where value is stored. May be NULL. May 144 /// point to a special dictionary. 145 /// @param len List length. Is an expression which evaluates to an integer. 146 147 /// @def TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START 148 /// @brief Macros used after pushing list onto the stack 149 /// 150 /// Only used for real list_T* lists, not for special dictionaries or partial 151 /// arguments. 152 /// 153 /// @param tv Pointer to typval where value is stored. May be NULL. May 154 /// point to a special dictionary. 155 /// @param mpsv Pushed MPConvStackVal value. 156 157 /// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS 158 /// @brief Macros used after finishing converting non-last list item 159 /// 160 /// @param tv Pointer to typval where list is stored. May be NULL. 161 162 /// @def TYPVAL_ENCODE_CONV_LIST_END 163 /// @brief Macros used after converting non-empty list 164 /// 165 /// @param tv Pointer to typval where list is stored. May be NULL. 166 167 /// @def TYPVAL_ENCODE_CONV_DICT_START 168 /// @brief Macros used before starting to convert non-empty dictionary 169 /// 170 /// Only used for real dict_T* dictionaries, not for special dictionaries. Also 171 /// used for partial self dictionary. 172 /// 173 /// @param tv Pointer to typval where dictionary is stored. May be NULL. May 174 /// point to a special dictionary. 175 /// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR 176 /// (for dictionaries represented as special lists). 177 /// @param len Dict length. Is an expression which evaluates to an integer. 178 179 /// @def TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START 180 /// @brief Macros used after pushing dictionary onto the stack 181 /// 182 /// @param tv Pointer to typval where dictionary is stored. May be NULL. 183 /// May not point to a special dictionary. 184 /// @param dict Converted dictionary, lvalue. 185 /// @param mpsv Pushed MPConvStackVal value. 186 187 /// @def TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK 188 /// @brief Macros used to check special dictionary key 189 /// 190 /// @param label Label for goto in case check was not successful. 191 /// @param key typval_T key to check. 192 193 /// @def TYPVAL_ENCODE_CONV_DICT_AFTER_KEY 194 /// @brief Macros used after finishing converting dictionary key 195 /// 196 /// @param tv Pointer to typval where dictionary is stored. May be NULL. May 197 /// point to a special dictionary. 198 /// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR 199 /// (for dictionaries represented as special lists). 200 201 /// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS 202 /// @brief Macros used after finishing converting non-last dictionary value 203 /// 204 /// @param tv Pointer to typval where dictionary is stored. May be NULL. May 205 /// point to a special dictionary. 206 /// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR 207 /// (for dictionaries represented as special lists). 208 209 /// @def TYPVAL_ENCODE_CONV_DICT_END 210 /// @brief Macros used after converting non-empty dictionary 211 /// 212 /// @param tv Pointer to typval where dictionary is stored. May be NULL. May 213 /// point to a special dictionary. 214 /// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR 215 /// (for dictionaries represented as special lists). 216 217 /// @def TYPVAL_ENCODE_CONV_RECURSE 218 /// @brief Macros used when self-containing container is detected 219 /// 220 /// @param val Container for which this situation was detected. 221 /// @param conv_type Type of the stack entry, @see MPConvStackValType. 222 223 /// @def TYPVAL_ENCODE_ALLOW_SPECIALS 224 /// @brief Macros that specifies whether special dictionaries are special 225 /// 226 /// Must be something that evaluates to boolean, most likely `true` or `false`. 227 /// If it is false then special dictionaries are not treated specially. 228 229 /// @def TYPVAL_ENCODE_SCOPE 230 /// @brief Scope of the main function: either nothing or `static` 231 232 /// @def TYPVAL_ENCODE_NAME 233 /// @brief Name of the target converter 234 /// 235 /// After including this file it will define function 236 /// `encode_vim_to_{TYPVAL_ENCODE_NAME}` with scope #TYPVAL_ENCODE_SCOPE and 237 /// static functions `_typval_encode_{TYPVAL_ENCODE_NAME}_convert_one_value` and 238 /// `_typval_encode_{TYPVAL_ENCODE_NAME}_check_self_reference`. 239 240 /// @def TYPVAL_ENCODE_FIRST_ARG_TYPE 241 /// @brief Type of the first argument, which will be used to return the results 242 /// 243 /// Is expected to be a pointer type. 244 245 /// @def TYPVAL_ENCODE_FIRST_ARG_NAME 246 /// @brief Name of the first argument 247 /// 248 /// This name will only be used by one of the above macros which are defined by 249 /// the caller. Functions defined here do not use first argument directly. 250 #include <assert.h> 251 #include <inttypes.h> 252 #include <stddef.h> 253 254 #include "klib/kvec.h" 255 #include "nvim/eval.h" 256 #include "nvim/eval/encode.h" 257 #include "nvim/eval/typval.h" 258 #include "nvim/eval/typval_encode.h" 259 #include "nvim/eval/vars.h" 260 #include "nvim/func_attr.h" 261 262 /// Dummy variable used because some macros need lvalue 263 /// 264 /// Must not be written to, if needed one must check that address of the 265 /// macros argument is (not) equal to `&TYPVAL_ENCODE_NODICT_VAR`. 266 const dict_T *const TYPVAL_ENCODE_NODICT_VAR = NULL; 267 268 static inline int TYPVAL_ENCODE_CHECK_SELF_REFERENCE( 269 TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, 270 void *const val, int *const val_copyID, 271 const MPConvStack *const mpstack, const int copyID, 272 const MPConvStackValType conv_type, 273 const char *const objname) 274 REAL_FATTR_NONNULL_ARG(2, 3, 4, 7) REAL_FATTR_WARN_UNUSED_RESULT 275 REAL_FATTR_ALWAYS_INLINE; 276 277 /// Function for checking whether container references itself 278 /// 279 /// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument. 280 /// @param[in,out] val Container to check. 281 /// @param val_copyID Pointer to the container attribute that holds copyID. 282 /// After checking whether value of this attribute is 283 /// copyID (variable) it is set to copyID. 284 /// @param[in] mpstack Stack with values to convert. Read-only, used for error 285 /// reporting. 286 /// @param[in] copyID CopyID used by the caller. 287 /// @param[in] conv_type Type of the conversion, @see MPConvStackValType. 288 /// @param[in] objname Object name, used for error reporting. 289 /// 290 /// @return NOTDONE in case of success, what to return in case of failure. 291 static inline int TYPVAL_ENCODE_CHECK_SELF_REFERENCE( 292 TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, void *const val, int *const val_copyID, 293 const MPConvStack *const mpstack, const int copyID, const MPConvStackValType conv_type, 294 const char *const objname) 295 { 296 if (*val_copyID == copyID) { 297 TYPVAL_ENCODE_CONV_RECURSE(val, conv_type); 298 return OK; 299 } 300 *val_copyID = copyID; 301 return NOTDONE; 302 } 303 304 static int TYPVAL_ENCODE_CONVERT_ONE_VALUE( 305 TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, 306 MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv, 307 typval_T *const tv, const int copyID, 308 const char *const objname) 309 REAL_FATTR_NONNULL_ARG(2, 4, 6) REAL_FATTR_WARN_UNUSED_RESULT; 310 311 /// Convert single value 312 /// 313 /// Only scalar values are converted immediately, everything else is pushed onto 314 /// the stack. 315 /// 316 /// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument, defined by the 317 /// includer. Only meaningful to macros 318 /// defined by the includer. 319 /// @param mpstack Stack with values to convert. Values which are not 320 /// converted completely by this function (i.e. 321 /// non-scalars) are pushed here. 322 /// @param cur_mpsv Currently converted value from stack. 323 /// @param tv Converted value. 324 /// @param[in] copyID CopyID. 325 /// @param[in] objname Object name, used for error reporting. 326 /// 327 /// @return OK in case of success, FAIL in case of failure. 328 static int TYPVAL_ENCODE_CONVERT_ONE_VALUE( 329 TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, MPConvStack *const mpstack, 330 MPConvStackVal *const cur_mpsv, typval_T *const tv, const int copyID, const char *const objname) 331 { 332 TYPVAL_ENCODE_CHECK_BEFORE; 333 switch (tv->v_type) { 334 case VAR_STRING: 335 TYPVAL_ENCODE_CONV_STRING(tv, tv->vval.v_string, tv_strlen(tv)); 336 break; 337 case VAR_NUMBER: 338 TYPVAL_ENCODE_CONV_NUMBER(tv, tv->vval.v_number); 339 break; 340 case VAR_FLOAT: 341 TYPVAL_ENCODE_CONV_FLOAT(tv, tv->vval.v_float); 342 break; 343 case VAR_BLOB: 344 TYPVAL_ENCODE_CONV_BLOB(tv, tv->vval.v_blob, 345 tv_blob_len(tv->vval.v_blob)); 346 break; 347 case VAR_FUNC: 348 TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string, ""); 349 TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0); 350 TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1); 351 TYPVAL_ENCODE_CONV_FUNC_END(tv); 352 break; 353 case VAR_PARTIAL: { 354 partial_T *const pt = tv->vval.v_partial; 355 char *const fun = pt == NULL ? NULL : partial_name(pt); 356 // When using uf_name prepend "g:" for a global function. 357 const char *const prefix = fun != NULL && pt != NULL && pt->pt_name == NULL 358 && ASCII_ISUPPER(fun[0]) ? "g:" : ""; 359 (void)prefix; 360 TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix); 361 kvi_push(*mpstack, ((MPConvStackVal) { 362 .type = kMPConvPartial, 363 .tv = tv, 364 .saved_copyID = copyID - 1, 365 .data = { 366 .p = { 367 .stage = kMPConvPartialArgs, 368 .pt = tv->vval.v_partial, 369 }, 370 }, 371 })); 372 break; 373 } 374 case VAR_LIST: { 375 if (tv->vval.v_list == NULL || tv_list_len(tv->vval.v_list) == 0) { 376 TYPVAL_ENCODE_CONV_EMPTY_LIST(tv); 377 break; 378 } 379 const int saved_copyID = tv_list_copyid(tv->vval.v_list); 380 TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID, 381 kMPConvList); 382 TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list)); 383 assert(saved_copyID != copyID); 384 kvi_push(*mpstack, ((MPConvStackVal) { 385 .type = kMPConvList, 386 .tv = tv, 387 .saved_copyID = saved_copyID, 388 .data = { 389 .l = { 390 .list = tv->vval.v_list, 391 .li = tv_list_first(tv->vval.v_list), 392 }, 393 }, 394 })); 395 TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, kv_last(*mpstack)); 396 break; 397 } 398 case VAR_BOOL: 399 switch (tv->vval.v_bool) { 400 case kBoolVarTrue: 401 case kBoolVarFalse: 402 TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_bool == kBoolVarTrue); 403 break; 404 } 405 break; 406 case VAR_SPECIAL: 407 switch (tv->vval.v_special) { 408 case kSpecialVarNull: 409 TYPVAL_ENCODE_CONV_NIL(tv); 410 break; 411 } 412 break; 413 case VAR_DICT: { 414 if (tv->vval.v_dict == NULL 415 || tv->vval.v_dict->dv_hashtab.ht_used == 0) { 416 TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, tv->vval.v_dict); 417 break; 418 } 419 const dictitem_T *type_di; 420 const dictitem_T *val_di; 421 if (TYPVAL_ENCODE_ALLOW_SPECIALS 422 && tv->vval.v_dict->dv_hashtab.ht_used == 2 423 && (type_di = tv_dict_find((dict_T *)tv->vval.v_dict, 424 S_LEN("_TYPE"))) != NULL 425 && type_di->di_tv.v_type == VAR_LIST 426 && (val_di = tv_dict_find((dict_T *)tv->vval.v_dict, 427 S_LEN("_VAL"))) != NULL) { 428 size_t i; 429 for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { 430 if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { 431 break; 432 } 433 } 434 TYPVAL_ENCODE_CHECK_BEFORE; 435 if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { 436 goto _convert_one_value_regular_dict; 437 } 438 switch ((MessagePackType)i) { 439 case kMPNil: 440 TYPVAL_ENCODE_CONV_NIL(tv); 441 break; 442 case kMPBoolean: 443 if (val_di->di_tv.v_type != VAR_NUMBER) { 444 goto _convert_one_value_regular_dict; 445 } 446 TYPVAL_ENCODE_CONV_BOOL(tv, val_di->di_tv.vval.v_number); 447 break; 448 case kMPInteger: { 449 const list_T *val_list; 450 varnumber_T sign; 451 varnumber_T highest_bits; 452 varnumber_T high_bits; 453 varnumber_T low_bits; 454 // List of 4 integers; first is signed (should be 1 or -1, but 455 // this is not checked), second is unsigned and have at most 456 // one (sign is -1) or two (sign is 1) non-zero bits (number of 457 // bits is not checked), other unsigned and have at most 31 458 // non-zero bits (number of bits is not checked). 459 if (val_di->di_tv.v_type != VAR_LIST 460 || tv_list_len(val_list = val_di->di_tv.vval.v_list) != 4) { 461 goto _convert_one_value_regular_dict; 462 } 463 464 const listitem_T *const sign_li = tv_list_first(val_list); 465 if (TV_LIST_ITEM_TV(sign_li)->v_type != VAR_NUMBER 466 || (sign = TV_LIST_ITEM_TV(sign_li)->vval.v_number) == 0) { 467 goto _convert_one_value_regular_dict; 468 } 469 470 const listitem_T *const highest_bits_li = ( 471 TV_LIST_ITEM_NEXT(val_list, sign_li)); 472 if (TV_LIST_ITEM_TV(highest_bits_li)->v_type != VAR_NUMBER 473 || ((highest_bits = TV_LIST_ITEM_TV(highest_bits_li)->vval.v_number) 474 < 0)) { 475 goto _convert_one_value_regular_dict; 476 } 477 478 const listitem_T *const high_bits_li = ( 479 TV_LIST_ITEM_NEXT(val_list, highest_bits_li)); 480 if (TV_LIST_ITEM_TV(high_bits_li)->v_type != VAR_NUMBER 481 || ((high_bits = TV_LIST_ITEM_TV(high_bits_li)->vval.v_number) 482 < 0)) { 483 goto _convert_one_value_regular_dict; 484 } 485 486 const listitem_T *const low_bits_li = tv_list_last(val_list); 487 if (TV_LIST_ITEM_TV(low_bits_li)->v_type != VAR_NUMBER 488 || ((low_bits = TV_LIST_ITEM_TV(low_bits_li)->vval.v_number) 489 < 0)) { 490 goto _convert_one_value_regular_dict; 491 } 492 493 const uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62) 494 | (uint64_t)(((uint64_t)high_bits) << 31) 495 | (uint64_t)low_bits); 496 if (sign > 0) { 497 TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number); 498 } else { 499 TYPVAL_ENCODE_CONV_NUMBER(tv, -number); 500 } 501 break; 502 } 503 case kMPFloat: 504 if (val_di->di_tv.v_type != VAR_FLOAT) { 505 goto _convert_one_value_regular_dict; 506 } 507 TYPVAL_ENCODE_CONV_FLOAT(tv, val_di->di_tv.vval.v_float); 508 break; 509 case kMPString: { 510 if (val_di->di_tv.v_type != VAR_LIST) { 511 goto _convert_one_value_regular_dict; 512 } 513 size_t len; 514 char *buf; 515 if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, 516 &buf)) { 517 goto _convert_one_value_regular_dict; 518 } 519 TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len); 520 xfree(buf); 521 break; 522 } 523 case kMPArray: { 524 if (val_di->di_tv.v_type != VAR_LIST) { 525 goto _convert_one_value_regular_dict; 526 } 527 const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list); 528 TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, 529 lv_copyID, copyID, 530 kMPConvList); 531 TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(val_di->di_tv.vval.v_list)); 532 assert(saved_copyID != copyID && saved_copyID != copyID - 1); 533 kvi_push(*mpstack, ((MPConvStackVal) { 534 .tv = tv, 535 .type = kMPConvList, 536 .saved_copyID = saved_copyID, 537 .data = { 538 .l = { 539 .list = val_di->di_tv.vval.v_list, 540 .li = tv_list_first(val_di->di_tv.vval.v_list), 541 }, 542 }, 543 })); 544 break; 545 } 546 case kMPMap: { 547 if (val_di->di_tv.v_type != VAR_LIST) { 548 goto _convert_one_value_regular_dict; 549 } 550 list_T *const val_list = val_di->di_tv.vval.v_list; 551 if (val_list == NULL || tv_list_len(val_list) == 0) { 552 TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR); 553 break; 554 } 555 TV_LIST_ITER_CONST(val_list, li, { 556 if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST 557 || tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) != 2) { 558 goto _convert_one_value_regular_dict; 559 } 560 }); 561 const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list); 562 TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID, 563 kMPConvPairs); 564 TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR, 565 tv_list_len(val_list)); 566 assert(saved_copyID != copyID && saved_copyID != copyID - 1); 567 kvi_push(*mpstack, ((MPConvStackVal) { 568 .tv = tv, 569 .type = kMPConvPairs, 570 .saved_copyID = saved_copyID, 571 .data = { 572 .l = { 573 .list = val_list, 574 .li = tv_list_first(val_list), 575 }, 576 }, 577 })); 578 break; 579 } 580 case kMPExt: { 581 const list_T *val_list; 582 varnumber_T type; 583 if (val_di->di_tv.v_type != VAR_LIST 584 || tv_list_len((val_list = val_di->di_tv.vval.v_list)) != 2 585 || (TV_LIST_ITEM_TV(tv_list_first(val_list))->v_type 586 != VAR_NUMBER) 587 || ((type = TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number) 588 > INT8_MAX) 589 || type < INT8_MIN 590 || (TV_LIST_ITEM_TV(tv_list_last(val_list))->v_type 591 != VAR_LIST)) { 592 goto _convert_one_value_regular_dict; 593 } 594 size_t len; 595 char *buf; 596 if (!( 597 encode_vim_list_to_buf(TV_LIST_ITEM_TV(tv_list_last(val_list))->vval.v_list, &len, 598 &buf))) { 599 goto _convert_one_value_regular_dict; 600 } 601 TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type); 602 xfree(buf); 603 break; 604 } 605 } 606 break; 607 } 608 _convert_one_value_regular_dict: {} 609 const int saved_copyID = tv->vval.v_dict->dv_copyID; 610 TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, copyID, 611 kMPConvDict); 612 TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict, 613 tv->vval.v_dict->dv_hashtab.ht_used); 614 assert(saved_copyID != copyID); 615 kvi_push(*mpstack, ((MPConvStackVal) { 616 .tv = tv, 617 .type = kMPConvDict, 618 .saved_copyID = saved_copyID, 619 .data = { 620 .d = { 621 .dict = tv->vval.v_dict, 622 .dictp = &tv->vval.v_dict, 623 .hi = tv->vval.v_dict->dv_hashtab.ht_array, 624 .todo = tv->vval.v_dict->dv_hashtab.ht_used, 625 }, 626 }, 627 })); 628 TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, tv->vval.v_dict, 629 kv_last(*mpstack)); 630 break; 631 } 632 case VAR_UNKNOWN: 633 internal_error(STR(TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()"); 634 return FAIL; 635 } 636 typval_encode_stop_converting_one_item: 637 return OK; 638 // Prevent “unused label” warnings. 639 goto typval_encode_stop_converting_one_item; 640 } 641 642 TYPVAL_ENCODE_SCOPE int TYPVAL_ENCODE_ENCODE( 643 TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, 644 typval_T *const tv, const char *const objname) 645 REAL_FATTR_NONNULL_ARG(2, 3) REAL_FATTR_WARN_UNUSED_RESULT; 646 647 /// Convert the whole typval 648 /// 649 /// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument, defined by the 650 /// includer. Only meaningful to macros 651 /// defined by the includer. 652 /// @param top_tv Converted value. 653 /// @param[in] objname Object name, used for error reporting. 654 /// 655 /// @return OK in case of success, FAIL in case of failure. 656 TYPVAL_ENCODE_SCOPE int TYPVAL_ENCODE_ENCODE( 657 TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, typval_T *const top_tv, 658 const char *const objname) 659 { 660 const int copyID = get_copyID(); 661 MPConvStack mpstack; 662 kvi_init(mpstack); 663 if (TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, 664 NULL, 665 top_tv, copyID, objname) 666 == FAIL) { 667 goto encode_vim_to__error_ret; 668 } 669 /// Label common for this and convert_one_value functions, used for escaping 670 /// from macros like TYPVAL_ENCODE_CONV_DICT_START. 671 typval_encode_stop_converting_one_item: 672 while (kv_size(mpstack)) { 673 MPConvStackVal *cur_mpsv = &kv_last(mpstack); 674 typval_T *tv = NULL; 675 switch (cur_mpsv->type) { 676 case kMPConvDict: { 677 if (!cur_mpsv->data.d.todo) { 678 (void)kv_pop(mpstack); 679 cur_mpsv->data.d.dict->dv_copyID = cur_mpsv->saved_copyID; 680 TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, *cur_mpsv->data.d.dictp); 681 continue; 682 } else if (cur_mpsv->data.d.todo 683 != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { 684 TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(cur_mpsv->tv, 685 *cur_mpsv->data.d.dictp); 686 } 687 while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { 688 cur_mpsv->data.d.hi++; 689 } 690 dictitem_T *const di = TV_DICT_HI2DI(cur_mpsv->data.d.hi); 691 cur_mpsv->data.d.todo--; 692 cur_mpsv->data.d.hi++; 693 TYPVAL_ENCODE_CONV_STR_STRING(NULL, &di->di_key[0], 694 strlen((char *)&di->di_key[0])); 695 TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv, 696 *cur_mpsv->data.d.dictp); 697 tv = &di->di_tv; 698 break; 699 } 700 case kMPConvList: 701 if (cur_mpsv->data.l.li == NULL) { 702 (void)kv_pop(mpstack); 703 tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID); 704 TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv); 705 continue; 706 } else if (cur_mpsv->data.l.li 707 != tv_list_first(cur_mpsv->data.l.list)) { 708 TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv); 709 } 710 tv = TV_LIST_ITEM_TV(cur_mpsv->data.l.li); 711 cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list, 712 cur_mpsv->data.l.li); 713 break; 714 case kMPConvPairs: { 715 if (cur_mpsv->data.l.li == NULL) { 716 (void)kv_pop(mpstack); 717 tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID); 718 TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); 719 continue; 720 } else if (cur_mpsv->data.l.li 721 != tv_list_first(cur_mpsv->data.l.list)) { 722 TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); 723 } 724 const list_T *const kv_pair = ( 725 TV_LIST_ITEM_TV(cur_mpsv->data.l.li)->vval.v_list); 726 TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(encode_vim_to__error_ret, 727 *TV_LIST_ITEM_TV(tv_list_first(kv_pair))); 728 if ( 729 TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_mpsv, 730 TV_LIST_ITEM_TV(tv_list_first(kv_pair)), copyID, objname) 731 == FAIL) { 732 goto encode_vim_to__error_ret; 733 } 734 TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv, 735 TYPVAL_ENCODE_NODICT_VAR); 736 tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)); 737 cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list, 738 cur_mpsv->data.l.li); 739 break; 740 } 741 case kMPConvPartial: { 742 partial_T *const pt = cur_mpsv->data.p.pt; 743 tv = cur_mpsv->tv; 744 (void)tv; 745 switch (cur_mpsv->data.p.stage) { 746 case kMPConvPartialArgs: 747 TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 748 pt == NULL ? 0 : pt->pt_argc); 749 cur_mpsv->data.p.stage = kMPConvPartialSelf; 750 if (pt != NULL && pt->pt_argc > 0) { 751 TYPVAL_ENCODE_CONV_LIST_START(NULL, pt->pt_argc); 752 kvi_push(mpstack, ((MPConvStackVal) { 753 .type = kMPConvPartialList, 754 .tv = NULL, 755 .saved_copyID = copyID - 1, 756 .data = { 757 .a = { 758 .arg = pt->pt_argv, 759 .argv = pt->pt_argv, 760 .todo = (size_t)pt->pt_argc, 761 }, 762 }, 763 })); 764 } 765 break; 766 case kMPConvPartialSelf: { 767 cur_mpsv->data.p.stage = kMPConvPartialEnd; 768 dict_T *const dict = pt == NULL ? NULL : pt->pt_dict; 769 if (dict != NULL) { 770 TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, dict->dv_hashtab.ht_used); 771 if (dict->dv_hashtab.ht_used == 0) { 772 TYPVAL_ENCODE_CONV_EMPTY_DICT(NULL, pt->pt_dict); 773 continue; 774 } 775 const int saved_copyID = dict->dv_copyID; 776 const int te_csr_ret = TYPVAL_ENCODE_CHECK_SELF_REFERENCE(TYPVAL_ENCODE_FIRST_ARG_NAME, 777 dict, &dict->dv_copyID, 778 &mpstack, copyID, kMPConvDict, 779 objname); 780 if (te_csr_ret != NOTDONE) { 781 if (te_csr_ret == FAIL) { 782 goto encode_vim_to__error_ret; 783 } else { 784 continue; 785 } 786 } 787 TYPVAL_ENCODE_CONV_DICT_START(NULL, pt->pt_dict, 788 dict->dv_hashtab.ht_used); 789 assert(saved_copyID != copyID && saved_copyID != copyID - 1); 790 kvi_push(mpstack, ((MPConvStackVal) { 791 .type = kMPConvDict, 792 .tv = NULL, 793 .saved_copyID = saved_copyID, 794 .data = { 795 .d = { 796 .dict = dict, 797 .dictp = &pt->pt_dict, 798 .hi = dict->dv_hashtab.ht_array, 799 .todo = dict->dv_hashtab.ht_used, 800 }, 801 }, 802 })); 803 TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(NULL, pt->pt_dict, 804 kv_last(mpstack)); 805 } else { 806 TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1); 807 } 808 break; 809 } 810 case kMPConvPartialEnd: 811 TYPVAL_ENCODE_CONV_FUNC_END(tv); 812 (void)kv_pop(mpstack); 813 break; 814 } 815 continue; 816 } 817 case kMPConvPartialList: 818 if (!cur_mpsv->data.a.todo) { 819 (void)kv_pop(mpstack); 820 TYPVAL_ENCODE_CONV_LIST_END(NULL); 821 continue; 822 } else if (cur_mpsv->data.a.argv != cur_mpsv->data.a.arg) { 823 TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(NULL); 824 } 825 tv = cur_mpsv->data.a.arg++; 826 cur_mpsv->data.a.todo--; 827 break; 828 } 829 assert(tv != NULL); 830 if (TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, 831 cur_mpsv, tv, copyID, objname) 832 == FAIL) { 833 goto encode_vim_to__error_ret; 834 } 835 } 836 kvi_destroy(mpstack); 837 return OK; 838 encode_vim_to__error_ret: 839 kvi_destroy(mpstack); 840 return FAIL; 841 // Prevent “unused label” warnings. 842 goto typval_encode_stop_converting_one_item; 843 }