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`. |
250 | const dict_T *const TYPVAL_ENCODE_NODICT_VAR = NULL; |
251 | |
252 | static 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. |
275 | static 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 | |
290 | static 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. |
314 | static 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 | } |
633 | typval_encode_stop_converting_one_item: |
634 | return OK; |
635 | // Prevent “unused label” warnings. |
636 | goto typval_encode_stop_converting_one_item; // -V779 |
637 | } |
638 | |
639 | TYPVAL_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. |
653 | TYPVAL_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. |
668 | typval_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; |
841 | encode_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 | |