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