v2 / vlib / v / gen / c / json.v
1377 lines · 1317 sloc · 53.65 KB · 07c796b670d9e498ccb25605af189617f61ec295
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module c
5
6import v.ast
7import v.token
8import v.util
9import strings
10
11// TODO: replace with comptime code generation.
12// TODO: remove cJSON dependency.
13
14// Old:
15// `User decode_User(string js) {`
16// now it's:
17// ```
18// User decode_User(cJSON* root) {
19// User res;
20// res.name = decode_string(js_get(root, "name"));
21// res.profile = decode_Profile(js_get(root, "profile"));
22// return res;
23// }
24// ```
25
26// Codegen json_decode/encode funcs
27fn (mut g Gen) gen_json_for_type(typ ast.Type) {
28 utyp := g.unwrap_generic(typ)
29 sym := g.table.sym(utyp)
30 if is_js_prim(sym.name) && !utyp.has_flag(.option) && !typ.is_ptr() {
31 return
32 }
33 if g.json_gen_pos != token.Pos{}
34 && (utyp !in g.json_types_pos || g.json_types_pos[utyp] == token.Pos{}) {
35 g.json_types_pos[utyp] = g.json_gen_pos
36 }
37 g.json_types << utyp
38}
39
40fn (mut g Gen) gen_json_for_type_with_pos(typ ast.Type, pos token.Pos) {
41 saved_json_gen_pos := g.json_gen_pos
42 g.json_gen_pos = pos
43 g.gen_json_for_type(typ)
44 g.json_gen_pos = saved_json_gen_pos
45}
46
47fn (mut g Gen) json_error(typ ast.Type, s string) {
48 utyp := g.unwrap_generic(typ)
49 mut pos := g.json_gen_pos
50 if utyp in g.json_types_pos {
51 pos = g.json_types_pos[utyp]
52 }
53 if pos != token.Pos{} {
54 g.error(s, pos)
55 }
56 verror(s)
57}
58
59fn (mut g Gen) gen_jsons() {
60 mut done := []ast.Type{}
61 for i := 0; i < g.json_types.len; i++ {
62 utyp := g.json_types[i]
63 if utyp in done {
64 continue
65 }
66 done << utyp
67 saved_json_gen_pos := g.json_gen_pos
68 g.json_gen_pos = g.json_types_pos[utyp]
69 mut dec := strings.new_builder(100)
70 mut enc := strings.new_builder(100)
71 sym := g.table.sym(utyp)
72 styp := g.styp(utyp)
73 ret_styp := styp.replace('*', '_ptr')
74 if utyp.is_ptr() && utyp.has_flag(.option) {
75 g.register_option(utyp.set_nr_muls(0))
76 }
77 g.register_result(utyp)
78
79 // decode_TYPE funcs receive an actual cJSON* object to decode
80 // cJSON_Parse(str) call is added by the compiler
81 // Codegen decoder
82 dec_fn_name := js_dec_name(styp)
83 dec_fn_dec := '${result_name}_${ret_styp} ${dec_fn_name}(cJSON* root)'
84
85 mut init_styp := '${styp} res'
86 if utyp.has_flag(.option) {
87 none_str := g.expr_string(ast.None{})
88 init_styp += ' = (${styp}){ .state=2, .err=${none_str}, .data={E_STRUCT} }'
89 } else {
90 if !utyp.is_ptr() {
91 init_styp += ' = '
92 g.set_current_pos_as_last_stmt_pos()
93 pos := g.out.len
94 g.write(g.type_default(utyp))
95 init_generated := g.out.cut_to(pos).trim_space()
96 if g.type_default_vars.len > 0 {
97 saved_init := init_styp
98 init_styp = g.type_default_vars.bytestr()
99 init_styp += '\n'
100 init_styp += saved_init
101 g.type_default_vars.clear()
102 }
103 init_styp += init_generated
104 } else if utyp.is_ptr() {
105 ptr_styp := g.styp(utyp.set_nr_muls(utyp.nr_muls() - 1))
106 init_styp += ' = HEAP(${ptr_styp}, {0})'
107 }
108 }
109
110 dec.writeln('
111${dec_fn_dec} {
112 ${init_styp};
113 if (!root) {
114 const char *error_ptr = cJSON_GetErrorPtr();
115 if (error_ptr != NULL) {
116 const ${ast.int_type_name} error_pos = (${ast.int_type_name})cJSON_GetErrorPos();
117 ${ast.int_type_name} maxcontext_chars = 30;
118 byte *buf = builtin__vcalloc_noscan(maxcontext_chars + 10);
119 if (error_pos > 0) {
120 ${ast.int_type_name} backlines = 1;
121 ${ast.int_type_name} backchars = error_pos < maxcontext_chars-7 ? (${ast.int_type_name})error_pos : maxcontext_chars-7 ;
122 char *prevline_ptr = (char*)error_ptr;
123 while(backchars--){
124 char prevc = *(prevline_ptr - 1);
125 if(0==prevc){
126 break;
127 }
128 if(10==prevc && !backlines--){
129 break;
130 }
131 prevline_ptr--;
132 if(123==prevc) {
133 break; // stop at `{` too
134 }
135 }
136 ${ast.int_type_name} maxchars = builtin__vstrlen_char(prevline_ptr);
137 builtin__vmemcpy(buf, prevline_ptr, (maxchars < maxcontext_chars ? maxchars : maxcontext_chars));
138 }
139 string msg;
140 msg = _S("failed to decode JSON string");
141 if (buf[0] != \'\\0\') {
142 msg = builtin__string__plus(msg, _S(": "));
143 }
144 return (${result_name}_${ret_styp}){.is_error = true,.err = builtin___v_error(builtin__string__plus(msg, builtin__tos2(buf))),.data = {0}};
145 }
146 }
147')
148
149 if utyp.has_flag(.option) {
150 dec.writeln('\tif (cJSON_IsNull(root)) {')
151 dec.writeln('\t${result_name}_${ret_styp} ret;')
152 dec.writeln('\tbuiltin___result_ok(&res, (${result_name}*)&ret, sizeof(res));')
153 dec.writeln('\treturn ret;')
154 dec.writeln('\t}')
155
156 base_type := utyp.clear_flag(.option)
157 base_type_str := g.styp(base_type)
158 // Optional struct pointers need storage before field decoding writes through them.
159 base_value := if base_type.is_ptr() && sym.info is ast.Struct {
160 ptr_styp := g.styp(base_type.set_nr_muls(base_type.nr_muls() - 1))
161 'HEAP(${ptr_styp}, {0})'
162 } else {
163 g.type_default(base_type)
164 }
165 dec.writeln('\tbuiltin___option_ok(&(${base_type_str}[]){ ${base_value} }, (${styp}*)&res, sizeof(${base_type_str}));\n')
166 }
167
168 extern_str := if g.pref.parallel_cc { 'extern ' } else { '' }
169 g.json_forward_decls.writeln('${extern_str}${dec_fn_dec};')
170 // Codegen encoder
171 // encode_TYPE funcs receive an object to encode
172 enc_fn_name := js_enc_name(styp)
173 enc_fn_dec := 'cJSON* ${enc_fn_name}(${styp} val)'
174 g.json_forward_decls.writeln('${extern_str}${enc_fn_dec};\n')
175 enc.writeln('
176${enc_fn_dec} {
177\tcJSON *o;')
178 if utyp.is_ptr() && !utyp.has_flag(.option) {
179 enc.writeln('\tif (val == 0) {')
180 enc.writeln('\t\treturn cJSON_CreateNull();')
181 enc.writeln('\t}')
182 }
183 if is_js_prim(sym.name) && utyp.is_ptr() {
184 g.gen_prim_enc_dec(utyp, mut enc, mut dec)
185 } else if sym.kind in [.array, .array_fixed] {
186 array_size := if sym.kind == .array_fixed {
187 (sym.info as ast.ArrayFixed).size
188 } else {
189 -1
190 }
191 // Handle arrays
192 value_type := g.table.value_type(utyp)
193 // If we have `[]Profile`, have to register a Profile en(de)coder first
194 g.gen_json_for_type(value_type)
195 dec.writeln(g.decode_array(utyp, value_type, array_size, ret_styp))
196 enc.writeln(g.encode_array(utyp, value_type, array_size))
197 } else if sym.kind == .map {
198 // Handle maps
199 m := sym.info as ast.Map
200 g.gen_json_for_type(m.key_type)
201 g.gen_json_for_type(m.value_type)
202 dec.writeln(g.decode_map(utyp, m.key_type, m.value_type, ret_styp))
203 enc.writeln(g.encode_map(utyp, m.key_type, m.value_type))
204 } else if sym.kind == .alias {
205 a := sym.info as ast.Alias
206 parent_typ := a.parent_type
207 psym := g.table.sym(parent_typ)
208 if is_js_prim(g.styp(parent_typ)) {
209 if utyp.has_flag(.option) {
210 g.gen_json_for_type(parent_typ.set_flag(.option))
211 g.gen_option_enc_dec(parent_typ.set_flag(.option), mut enc, mut dec)
212 } else {
213 g.gen_json_for_type(parent_typ)
214 g.gen_prim_enc_dec(parent_typ, mut enc, mut dec)
215 }
216 } else if psym.info is ast.Struct {
217 enc.writeln('\to = cJSON_CreateObject();')
218 gen_struct_root_validation(ret_styp, mut dec)
219 g.gen_struct_enc_dec(utyp, psym.info, ret_styp, mut enc, mut dec, '')
220 } else if psym.kind == .enum {
221 g.gen_enum_enc_dec(utyp, psym, mut enc, mut dec)
222 } else if psym.kind == .sum_type {
223 g.json_error(utyp, 'json: ${sym.name} aliased sumtypes does not work at the moment')
224 } else if psym.kind == .map {
225 m := psym.info as ast.Map
226 g.gen_json_for_type(m.key_type)
227 g.gen_json_for_type(m.value_type)
228 dec.writeln(g.decode_map(utyp, m.key_type, m.value_type, ret_styp))
229 enc.writeln(g.encode_map(utyp, m.key_type, m.value_type))
230 } else if utyp.has_flag(.option) {
231 g.gen_option_enc_dec(utyp, mut enc, mut dec)
232 } else {
233 g.json_error(utyp, 'json: ${sym.name} is not struct')
234 }
235 } else if sym.kind == .sum_type {
236 enc.writeln('\to = cJSON_CreateObject();')
237 // Sumtypes. Range through variants of sumtype
238 if sym.info !is ast.SumType {
239 g.json_error(utyp, 'json: ${sym.name} is not a sumtype')
240 }
241 g.gen_sumtype_enc_dec(utyp, sym, mut enc, mut dec, ret_styp)
242 } else if sym.kind == .enum {
243 g.gen_enum_enc_dec(utyp, sym, mut enc, mut dec)
244 } else if utyp.has_flag(.option)
245 && (is_js_prim(g.styp(utyp.clear_flag(.option))) || sym.info !is ast.Struct) {
246 g.gen_option_enc_dec(utyp, mut enc, mut dec)
247 } else {
248 enc.writeln('\to = cJSON_CreateObject();')
249 // Structs. Range through fields
250 if sym.info !is ast.Struct {
251 g.json_error(utyp, 'json: ${sym.name} is not struct')
252 }
253 gen_struct_root_validation(ret_styp, mut dec)
254 g.gen_struct_enc_dec(utyp, sym.info, ret_styp, mut enc, mut dec, '')
255 }
256 // cJSON_delete
257 dec.writeln('\t${result_name}_${ret_styp} ret;')
258 dec.writeln('\tbuiltin___result_ok(&res, (${result_name}*)&ret, sizeof(res));')
259 dec.writeln('\treturn ret;\n}')
260 enc.writeln('\treturn o;\n}')
261 g.gowrappers.writeln(dec.str())
262 g.gowrappers.writeln(enc.str())
263 g.json_gen_pos = saved_json_gen_pos
264 }
265}
266
267@[inline]
268fn (mut g Gen) gen_enum_to_str(utyp ast.Type, sym ast.TypeSymbol, enum_var string, result_var string, ident string,
269 mut enc strings.Builder) {
270 enum_prefix := g.gen_enum_prefix(utyp.clear_flag(.option))
271 enc.writeln('${ident}switch (${enum_var}) {')
272 for val in (sym.info as ast.Enum).vals {
273 enc.write_string('${ident}\tcase ${enum_prefix}${val}:\t')
274 // read [json:] attr from the Enum value
275 attr := g.table.enum_decls[sym.name].fields.filter(it.name == val)[0].attrs.find_first('json') or {
276 ast.Attr{}
277 }
278 if attr.has_arg {
279 enc.writeln('${result_var} = json__encode_string(_S("${attr.arg}")); break;')
280 } else {
281 enc.writeln('${result_var} = json__encode_string(_S("${val}")); break;')
282 }
283 }
284 enc.writeln('${ident}}')
285}
286
287@[inline]
288fn (mut g Gen) gen_str_to_enum(utyp ast.Type, sym ast.TypeSymbol, val_var string, result_var string, ident string,
289 mut dec strings.Builder) {
290 enum_prefix := g.gen_enum_prefix(utyp.clear_flag(.option))
291 is_option := utyp.has_flag(.option)
292 for k, val in (sym.info as ast.Enum).vals {
293 // read [json:] attr from the Enum value
294 attr := g.table.enum_decls[sym.name].fields.filter(it.name == val)[0].attrs.find_first('json') or {
295 ast.Attr{}
296 }
297 if k == 0 {
298 dec.write_string('${ident}if (builtin__string__eq(_S("${val}"), ${val_var})')
299 } else {
300 dec.write_string('${ident}else if (builtin__string__eq(_S("${val}"), ${val_var})')
301 }
302 if attr.has_arg {
303 dec.write_string(' || builtin__string__eq(_S("${attr.arg}"), ${val_var})')
304 }
305 dec.write_string(')\t')
306 if is_option {
307 base_typ := g.base_type(utyp)
308 dec.writeln('builtin___option_ok(&(${base_typ}[]){ ${enum_prefix}${val} }, (${option_name}*)${result_var}, sizeof(${base_typ}));')
309 } else {
310 dec.writeln('${result_var} = ${enum_prefix}${val};')
311 }
312 }
313}
314
315@[inline]
316fn (mut g Gen) is_enum_as_int(sym ast.TypeSymbol) bool {
317 if enum_decl := g.table.enum_decls[sym.name] {
318 if _ := enum_decl.attrs.find_first('json_as_number') {
319 return true
320 }
321 }
322 return false
323}
324
325@[inline]
326fn (mut g Gen) gen_enum_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc strings.Builder, mut dec strings.Builder) {
327 is_option := utyp.has_flag(.option)
328
329 if g.is_enum_as_int(sym) {
330 if is_option {
331 base_typ := g.styp(utyp.clear_flag(.option))
332 enc.writeln('\to = ${js_enc_name('u64')}(*val.data);')
333 dec.writeln('\tbuiltin___option_ok(&(${base_typ}[]){ ${js_dec_name('u64')}(root) }, (${option_name}*)&res, sizeof(${base_typ}));')
334 } else {
335 dec.writeln('\tres = ${js_dec_name('u64')}(root);')
336 enc.writeln('\to = ${js_enc_name('u64')}(val);')
337 }
338 } else {
339 tmp := g.new_tmp_var()
340 dec.writeln('\tstring ${tmp} = ${js_dec_name('string')}(root);')
341 if is_option {
342 g.gen_str_to_enum(utyp, sym, tmp, '&res', '\t', mut dec)
343 g.gen_enum_to_str(utyp, sym, '*(${g.base_type(utyp)}*)val.data', 'o', '\t\t', mut enc)
344 } else {
345 g.gen_str_to_enum(utyp, sym, tmp, 'res', '\t', mut dec)
346 g.gen_enum_to_str(utyp, sym, 'val', 'o', '\t', mut enc)
347 }
348 }
349}
350
351@[inline]
352fn (mut g Gen) gen_prim_enc_dec(typ ast.Type, mut enc strings.Builder, mut dec strings.Builder) {
353 if typ.is_ptr() {
354 type_str := g.styp(typ.clear_flag(.option).set_nr_muls(typ.nr_muls() - 1))
355 type_str_0 := g.styp(typ.clear_flag(.option).set_nr_muls(0))
356 encode_name := js_enc_name(type_str_0)
357 dec_name := js_dec_name(type_str)
358 if typ.has_flag(.option) {
359 enc.writeln('\to = ${encode_name}(${'*'.repeat(typ.nr_muls() + 1)}(${type_str_0}${'*'.repeat(typ.nr_muls())}*)&val.data);')
360 } else {
361 enc.writeln('\to = ${encode_name}(${'*'.repeat(typ.nr_muls())}val);')
362 }
363
364 if typ.nr_muls() > 1 {
365 g.gen_json_for_type(typ.clear_flag(.option).set_nr_muls(typ.nr_muls() - 1))
366 if typ.has_flag(.option) {
367 tmp_var := g.new_tmp_var()
368 dec.writeln('${type_str}* ${tmp_var} = HEAP(${type_str}, *(${type_str}*) ${dec_name}(root).data);')
369 dec.writeln('\tbuiltin___option_ok(&(${type_str}*[]) { &(*(${tmp_var})) }, (${option_name}*)&res, sizeof(${type_str}*));')
370 } else {
371 dec.writeln('\tres = HEAP(${type_str}, *(${type_str}*) ${dec_name}(root).data);')
372 }
373 } else {
374 if typ.has_flag(.option) {
375 tmp_var := g.new_tmp_var()
376 dec.writeln('${type_str}* ${tmp_var} = HEAP(${type_str}, ${dec_name}(root));')
377 dec.writeln('\tbuiltin___option_ok(&(${type_str}*[]) { &(*(${tmp_var})) }, (${option_name}*)&res, sizeof(${type_str}*));')
378 } else {
379 dec.writeln('\tres = HEAP(${type_str}, ${dec_name}(root));')
380 }
381 }
382 } else {
383 type_str := g.styp(typ.clear_flag(.option))
384 encode_name := js_enc_name(type_str)
385 dec_name := js_dec_name(type_str)
386 enc.writeln('\to = ${encode_name}(val);')
387 dec.writeln('\tres = ${dec_name}(root);')
388 }
389}
390
391fn gen_struct_root_validation(ret_styp string, mut dec strings.Builder) {
392 dec.writeln('\tif (!cJSON_IsObject(root) && !cJSON_IsNull(root) && !cJSON_IsArray(root)) {')
393 dec.writeln('\t\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = builtin___v_error(builtin__string__plus(_S("Json element is not an object: "), json__json_print(root))), .data = {0} };')
394 dec.writeln('\t}')
395}
396
397@[inline]
398fn (mut g Gen) gen_option_enc_dec(typ ast.Type, mut enc strings.Builder, mut dec strings.Builder) {
399 enc.writeln('\tif (val.state == 2) {')
400 enc.writeln('\t\treturn NULL;')
401 enc.writeln('\t}')
402 type_str := g.styp(typ.clear_flag(.option))
403 encode_name := js_enc_name(type_str)
404 enc.writeln('\to = ${encode_name}(*(${type_str}*)val.data);')
405
406 dec_name := js_dec_name(type_str)
407 dec.writeln('\tif (!cJSON_IsNull(root)) {')
408 dec.writeln('\t\tbuiltin___option_ok(&(${type_str}[]){ ${dec_name}(root) }, (${option_name}*)&res, sizeof(${type_str}));')
409 dec.writeln('\t} else {')
410 default_init := if typ.is_int() || typ.is_float() || typ.is_bool() { '0' } else { '{0}' }
411 dec.writeln('\t\tbuiltin___option_none(&(${type_str}[]){ ${default_init} }, (${option_name}*)&res, sizeof(${type_str}));')
412 dec.writeln('\t}')
413}
414
415@[inline]
416fn (mut g Gen) gen_sumtype_variant_decode(variant_typ string, sumtype_cname string, ret_styp string,
417 prefix string, is_option bool, decoded_var string, indent string, mut dec strings.Builder) {
418 tmp := g.new_tmp_var()
419 dec.writeln('${indent}${result_name}_${variant_typ} ${tmp} = ${js_dec_name(variant_typ)}(root);')
420 dec.writeln('${indent}if (${tmp}.is_error) {')
421 dec.writeln('${indent}\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = ${tmp}.err, .data = {0} };')
422 dec.writeln('${indent}}')
423 if is_option {
424 dec.writeln('${indent}builtin___option_ok(&(${sumtype_cname}[]){ ${variant_typ}_to_sumtype_${sumtype_cname}((${variant_typ}*)${tmp}.data, false) }, (${option_name}*)&res, sizeof(${sumtype_cname}));')
425 } else {
426 dec.writeln('${indent}${prefix}res = ${variant_typ}_to_sumtype_${sumtype_cname}((${variant_typ}*)${tmp}.data, false);')
427 }
428 dec.writeln('${indent}${decoded_var} = true;')
429}
430
431@[inline]
432fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc strings.Builder, mut dec strings.Builder,
433 ret_styp string) {
434 info := sym.info as ast.SumType
435 type_var := g.new_tmp_var()
436 typ := ast.idx_to_type(g.table.type_idxs[sym.name])
437 prefix := if utyp.is_ptr() { '*' } else { '' }
438 field_op := if utyp.is_ptr() { '->' } else { '.' }
439 is_option := utyp.has_flag(.option)
440 var_data := if is_option { '(*(${g.base_type(utyp)}*)val.data)' } else { 'val' }
441 decoded_var := g.new_tmp_var()
442 dec.writeln('\tbool ${decoded_var} = false;')
443
444 // DECODING (inline)
445 $if !json_no_inline_sumtypes ? {
446 // Handle "key": null
447 // In this case the first variant must be used (something like InvalidExpr for example)
448 // An empty instance of the first variant is generated.
449 // This way the user can easily check if the sum type was not provided in json ("key":null):
450 // `if node.expr is InvalidExpr { ... }`
451 // (Do this only for structs)
452 mut null_option_variant_typ := ''
453 mut null_option_variant_count := 0
454 for variant in info.variants {
455 if variant.has_flag(.option) {
456 null_option_variant_typ = g.styp(variant)
457 null_option_variant_count++
458 }
459 }
460 type_tmp := g.new_tmp_var()
461 first_variant := info.variants[0]
462 variant_typ := g.styp(first_variant)
463 fv_sym := g.table.sym(first_variant)
464 first_variant_name := fv_sym.cname
465 // println('KIND=${fv_sym.kind}')
466 if null_option_variant_count == 1 {
467 null_option_tmp := g.new_tmp_var()
468 dec.writeln('\tif (root->type == cJSON_NULL) {')
469 dec.writeln('\t\t${result_name}_${null_option_variant_typ} ${null_option_tmp} = ${js_dec_name(null_option_variant_typ)}(root);')
470 dec.writeln('\t\tif (${null_option_tmp}.is_error) {')
471 dec.writeln('\t\t\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = ${null_option_tmp}.err, .data = {0} };')
472 dec.writeln('\t\t}')
473 if is_option {
474 dec.writeln('\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${null_option_variant_typ}_to_sumtype_${sym.cname}((${null_option_variant_typ}*)${null_option_tmp}.data, false) }, (${option_name}*)&res, sizeof(${sym.cname}));')
475 } else {
476 dec.writeln('\t\tres = ${null_option_variant_typ}_to_sumtype_${ret_styp}((${null_option_variant_typ}*)${null_option_tmp}.data, false);')
477 }
478 dec.writeln('\t\t${decoded_var} = true;')
479 dec.writeln('\t} \n else ')
480 } else if fv_sym.kind == .struct && !is_option && field_op != '->' {
481 dec.writeln('\tif (root->type == cJSON_NULL) { ')
482 dec.writeln('\t\tstruct ${first_variant_name} empty = {0};')
483 dec.writeln('\t\t${decoded_var} = true;')
484 dec.writeln('res = ${variant_typ}_to_sumtype_${ret_styp}(&empty, false); } \n else ')
485 // dec.writeln('res = ${variant_typ}_to_sumtype_${sym.cname}(&empty, false); } \n else ')
486 }
487 //
488 dec.writeln('if (cJSON_IsObject(root) || (cJSON_IsArray(root) && cJSON_IsObject(root->child))) {')
489 dec.writeln('\t\tcJSON* ${type_tmp} = cJSON_IsObject(root) ? js_get(root, "_type") : js_get(root->child, "_type");')
490 dec.writeln('\t\tif (${type_tmp} != 0) {')
491 dec.writeln('\t\t\tchar* ${type_var} = cJSON_GetStringValue(${type_tmp});')
492 // dec.writeln('\t\t\tcJSON_DeleteItemFromObjectCaseSensitive(root, "_type");')
493 }
494
495 mut variant_types := []string{}
496 mut variant_symbols := []ast.TypeSymbol{}
497 mut at_least_one_prim := false
498 mut object_variant_count := 0
499 mut fallback_object_variant_typ := ''
500 for variant in info.variants {
501 variant_typ := g.styp(variant)
502 variant_types << variant_typ
503 variant_sym := g.table.sym(variant)
504 variant_symbols << variant_sym
505 final_variant_sym := g.table.final_sym(variant)
506 at_least_one_prim = at_least_one_prim || is_js_prim(variant_typ)
507 || variant_sym.kind == .enum || variant_sym.name == 'time.Time'
508 if variant_sym.name != 'time.Time' && final_variant_sym.kind in [.struct, .map] {
509 object_variant_count++
510 if object_variant_count == 1 {
511 fallback_object_variant_typ = variant_typ
512 } else {
513 fallback_object_variant_typ = ''
514 }
515 }
516 unmangled_variant_name := variant_sym.name.split('.').last()
517
518 // TODO: Do not generate dec/enc for 'time.Time', because we handle it by saving it as u64
519 g.gen_json_for_type(variant)
520
521 // Helpers for decoding
522 g.get_sumtype_casting_fn(variant, typ)
523 g.definitions.writeln('static inline ${sym.cname} ${variant_typ}_to_sumtype_${sym.cname}(${variant_typ}* x, bool is_mut);')
524
525 // ENCODING
526 enc.writeln('\tif (${var_data}${field_op}_typ == ${g.type_sidx(variant)}) {')
527 $if json_no_inline_sumtypes ? {
528 if variant_sym.kind == .enum {
529 enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name('u64')}(*${var_data}${field_op}_${variant_typ}));')
530 } else if variant_sym.name == 'time.Time' {
531 enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name('i64')}(time__Time_unix(*${var_data}${field_op}_${variant_typ})));')
532 } else {
533 enc.writeln('\t\tcJSON_AddItemToObject(o, "${unmangled_variant_name}", ${js_enc_name(variant_typ)}(*${var_data}${field_op}_${variant_typ}));')
534 }
535 } $else {
536 if is_js_prim(variant_typ) {
537 enc.writeln('\t\tcJSON_free(o); return ${js_enc_name(variant_typ)}(*${var_data}${field_op}_${variant_typ});')
538 } else if variant_sym.kind == .enum {
539 if g.is_enum_as_int(variant_sym) {
540 enc.writeln('\t\tcJSON_free(o); return ${js_enc_name('u64')}(*${var_data}${field_op}_${variant_typ});')
541 } else {
542 enc.writeln('\t\tcJSON_free(o);')
543 tmp2 := g.new_tmp_var()
544 if utyp.has_flag(.option) {
545 enc.writeln('\t\tu64 ${tmp2} = *${var_data}${field_op}_${variant_typ};')
546 g.gen_enum_to_str(variant, variant_sym, tmp2, 'o', '\t\t', mut enc)
547 } else {
548 enc.writeln('\t\tu64 ${tmp2} = *${var_data}${field_op}_${variant_typ};')
549 g.gen_enum_to_str(variant, variant_sym, tmp2, 'o', '\t\t', mut enc)
550 }
551 }
552 } else if variant_sym.name == 'time.Time' {
553 enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("${unmangled_variant_name}"));')
554 enc.writeln('\t\tcJSON_AddItemToObject(o, "value", ${js_enc_name('i64')}(time__Time_unix(*${var_data}${field_op}_${variant_typ})));')
555 } else {
556 encoded_variant := g.new_tmp_var()
557 enc.writeln('\t\tcJSON* ${encoded_variant} = ${js_enc_name(variant_typ)}(*${var_data}${field_op}_${variant_typ});')
558 if variant.has_flag(.option) {
559 enc.writeln('\t\tif (${encoded_variant} != NULL) {')
560 enc.writeln('\t\t\tcJSON_free(o);')
561 enc.writeln('\t\t\to = ${encoded_variant};')
562 if variant_sym.kind == .array {
563 enc.writeln('\t\t\tif (cJSON_IsObject(o->child)) {')
564 enc.writeln('\t\t\t\tcJSON_AddItemToObject(o->child, "_type", cJSON_CreateString("${unmangled_variant_name}"));')
565 enc.writeln('\t\t\t}')
566 } else {
567 enc.writeln('\t\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("${unmangled_variant_name}"));')
568 }
569 enc.writeln('\t\t}')
570 } else {
571 enc.writeln('\t\tcJSON_free(o);')
572 enc.writeln('\t\to = ${encoded_variant};')
573 if variant_sym.kind == .array {
574 enc.writeln('\t\tif (cJSON_IsObject(o->child)) {')
575 enc.writeln('\t\t\tcJSON_AddItemToObject(o->child, "_type", cJSON_CreateString("${unmangled_variant_name}"));')
576 enc.writeln('\t\t}')
577 } else {
578 enc.writeln('\t\tcJSON_AddItemToObject(o, "_type", cJSON_CreateString("${unmangled_variant_name}"));')
579 }
580 }
581 }
582 }
583 enc.writeln('\t}')
584
585 // DECODING
586 tmp := g.new_tmp_var()
587 $if json_no_inline_sumtypes ? {
588 dec.writeln('\tif (strcmp("${unmangled_variant_name}", root->child->string) == 0) {')
589 if is_js_prim(variant_typ) {
590 gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true)
591 dec.writeln('\t\t${variant_typ} value = ${js_dec_name(variant_typ)}(jsonroot_${tmp});')
592 } else if variant_sym.kind == .enum {
593 if g.is_enum_as_int(variant_sym) {
594 gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true)
595 dec.writeln('\t\t${variant_typ} value = ${js_dec_name('u64')}(jsonroot_${tmp});')
596 } else {
597 gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true)
598 dec.writeln('\t\t${variant_typ} value;')
599 tmp2 := g.new_tmp_var()
600 dec.writeln('\t\tstring ${tmp2} = json__decode_string(jsonroot_${tmp});')
601 g.gen_enum_to_str(variant, variant_sym, tmp2, 'value', '\t\t', mut dec)
602 }
603 } else if variant_sym.name == 'time.Time' {
604 gen_js_get(ret_styp, tmp, unmangled_variant_name, mut dec, true)
605 dec.writeln('\t\t${variant_typ} value = time__unix(${js_dec_name('i64')}(jsonroot_${tmp}));')
606 } else {
607 gen_js_get_opt(js_dec_name(variant_typ), variant_typ, ret_styp, tmp,
608 unmangled_variant_name, mut dec, true)
609 dec.writeln('\t\t${variant_typ} value = *(${variant_typ}*)(${tmp}.data);')
610 }
611 if is_option {
612 dec.writeln('\t\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${variant_typ}_to_sumtype_${sym.cname}(&value, false) }, (${option_name}*)&res, sizeof(${sym.cname}));')
613 } else {
614 dec.writeln('\t\tres = ${variant_typ}_to_sumtype_${ret_styp}(&value, false);')
615 }
616 dec.writeln('\t\t${decoded_var} = true;')
617 dec.writeln('\t}')
618 } $else {
619 if variant_sym.name == 'time.Time' {
620 dec.writeln('\t\t\tif (strcmp("Time", ${type_var}) == 0) {')
621 gen_js_get(ret_styp, tmp, 'value', mut dec, true)
622 dec.writeln('\t\t\t\t${variant_typ} ${tmp} = time__unix(${js_dec_name('i64')}(jsonroot_${tmp}));')
623 if utyp.has_flag(.option) {
624 dec.writeln('\t\t\t\t${prefix}res.state = 0;')
625 tmp_time_var := g.new_tmp_var()
626 dec.writeln('\t\t\t\t${g.base_type(utyp)} ${tmp_time_var} = ${variant_typ}_to_sumtype_${sym.cname}(&${tmp}, false);')
627 dec.writeln('\t\t\t\tbuiltin__vmemcpy(&${prefix}res.data, ${tmp_time_var}._time__Time, sizeof(${variant_typ}));')
628 } else {
629 dec.writeln('\t\t\t\t${prefix}res = ${variant_typ}_to_sumtype_${sym.cname}(&${tmp}, false);')
630 }
631 dec.writeln('\t\t\t\t${decoded_var} = true;')
632 dec.writeln('\t\t\t}')
633 } else if !is_js_prim(variant_typ) && variant_sym.kind != .enum {
634 dec.writeln('\t\t\tif (strcmp("${unmangled_variant_name}", ${type_var}) == 0 && ${variant_sym.kind == .array} == cJSON_IsArray(root)) {')
635 g.gen_sumtype_variant_decode(variant_typ, sym.cname, ret_styp, prefix, is_option,
636 decoded_var, '\t\t\t\t', mut dec)
637 dec.writeln('\t\t\t}')
638 }
639 }
640 }
641
642 // DECODING (inline)
643 $if !json_no_inline_sumtypes ? {
644 if object_variant_count == 1 && fallback_object_variant_typ != '' {
645 dec.writeln('\t\t} else if (cJSON_IsObject(root)) {')
646 g.gen_sumtype_variant_decode(fallback_object_variant_typ, sym.cname, ret_styp, prefix,
647 is_option, decoded_var, '\t\t\t', mut dec)
648 }
649 dec.writeln('\t\t}')
650
651 mut number_is_met := false
652 mut string_is_met := false
653 mut last_number_type := ''
654
655 if at_least_one_prim {
656 dec.writeln('\t} else {')
657
658 if 'bool' in variant_types {
659 var_t := 'bool'
660 dec.writeln('\t\tif (cJSON_IsBool(root)) {')
661 dec.writeln('\t\t\t${var_t} value = ${js_dec_name(var_t)}(root);')
662 dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value, false);')
663 dec.writeln('\t\t\t${decoded_var} = true;')
664 dec.writeln('\t\t}')
665 }
666
667 for i, var_t in variant_types {
668 if variant_symbols[i].kind == .enum {
669 if number_is_met {
670 var_num := var_t.replace('__', '.')
671 last_num := last_number_type.replace('__', '.')
672 verror_suggest_json_no_inline_sumtypes(sym.name, last_num, var_num)
673 }
674 number_is_met = true
675 last_number_type = var_t
676 dec.writeln('\t\tif (cJSON_IsNumber(root)) {')
677 dec.writeln('\t\t\t${var_t} value = ${js_dec_name('u64')}(root);')
678 if utyp.has_flag(.option) {
679 dec.writeln('\t\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${var_t}_to_sumtype_${sym.cname}(&value, false) }, (${option_name}*)&${prefix}res, sizeof(${sym.cname}));')
680 } else {
681 dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value, false);')
682 }
683 dec.writeln('\t\t\t${decoded_var} = true;')
684 dec.writeln('\t\t}')
685 }
686
687 if var_t in ['string', 'rune'] {
688 if string_is_met {
689 var_num := var_t.replace('__', '.')
690 verror_suggest_json_no_inline_sumtypes(sym.name, 'string', var_num)
691 }
692 string_is_met = true
693 dec.writeln('\t\tif (cJSON_IsString(root)) {')
694 dec.writeln('\t\t\t${var_t} value = ${js_dec_name(var_t)}(root);')
695 if utyp.has_flag(.option) {
696 dec.writeln('\t\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${var_t}_to_sumtype_${sym.cname}(&value, false) }, (${option_name}*)&${prefix}res, sizeof(${sym.cname}));')
697 } else {
698 dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value, false);')
699 }
700 dec.writeln('\t\t\t${decoded_var} = true;')
701 dec.writeln('\t\t}')
702 }
703
704 if var_t.starts_with('Array_') {
705 tmp := g.new_tmp_var()
706 judge_elem_typ := if var_t.ends_with('string') {
707 'cJSON_IsString(root->child)'
708 } else if var_t.ends_with('bool') {
709 'cJSON_IsBool(root->child)'
710 } else if g.table.sym(g.table.value_type(ast.idx_to_type(variant_symbols[i].idx))).kind == .struct {
711 'cJSON_IsObject(root->child)'
712 } else {
713 'cJSON_IsNumber(root->child)'
714 }
715 dec.writeln('\t\tif (cJSON_IsArray(root) && ${judge_elem_typ}) {')
716 dec.writeln('\t\t\t${result_name}_${var_t} ${tmp} = ${js_dec_name(var_t)}(root);')
717 dec.writeln('\t\t\tif (${tmp}.is_error) {')
718 dec.writeln('\t\t\t\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = ${tmp}.err, .data = {0} };')
719 dec.writeln('\t\t\t}')
720 if utyp.has_flag(.option) {
721 dec.writeln('\t\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${var_t}_to_sumtype_${sym.cname}((${var_t}*)${tmp}.data, false) }, (${option_name}*)&${prefix}res, sizeof(${sym.cname}));')
722 } else {
723 dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}((${var_t}*)${tmp}.data, false);')
724 }
725 dec.writeln('\t\t\t${decoded_var} = true;')
726 dec.writeln('\t\t}')
727 }
728
729 if var_t in ['i8', 'i16', 'i32', 'i64', ast.int_type_name, 'int', 'u8', 'u16',
730 'u32', 'u64', 'byte', 'rune', 'f64', 'f32'] {
731 if number_is_met {
732 var_num := var_t.replace('__', '.')
733 last_num := last_number_type.replace('__', '.')
734 verror_suggest_json_no_inline_sumtypes(sym.name, last_num, var_num)
735 }
736 number_is_met = true
737 last_number_type = var_t
738 dec.writeln('\t\tif (cJSON_IsNumber(root)) {')
739 dec.writeln('\t\t\t${var_t} value = ${js_dec_name(var_t)}(root);')
740 if utyp.has_flag(.option) {
741 dec.writeln('\t\t\tbuiltin___option_ok(&(${sym.cname}[]){ ${var_t}_to_sumtype_${sym.cname}(&value, false) }, (${option_name}*)&${prefix}res, sizeof(${sym.cname}));')
742 } else {
743 dec.writeln('\t\t\t${prefix}res = ${var_t}_to_sumtype_${sym.cname}(&value, false);')
744 }
745 dec.writeln('\t\t\t${decoded_var} = true;')
746 dec.writeln('\t\t}')
747 }
748 }
749 }
750 dec.writeln('\t}')
751 }
752 dec.writeln('\tif (!${decoded_var}) {')
753 dec.writeln('\t\treturn (${result_name}_${ret_styp}){ .is_error = true, .err = builtin___v_error(builtin__string__plus(_S("cannot decode `${sym.name}` sum type from JSON value: "), json__json_print(root))), .data = {0} };')
754 dec.writeln('\t}')
755}
756
757fn (mut g Gen) gen_prim_type_validation(name string, typ ast.Type, tmp string, is_required bool, ret_styp string, mut dec strings.Builder) {
758 none_check := if !is_required { 'cJSON_IsNull(jsonroot_${tmp}) || ' } else { '' }
759 type_check := if typ.is_int() || typ.is_float() {
760 '${none_check}cJSON_IsNumber(jsonroot_${tmp}) || (cJSON_IsString(jsonroot_${tmp}) && strlen(jsonroot_${tmp}->valuestring))'
761 } else if typ.is_string() {
762 '${none_check}(cJSON_IsString(jsonroot_${tmp}) || cJSON_IsObject(jsonroot_${tmp}) || cJSON_IsArray(jsonroot_${tmp}))'
763 } else if typ.is_bool() {
764 '${none_check}cJSON_IsBool(jsonroot_${tmp})'
765 } else {
766 ''
767 }
768 if type_check == '' {
769 return
770 }
771 dec.writeln('\t\tif (!(${type_check})) {')
772 dec.writeln('\t\t\treturn (${ret_styp}){ .is_error = true, .err = builtin___v_error(builtin__string__plus(_S("type mismatch for field \'${name}\', expecting `${g.table.type_to_str(typ)}` type, got: "), json__json_print(jsonroot_${tmp}))), .data = {0} };')
773 dec.writeln('\t\t}')
774}
775
776@[inline]
777fn (mut g Gen) gen_struct_enc_dec(utyp ast.Type, type_info ast.TypeInfo, styp string, mut enc strings.Builder,
778 mut dec strings.Builder, embed_prefix string) {
779 info := type_info as ast.Struct
780 for field in info.fields {
781 mut name := field.name
782 mut is_raw := false
783 mut is_skip := false
784 mut is_required := false
785 mut is_omit_empty := false
786 mut skip_embed := false
787 mut is_json_null := false
788
789 for attr in field.attrs {
790 match attr.name {
791 'json' {
792 if attr.arg == '-' {
793 // [json:'-']
794 is_skip = true
795 } else {
796 name = attr.arg
797 }
798 }
799 'skip' {
800 is_skip = true
801 }
802 'raw' {
803 is_raw = true
804 }
805 'required' {
806 is_required = true
807 }
808 'omitempty' {
809 is_omit_empty = true
810 }
811 'json_null' {
812 is_json_null = true
813 }
814 else {}
815 }
816 }
817 if is_skip {
818 continue
819 }
820 field_type := vint2int(g.styp(field.typ))
821 field_sym := g.table.sym(field.typ)
822 op := if utyp.is_ptr() { '->' } else { '.' }
823 embed_member := if embed_prefix.len > 0 { '.${embed_prefix}' } else { '' }
824 prefix := if utyp.has_flag(.option) {
825 '(*(${g.base_type(utyp)}*)res${embed_member}.data)'
826 } else {
827 'res${embed_member}'
828 }
829 // First generate decoding
830 if is_raw {
831 if field.typ.has_flag(.option) {
832 g.gen_json_for_type(field.typ)
833 base_typ := g.base_type(field.typ)
834 dec.writeln('\tif (js_get(root, "${name}") == NULL)')
835 default_init := if field.typ.is_int() || field.typ.is_float() || field.typ.is_bool() {
836 '0'
837 } else {
838 '{0}'
839 }
840 dec.writeln('\t\tbuiltin___option_none(&(${base_typ}[]) { ${default_init} }, (${option_name}*)&${prefix}${op}${c_name(field.name)}, sizeof(${base_typ}));')
841 dec.writeln('\telse')
842 dec.writeln('\t\tbuiltin___option_ok(&(${base_typ}[]) { json__json_print(js_get(root, "${name}")) }, (${option_name}*)&${prefix}${op}${c_name(field.name)}, sizeof(${base_typ}));')
843 } else {
844 dec.writeln('\t${prefix}${op}${c_name(field.name)} = json__json_print(js_get(root, "${name}"));')
845 }
846 } else {
847 // Now generate decoders for all field types in this struct
848 // need to do it here so that these functions are generated first
849 g.gen_json_for_type(field.typ)
850 dec_name := js_dec_name(field_type)
851 if is_js_prim(field_type) {
852 tmp := g.new_tmp_var()
853 gen_js_get(styp, tmp, name, mut dec, is_required)
854 dec.writeln('\tif (jsonroot_${tmp}) {')
855 if utyp.has_flag(.option) {
856 dec.writeln('\t\tres.state = 0;')
857 }
858 g.gen_prim_type_validation(field.name, field.typ, tmp, is_required,
859 '${result_name}_${styp}', mut dec)
860 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${dec_name}(jsonroot_${tmp});')
861 if field.has_default_expr {
862 dec.writeln('\t} else {')
863 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${g.expr_string_opt(field.typ,
864 field.default_expr)};')
865 }
866 dec.writeln('\t}')
867 } else if field_sym.kind == .enum {
868 tmp := g.new_tmp_var()
869 is_option_field := field.typ.has_flag(.option)
870 if field.typ.has_flag(.option) {
871 gen_js_get_opt(js_dec_name(field_type), field_type, styp, tmp, name, mut dec,
872 is_required)
873 dec.writeln('\tif (jsonroot_${tmp} && !cJSON_IsNull(jsonroot_${tmp})) {')
874 } else {
875 gen_js_get(styp, tmp, name, mut dec, is_required)
876 dec.writeln('\tif (jsonroot_${tmp}) {')
877 }
878 if g.is_enum_as_int(field_sym) {
879 if is_option_field {
880 base_typ := g.base_type(field.typ)
881 dec.writeln('\t\tbuiltin___option_ok(&(${base_typ}[]) { ${js_dec_name('u64')}(jsonroot_${tmp}) }, (${option_name}*)&${prefix}${op}${c_name(field.name)}, sizeof(${base_typ}));')
882 } else {
883 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${js_dec_name('u64')}(jsonroot_${tmp});')
884 }
885 } else {
886 if is_option_field {
887 base_typ := g.base_type(field.typ)
888 dec.writeln('\t\tbuiltin___option_ok(&(${base_typ}[]) { *(${base_typ}*)((${g.styp(field.typ)}*)${tmp}.data)->data }, (${option_name}*)&${prefix}${op}${c_name(field.name)}, sizeof(${base_typ}));')
889 } else {
890 tmp2 := g.new_tmp_var()
891 dec.writeln('\t\tstring ${tmp2} = json__decode_string(jsonroot_${tmp});')
892 g.gen_str_to_enum(field.typ, field_sym, tmp2,
893 '${prefix}${op}${c_name(field.name)}', '\t\t', mut dec)
894 }
895 }
896 if field.has_default_expr {
897 dec.writeln('\t} else {')
898 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${g.expr_string_opt(field.typ,
899 field.default_expr)};')
900 }
901 dec.writeln('\t}')
902 } else if field_sym.name == 'time.Time' {
903 // time struct requires special treatment
904 // it can be decoded from either a JSON string or number
905 tmp := g.new_tmp_var()
906 gen_js_get(styp, tmp, name, mut dec, is_required)
907 dec.writeln('\tif (jsonroot_${tmp}) {')
908 tmp_time_res := g.new_tmp_var()
909 dec.writeln('\t\t${result_name}_time__Time ${tmp_time_res} = json__decode_time(jsonroot_${tmp});')
910 dec.writeln('\t\tif (${tmp_time_res}.is_error) {')
911 dec.writeln('\t\t\treturn (${result_name}_${styp}){ .is_error = true, .err = ${tmp_time_res}.err, .data = {0} };')
912 dec.writeln('\t\t}')
913 if field.typ.has_flag(.option) {
914 dec.writeln('\t\tif (!(cJSON_IsNull(jsonroot_${tmp}))) {')
915 dec.writeln('\t\t\t${prefix}${op}${c_name(field.name)}.state = 0;')
916 tmp_time_var := g.new_tmp_var()
917 dec.writeln('\t\t\t${g.base_type(field.typ)} ${tmp_time_var} = *(time__Time*)${tmp_time_res}.data;')
918 dec.writeln('\t\t\tbuiltin__vmemcpy(&${prefix}${op}${c_name(field.name)}.data, &${tmp_time_var}, sizeof(${g.base_type(field.typ)}));')
919 dec.writeln('\t\t}')
920 } else {
921 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = *(time__Time*)${tmp_time_res}.data;')
922 if field.has_default_expr {
923 dec.writeln('\t} else {')
924 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${g.expr_string_opt(field.typ,
925 field.default_expr)};')
926 }
927 }
928 dec.writeln('\t}')
929 } else if field_sym.kind == .alias {
930 alias := field_sym.info as ast.Alias
931 parent_type := if field.typ.has_flag(.option) {
932 alias.parent_type.set_flag(.option)
933 } else {
934 alias.parent_type
935 }
936 sparent_type := g.styp(parent_type)
937 parent_dec_name := js_dec_name(sparent_type)
938 if is_js_prim(sparent_type) {
939 tmp := g.new_tmp_var()
940 gen_js_get(styp, tmp, name, mut dec, is_required)
941 dec.writeln('\tif (jsonroot_${tmp}) {')
942 g.gen_prim_type_validation(field.name, parent_type, tmp, is_required,
943 '${result_name}_${styp}', mut dec)
944 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${parent_dec_name} (jsonroot_${tmp});')
945 if field.has_default_expr {
946 dec.writeln('\t} else {')
947 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${g.expr_string_opt(field.typ,
948 field.default_expr)};')
949 }
950 dec.writeln('\t}')
951 } else {
952 g.gen_json_for_type(parent_type)
953 tmp := g.new_tmp_var()
954 gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required)
955 dec.writeln('\tif (jsonroot_${tmp}) {')
956 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = *(${field_type}*) ${tmp}.data;')
957 if field.has_default_expr {
958 dec.writeln('\t} else {')
959 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${g.expr_string_opt(field.typ,
960 field.default_expr)};')
961 }
962 dec.writeln('\t}')
963 }
964 } else {
965 // embeded
966 if field.is_embed && field_sym.info is ast.Struct {
967 for embed in info.embeds {
968 if embed == int(field.typ) {
969 prefix_embed := if embed_prefix != '' {
970 '${embed_prefix}.${name}'
971 } else {
972 name
973 }
974 g.gen_struct_enc_dec(field.typ, g.table.sym(field.typ).info, styp, mut
975 enc, mut dec, prefix_embed)
976 skip_embed = true
977 break
978 }
979 }
980 }
981 tmp := g.new_tmp_var()
982 gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required)
983 dec.writeln('\tif (jsonroot_${tmp}) {')
984 if is_js_prim(g.styp(field.typ.clear_option_and_result())) {
985 g.gen_prim_type_validation(field.name, field.typ, tmp, is_required,
986 '${result_name}_${styp}', mut dec)
987 }
988 if field.typ.has_flag(.option) {
989 dec.writeln('\t\tbuiltin__vmemcpy(&${prefix}${op}${c_name(field.name)}, (${field_type}*)${tmp}.data, sizeof(${field_type}));')
990 } else {
991 if field_sym.kind == .array_fixed {
992 dec.writeln('\t\tbuiltin__vmemcpy(${prefix}${op}${c_name(field.name)},*(${field_type}*)${tmp}.data,sizeof(${field_type}));')
993 } else {
994 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = *(${field_type}*) ${tmp}.data;')
995 }
996 }
997 if field.has_default_expr {
998 dec.writeln('\t} else {')
999 default_str := g.expr_string_opt(field.typ, field.default_expr)
1000 if default_str.count(';\n') > 1 {
1001 dec.writeln(default_str.all_before_last('\n'))
1002 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${default_str.all_after_last('\n')};')
1003 } else {
1004 dec.writeln('\t\t${prefix}${op}${c_name(field.name)} = ${default_str};')
1005 }
1006 }
1007 dec.writeln('\t}')
1008 }
1009 }
1010 if skip_embed {
1011 continue
1012 }
1013 // Encoding
1014 mut enc_name := js_enc_name(field_type)
1015 prefix_enc := if utyp.has_flag(.option) {
1016 '(*(${g.base_type(utyp)}*)val${embed_member}.data)'
1017 } else {
1018 'val${embed_member}'
1019 }
1020 is_option := field.typ.has_flag(.option)
1021 indent := if is_option { '\t\t' } else { '\t' }
1022 if is_option {
1023 enc.writeln('\tif (${prefix_enc}${op}${c_name(field.name)}.state != 2) {')
1024 }
1025 if is_omit_empty {
1026 if field.typ.has_flag(.option) {
1027 enc.writeln('${indent}if (${prefix_enc}${op}${c_name(field.name)}.state != 2)')
1028 } else if field.typ == ast.string_type {
1029 enc.writeln('${indent}if (${prefix_enc}${op}${c_name(field.name)}.len != 0)')
1030 } else {
1031 if !field.typ.is_ptr()
1032 && field_sym.kind in [.alias, .sum_type, .map, .array, .struct] {
1033 ptr_typ := g.equality_fn(field.typ)
1034 if field_sym.kind == .alias {
1035 enc.writeln('${indent}if (!${ptr_typ}_alias_eq(${prefix_enc}${op}${c_name(field.name)}, ${g.type_default(field.typ)}))')
1036 } else if field_sym.kind == .sum_type {
1037 enc.writeln('${indent}if (${prefix_enc}${op}${c_name(field.name)}._typ != 0)')
1038 } else if field_sym.kind == .map {
1039 enc.writeln('${indent}if (!${ptr_typ}_map_eq(${prefix_enc}${op}${c_name(field.name)}, ${g.type_default(field.typ)}))')
1040 } else if field_sym.kind == .array {
1041 enc.writeln('${indent}if (!${ptr_typ}_arr_eq(${prefix_enc}${op}${c_name(field.name)}, ${g.type_default(field.typ)}))')
1042 } else if field_sym.kind == .struct {
1043 enc.writeln('${indent}if (!${ptr_typ}_struct_eq(${prefix_enc}${op}${c_name(field.name)}, ${g.type_default(field.typ)}))')
1044 }
1045 } else {
1046 enc.writeln('${indent}if (${prefix_enc}${op}${c_name(field.name)} != ${g.type_default(field.typ)})')
1047 }
1048 }
1049 }
1050 if !is_js_prim(field_type) {
1051 if field_sym.kind == .alias {
1052 ainfo := field_sym.info as ast.Alias
1053 if field.typ.has_flag(.option) {
1054 enc_name = js_enc_name(g.styp(ainfo.parent_type.set_flag(.option)))
1055 } else {
1056 enc_name = js_enc_name(g.styp(ainfo.parent_type))
1057 }
1058 }
1059 }
1060 if field_sym.kind == .enum {
1061 if g.is_enum_as_int(field_sym) {
1062 if field.typ.has_flag(.option) {
1063 enc.writeln('${indent}\tcJSON_AddItemToObject(o, "${name}", json__encode_u64(*${prefix_enc}${op}${c_name(field.name)}.data));\n')
1064 } else {
1065 enc.writeln('${indent}\tcJSON_AddItemToObject(o, "${name}", json__encode_u64(${prefix_enc}${op}${c_name(field.name)}));\n')
1066 }
1067 } else {
1068 if field.typ.has_flag(.option) {
1069 enc.writeln('${indent}\t{')
1070 enc.writeln('${indent}\t\tcJSON *enum_val;')
1071 g.gen_enum_to_str(field.typ, field_sym,
1072 '*(${g.base_type(field.typ)}*)${prefix_enc}${op}${c_name(field.name)}.data',
1073 'enum_val', '${indent}\t\t', mut enc)
1074 enc.writeln('${indent}\t\tcJSON_AddItemToObject(o, "${name}", enum_val);')
1075 enc.writeln('${indent}\t}')
1076 } else {
1077 enc.writeln('${indent}\t{')
1078 enc.writeln('${indent}\t\tcJSON *enum_val;')
1079 g.gen_enum_to_str(field.typ, field_sym,
1080 '${prefix_enc}${op}${c_name(field.name)}', 'enum_val', '${indent}\t\t', mut
1081 enc)
1082 enc.writeln('${indent}\t\tcJSON_AddItemToObject(o, "${name}", enum_val);')
1083 enc.writeln('${indent}\t}')
1084 }
1085 }
1086 } else {
1087 if field_sym.name == 'time.Time' {
1088 // time struct requires special treatment
1089 // it has to be encoded as a unix timestamp number
1090 if is_option {
1091 enc.writeln('${indent}cJSON_AddItemToObject(o, "${name}", json__encode_u64(time__Time_unix(*(${g.base_type(field.typ)}*)(${prefix_enc}${op}${c_name(field.name)}.data))));')
1092 } else {
1093 enc.writeln('${indent}cJSON_AddItemToObject(o, "${name}", json__encode_u64(time__Time_unix(${prefix_enc}${op}${c_name(field.name)})));')
1094 }
1095 } else {
1096 if !field.typ.is_any_kind_of_pointer() {
1097 if field_sym.kind == .alias && field.typ.has_flag(.option) {
1098 parent_type := g.table.unaliased_type(field.typ).set_flag(.option)
1099 enc.writeln('${indent}\tcJSON_AddItemToObject(o, "${name}", ${enc_name}(*(${g.styp(parent_type)}*)&${prefix_enc}${op}${c_name(field.name)}));')
1100 } else {
1101 enc.writeln('${indent}\tcJSON_AddItemToObject(o, "${name}", ${enc_name}(${prefix_enc}${op}${c_name(field.name)}));')
1102 }
1103 } else {
1104 arg_prefix := if field.typ.is_ptr() { '' } else { '*' }
1105 sptr_value := '${prefix_enc}${op}${c_name(field.name)}'
1106 if !field.typ.has_flag(.option) {
1107 enc.writeln('${indent}if (${sptr_value} != 0) {')
1108 enc.writeln('${indent}\tcJSON_AddItemToObject(o, "${name}", ${enc_name}(${arg_prefix}${sptr_value}));')
1109 enc.writeln('${indent}}\n')
1110 } else {
1111 enc.writeln('${indent}cJSON_AddItemToObject(o, "${name}", ${enc_name}(${arg_prefix}${sptr_value}));')
1112 }
1113 }
1114 }
1115 }
1116
1117 if is_option {
1118 if is_json_null {
1119 enc.writeln('\t} else {')
1120 enc.writeln('\t\tcJSON_AddItemToObject(o, "${name}", cJSON_CreateNull());')
1121 }
1122 enc.writeln('\t}')
1123 }
1124 }
1125}
1126
1127fn gen_js_get(styp string, tmp string, name string, mut dec strings.Builder, is_required bool) {
1128 dec.writeln('\tcJSON *jsonroot_${tmp} = js_get(root, "${name}");')
1129 if is_required {
1130 dec.writeln('\tif (jsonroot_${tmp} == 0) {')
1131 dec.writeln('\t\treturn (${result_name}_${styp}){ .is_error = true, .err = builtin___v_error(_S("expected field \'${name}\' is missing")), .data = {0} };')
1132 dec.writeln('\t}')
1133 }
1134}
1135
1136fn gen_js_get_opt(dec_name string, field_type string, styp string, tmp string, name string, mut dec strings.Builder,
1137 is_required bool) {
1138 gen_js_get(styp, tmp, name, mut dec, is_required)
1139 value_field_type := field_type.replace('*', '_ptr')
1140 dec.writeln('\t${result_name}_${value_field_type.replace('*', '_ptr')} ${tmp} = {0};')
1141 dec.writeln('\tif (jsonroot_${tmp}) {')
1142 // dec.writeln('\t\tif (jsonroot_${tmp}->type == cJSON_NULL) { puts("${name} IS JSON_NULL"); }')
1143 dec.writeln('\t\t${tmp} = ${dec_name}(jsonroot_${tmp});')
1144 dec.writeln('\t\tif (${tmp}.is_error) {')
1145 dec.writeln('\t\t\treturn (${result_name}_${styp}){ .is_error = true, .err = ${tmp}.err, .data = {0} };')
1146 dec.writeln('\t\t}')
1147 dec.writeln('\t}')
1148}
1149
1150fn js_enc_name(typ string) string {
1151 mut suffix := typ.replace('*', '_ptr')
1152 if typ == 'i32' {
1153 suffix = typ.replace('i32', 'int')
1154 }
1155 suffix = vint2int(suffix)
1156 name := 'json__encode_${suffix}'
1157 return util.no_dots(name)
1158}
1159
1160fn js_dec_name(typ string) string {
1161 mut suffix := typ.replace('*', '_ptr')
1162 if typ == 'i32' {
1163 suffix = typ.replace('i32', 'int')
1164 }
1165 suffix = vint2int(suffix)
1166 name := 'json__decode_${suffix}'
1167 return util.no_dots(name)
1168}
1169
1170fn is_js_prim(typ string) bool {
1171 return typ in [ast.int_type_name, 'int', 'rune', 'string', 'bool', 'f32', 'f64', 'i8', 'i16',
1172 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'byte']
1173}
1174
1175fn (mut g Gen) decode_array(utyp ast.Type, value_type ast.Type, fixed_array_size int, ret_styp string) string {
1176 styp := g.styp(value_type)
1177 fn_name := js_dec_name(styp)
1178 noscan := g.check_noscan(value_type)
1179
1180 mut res_str := ''
1181 mut array_free_str := ''
1182 mut fixed_array_idx := ''
1183 mut fixed_array_idx_increment := ''
1184 mut array_element_assign := ''
1185 is_array_fixed_val := g.table.final_sym(value_type).kind == .array_fixed
1186 if utyp.has_flag(.option) {
1187 if fixed_array_size > -1 {
1188 fixed_array_idx += '${ast.int_type_name} fixed_array_idx = 0;'
1189 array_element_assign += '((${styp}*)res.data)[fixed_array_idx] = val;'
1190 fixed_array_idx_increment += 'fixed_array_idx++; res.state = 0;'
1191 } else {
1192 array_element_assign += 'builtin__array_push${noscan}((array*)&res.data, &val);'
1193 res_str += 'builtin___option_ok(&(${g.base_type(utyp)}[]) { builtin____new_array${noscan}(0, 0, sizeof(${styp})) }, (${option_name}*)&res, sizeof(${g.base_type(utyp)}));'
1194 array_free_str += 'builtin__array_free(&res.data);'
1195 }
1196 } else {
1197 if is_array_fixed_val {
1198 fixed_array_idx += '${ast.int_type_name} fixed_array_idx = 0;'
1199 array_element_assign += 'memcpy(res[fixed_array_idx], val, sizeof(${styp}));'
1200 fixed_array_idx_increment += 'fixed_array_idx++;'
1201 } else if fixed_array_size > -1 {
1202 fixed_array_idx += '${ast.int_type_name} fixed_array_idx = 0;'
1203 array_element_assign += 'res[fixed_array_idx] = val;'
1204 fixed_array_idx_increment += 'fixed_array_idx++;'
1205 } else {
1206 array_element_assign += 'builtin__array_push${noscan}((array*)&res, &val);'
1207 res_str += 'res = builtin____new_array${noscan}(0, 0, sizeof(${styp}));'
1208 array_free_str += 'builtin__array_free(&res);'
1209 }
1210 }
1211
1212 mut s := ''
1213 if is_js_prim(styp) {
1214 s = '${styp} val = ${fn_name}((cJSON *)jsval); '
1215 } else if is_array_fixed_val {
1216 s = '
1217 ${result_name}_${styp.replace('*', '_ptr')} val2 = ${fn_name} ((cJSON *)jsval);
1218 if(val2.is_error) {
1219 ${array_free_str}
1220 return *(${result_name}_${ret_styp}*)&val2;
1221 }
1222 ${styp} val;
1223 memcpy(&val, (${styp}*)val2.data, sizeof(${styp}));'
1224 } else {
1225 s = '
1226 ${result_name}_${styp.replace('*', '_ptr')} val2 = ${fn_name} ((cJSON *)jsval);
1227 if(val2.is_error) {
1228 ${array_free_str}
1229 return *(${result_name}_${ret_styp}*)&val2;
1230 }
1231 ${styp} val = *(${styp}*)val2.data;
1232'
1233 }
1234
1235 return '
1236 if(root && !cJSON_IsArray(root) && !cJSON_IsNull(root)) {
1237 return (${result_name}_${ret_styp}){.is_error = true, .err = builtin___v_error(builtin__string__plus(_S("Json element is not an array: "), json__json_print(root))), .data = {0}};
1238 }
1239 ${res_str}
1240 const cJSON *jsval = NULL;
1241 ${fixed_array_idx}
1242 cJSON_ArrayForEach(jsval, root)
1243 {
1244 ${s}
1245 ${array_element_assign}
1246 ${fixed_array_idx_increment}
1247 }
1248'
1249}
1250
1251fn (mut g Gen) encode_array(utyp ast.Type, value_type ast.Type, fixed_array_size int) string {
1252 styp := g.styp(value_type)
1253 fn_name := js_enc_name(styp)
1254
1255 mut data_str := ''
1256 mut size_str := ''
1257
1258 if utyp.has_flag(.option) {
1259 data_str, size_str = if fixed_array_size > -1 {
1260 // fixed array
1261 '(${styp}*)(*(${g.base_type(utyp)}*)val.data)', '${fixed_array_size}'
1262 } else {
1263 '(${styp}*)(*(${g.base_type(utyp)}*)val.data).data', '(*(${g.base_type(utyp)}*)val.data).len'
1264 }
1265 } else {
1266 data_str, size_str = if fixed_array_size > -1 {
1267 // fixed array
1268 '(${styp}*)val', '${fixed_array_size}'
1269 } else {
1270 '(${styp}*)val.data', 'val.len'
1271 }
1272 }
1273
1274 return '
1275 o = cJSON_CreateArray();
1276 for (${ast.int_type_name} i = 0; i < ${size_str}; i++){
1277 cJSON_AddItemToArray(o, ${fn_name}( (${data_str})[i] ));
1278 }
1279'
1280}
1281
1282fn (mut g Gen) decode_map(utyp ast.Type, key_type ast.Type, value_type ast.Type, ustyp string) string {
1283 styp := g.styp(key_type)
1284 mut styp_v := g.styp(value_type)
1285 ret_styp := styp_v.replace('*', '_ptr')
1286 key_type_symbol := g.table.sym(key_type)
1287 hash_fn, key_eq_fn, clone_fn, free_fn := g.map_fn_ptrs(key_type_symbol)
1288 fn_name_v := js_dec_name(styp_v)
1289 ref, ptr := if utyp.is_ptr() { '', '*' } else { '&', '' }
1290 mut s := ''
1291 if is_js_prim(styp_v) {
1292 s = '${styp_v} val = ${fn_name_v} (js_get(root, jsval->string));'
1293 } else {
1294 s = '
1295 ${result_name}_${ret_styp} val2 = ${fn_name_v} (js_get(root, jsval->string));
1296 if(val2.is_error) {
1297 builtin__map_free(${ref}res);
1298 return *(${result_name}_${ustyp}*)&val2;
1299 }
1300 ${styp_v} val = *(${styp_v}*)val2.data;
1301'
1302 }
1303
1304 if utyp.has_flag(.option) {
1305 return '
1306 if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) {
1307 return (${result_name}_${ustyp}){ .is_error = true, .err = builtin___v_error(builtin__string__plus(_S("Json element is not an object: "), json__json_print(root))), .data = {0}};
1308 }
1309 builtin___option_ok(&(${g.base_type(utyp)}[]) { builtin__new_map(sizeof(${styp}), sizeof(${styp_v}), ${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn}) }, (${option_name}*)&res, sizeof(${g.base_type(utyp)}));
1310 cJSON *jsval = NULL;
1311 cJSON_ArrayForEach(jsval, root)
1312 {
1313 ${s}
1314 string key = builtin__tos2((byteptr)jsval->string);
1315 builtin__map_set((map*)res.data, &key, &val);
1316 }
1317'
1318 } else {
1319 return '
1320 if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) {
1321 return (${result_name}_${ustyp}){ .is_error = true, .err = builtin___v_error(builtin__string__plus(_S("Json element is not an object: "), json__json_print(root))), .data = {0}};
1322 }
1323 ${ptr}res = builtin__new_map(sizeof(${styp}), sizeof(${styp_v}), ${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn});
1324 cJSON *jsval = NULL;
1325 cJSON_ArrayForEach(jsval, root)
1326 {
1327 ${s}
1328 string key = builtin__tos2((byteptr)jsval->string);
1329 builtin__map_set(${ref}res, &key, &val);
1330 }
1331'
1332 }
1333}
1334
1335fn (mut g Gen) encode_map(utyp ast.Type, key_type ast.Type, value_type ast.Type) string {
1336 styp := g.styp(key_type)
1337 styp_v := g.styp(value_type)
1338 key_array_type := ast.idx_to_type(g.table.find_or_register_array(key_type))
1339 key_array_styp := g.styp(key_array_type)
1340 fn_name_v := js_enc_name(styp_v)
1341 zero := g.type_default(value_type)
1342 keys_tmp := g.new_tmp_var()
1343 mut key := 'string key = '
1344 if key_type.is_string() {
1345 key += '((${styp}*)${keys_tmp}.data)[i];'
1346 } else {
1347 // key += '${styp}_str(((${styp}*)${keys_tmp}.data)[i]);'
1348 verror('json: encode only maps with string keys')
1349 }
1350 if utyp.has_flag(.option) {
1351 return '
1352 o = cJSON_CreateObject();
1353 ${key_array_styp} ${keys_tmp} = builtin__map_keys((map*)val.data);
1354 for (${ast.int_type_name} i = 0; i < ${keys_tmp}.len; ++i) {
1355 ${key}
1356 cJSON_AddItemToObject(o, (char*) key.str, ${fn_name_v} ( *(${styp_v}*) builtin__map_get((map*)val.data, &key, &(${styp_v}[]) { ${zero} } ) ) );
1357 }
1358 builtin__array_free(&${keys_tmp});
1359'
1360 } else {
1361 ref := if utyp.is_ptr() { '' } else { '&' }
1362 return '
1363 o = cJSON_CreateObject();
1364 ${key_array_styp} ${keys_tmp} = builtin__map_keys(${ref}val);
1365 for (${ast.int_type_name} i = 0; i < ${keys_tmp}.len; ++i) {
1366 ${key}
1367 cJSON_AddItemToObject(o, (char*) key.str, ${fn_name_v} ( *(${styp_v}*) builtin__map_get(${ref}val, &key, &(${styp_v}[]) { ${zero} } ) ) );
1368 }
1369 builtin__array_free(&${keys_tmp});
1370'
1371 }
1372}
1373
1374@[noreturn]
1375fn verror_suggest_json_no_inline_sumtypes(sumtype_name string, type_name1 string, type_name2 string) {
1376 verror('json: can not decode `${sumtype_name}` sumtype, too many numeric types (conflict of `${type_name1}` and `${type_name2}`), you can try to use alias for `${type_name2}` or compile v with `json_no_inline_sumtypes` flag')
1377}
1378