From c43ea09d8758924f2c88b8fff4964a9f028f9713 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sat, 22 Apr 2023 04:55:25 -0300 Subject: [PATCH] json: fix -cstrict build + optional map (#18014) --- vlib/json/json_encode_map_test.v | 22 ++++++++ vlib/v/gen/c/json.v | 91 +++++++++++++++++++++----------- 2 files changed, 82 insertions(+), 31 deletions(-) create mode 100644 vlib/json/json_encode_map_test.v diff --git a/vlib/json/json_encode_map_test.v b/vlib/json/json_encode_map_test.v new file mode 100644 index 0000000000..6f87ca6b8f --- /dev/null +++ b/vlib/json/json_encode_map_test.v @@ -0,0 +1,22 @@ +import json + +struct Test { + optional_string ?string + optional_array ?[]string + optional_struct_array ?[]string + optional_map ?map[string]string +} + +fn test_main() { + test := Test{} + encoded := json.encode(test) + assert dump(encoded) == '{}' + + test2 := Test{ + optional_map: { + 'foo': 'bar' + } + } + encoded2 := json.encode(test2) + assert dump(encoded2) == '{"optional_map":{"foo":"bar"}}' +} diff --git a/vlib/v/gen/c/json.v b/vlib/v/gen/c/json.v index c012fc388d..ed884c274d 100644 --- a/vlib/v/gen/c/json.v +++ b/vlib/v/gen/c/json.v @@ -135,8 +135,8 @@ ${enc_fn_dec} { m := sym.info as ast.Map g.gen_json_for_type(m.key_type) g.gen_json_for_type(m.value_type) - dec.writeln(g.decode_map(m.key_type, m.value_type, ret_styp)) - enc.writeln(g.encode_map(m.key_type, m.value_type)) + dec.writeln(g.decode_map(utyp, m.key_type, m.value_type, ret_styp)) + enc.writeln(g.encode_map(utyp, m.key_type, m.value_type)) } else if sym.kind == .alias { a := sym.info as ast.Alias parent_typ := a.parent_type @@ -155,8 +155,8 @@ ${enc_fn_dec} { m := psym.info as ast.Map g.gen_json_for_type(m.key_type) g.gen_json_for_type(m.value_type) - dec.writeln(g.decode_map(m.key_type, m.value_type, ret_styp)) - enc.writeln(g.encode_map(m.key_type, m.value_type)) + dec.writeln(g.decode_map(utyp, m.key_type, m.value_type, ret_styp)) + enc.writeln(g.encode_map(utyp, m.key_type, m.value_type)) } else { verror('json: ${sym.name} is not struct') } @@ -184,7 +184,7 @@ ${enc_fn_dec} { dec.writeln('\t${result_name}_${ret_styp} ret;') dec.writeln('\t_result_ok(&res, (${result_name}*)&ret, sizeof(res));') if utyp.has_flag(.option) { - dec.writeln('\t_option_ok(&res.data, &ret.data, sizeof(${g.base_type(utyp)}));') + dec.writeln('\t_option_ok(&res.data, (${option_name}*)&ret.data, sizeof(${g.base_type(utyp)}));') } dec.writeln('\treturn ret;\n}') enc.writeln('\treturn o;\n}') @@ -241,7 +241,7 @@ fn (mut g Gen) gen_enum_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc strin if is_option { base_typ := g.typ(utyp.clear_flag(.option)) enc.writeln('\to = ${js_enc_name('u64')}(*val.data);') - dec.writeln('\t_option_ok(&(${base_typ}[]){ ${js_dec_name('u64')}(root) }, &res, sizeof(${base_typ}));') + dec.writeln('\t_option_ok(&(${base_typ}[]){ ${js_dec_name('u64')}(root) }, (${option_name}*)&res, sizeof(${base_typ}));') } else { dec.writeln('\tres = ${js_dec_name('u64')}(root);') enc.writeln('\to = ${js_enc_name('u64')}(val);') @@ -280,7 +280,7 @@ fn (mut g Gen) gen_option_enc_dec(typ ast.Type, mut enc strings.Builder, mut dec enc.writeln('\to = ${encode_name}(*(${type_str}*)val.data);') dec_name := js_dec_name(type_str) - dec.writeln('\t_option_ok(&(${type_str}[]){ ${dec_name}(root) }, &res, sizeof(${type_str}));') + dec.writeln('\t_option_ok(&(${type_str}[]){ ${dec_name}(root) }, (${option_name}*)&res, sizeof(${type_str}));') } [inline] @@ -393,7 +393,7 @@ fn (mut g Gen) gen_sumtype_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc st dec.writeln('\t\t${variant_typ} value = *(${variant_typ}*)(${tmp}.data);') } if is_option { - dec.writeln('\t\t\t_option_ok(&(${sym.cname}[]){ ${variant_typ}_to_sumtype_${sym.cname}(&value) }, &res, sizeof(${sym.cname}));') + dec.writeln('\t\t\t_option_ok(&(${sym.cname}[]){ ${variant_typ}_to_sumtype_${sym.cname}(&value) }, (${option_name}*)&res, sizeof(${sym.cname}));') } else { dec.writeln('\t\tres = ${variant_typ}_to_sumtype_${ret_styp}(&value);') } @@ -806,7 +806,7 @@ fn (mut g Gen) decode_array(utyp ast.Type, value_type ast.Type, fixed_array_size fixed_array_idx_increment += 'fixed_array_idx++;' } else { array_element_assign += 'array_push${noscan}((array*)&res.data, &val);' - res_str += '_option_ok(&(${g.base_type(utyp)}[]) { __new_array${noscan}(0, 0, sizeof(${styp})) }, &res, sizeof(${g.base_type(utyp)}));' + res_str += '_option_ok(&(${g.base_type(utyp)}[]) { __new_array${noscan}(0, 0, sizeof(${styp})) }, (${option_name}*)&res, sizeof(${g.base_type(utyp)}));' array_free_str += 'array_free(&res.data);' } } else { @@ -882,7 +882,7 @@ fn (mut g Gen) encode_array(utyp ast.Type, value_type ast.Type, fixed_array_size ' } -fn (mut g Gen) decode_map(key_type ast.Type, value_type ast.Type, ustyp string) string { +fn (mut g Gen) decode_map(utyp ast.Type, key_type ast.Type, value_type ast.Type, ustyp string) string { styp := g.typ(key_type) mut styp_v := g.typ(value_type) ret_styp := styp_v.replace('*', '_ptr') @@ -902,22 +902,39 @@ fn (mut g Gen) decode_map(key_type ast.Type, value_type ast.Type, ustyp string) ${styp_v} val = *(${styp_v}*)val2.data; ' } - return ' - if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) { - return (${result_name}_${ustyp}){ .is_error = true, .err = _v_error(string__plus(_SLIT("Json element is not an object: "), tos2((byteptr)cJSON_PrintUnformatted(root)))), .data = {0}}; - } - res = new_map(sizeof(${styp}), sizeof(${styp_v}), ${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn}); - cJSON *jsval = NULL; - cJSON_ArrayForEach(jsval, root) - { - ${s} - string key = tos2((byteptr)jsval->string); - map_set(&res, &key, &val); - } + + if utyp.has_flag(.option) { + return ' + if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) { + return (${result_name}_${ustyp}){ .is_error = true, .err = _v_error(string__plus(_SLIT("Json element is not an object: "), tos2((byteptr)cJSON_PrintUnformatted(root)))), .data = {0}}; + } + _option_ok(&(${g.base_type(utyp)}[]) { new_map(sizeof(${styp}), sizeof(${styp_v}), ${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn}) }, (${option_name}*)&res, sizeof(${g.base_type(utyp)})); + cJSON *jsval = NULL; + cJSON_ArrayForEach(jsval, root) + { + ${s} + string key = tos2((byteptr)jsval->string); + map_set((map*)res.data, &key, &val); + } ' + } else { + return ' + if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) { + return (${result_name}_${ustyp}){ .is_error = true, .err = _v_error(string__plus(_SLIT("Json element is not an object: "), tos2((byteptr)cJSON_PrintUnformatted(root)))), .data = {0}}; + } + res = new_map(sizeof(${styp}), sizeof(${styp_v}), ${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn}); + cJSON *jsval = NULL; + cJSON_ArrayForEach(jsval, root) + { + ${s} + string key = tos2((byteptr)jsval->string); + map_set(&res, &key, &val); + } +' + } } -fn (mut g Gen) encode_map(key_type ast.Type, value_type ast.Type) string { +fn (mut g Gen) encode_map(utyp ast.Type, key_type ast.Type, value_type ast.Type) string { styp := g.typ(key_type) styp_v := g.typ(value_type) fn_name_v := js_enc_name(styp_v) @@ -930,13 +947,25 @@ fn (mut g Gen) encode_map(key_type ast.Type, value_type ast.Type) string { // key += '${styp}_str((($styp*)${keys_tmp}.data)[i]);' verror('json: encode only maps with string keys') } - return ' - o = cJSON_CreateObject(); - Array_${styp} ${keys_tmp} = map_keys(&val); - for (int i = 0; i < ${keys_tmp}.len; ++i) { - ${key} - cJSON_AddItemToObject(o, (char*) key.str, ${fn_name_v} ( *(${styp_v}*) map_get(&val, &key, &(${styp_v}[]) { ${zero} } ) ) ); - } - array_free(&${keys_tmp}); + if utyp.has_flag(.option) { + return ' + o = cJSON_CreateObject(); + Array_${styp} ${keys_tmp} = map_keys((map*)val.data); + for (int i = 0; i < ${keys_tmp}.len; ++i) { + ${key} + cJSON_AddItemToObject(o, (char*) key.str, ${fn_name_v} ( *(${styp_v}*) map_get((map*)val.data, &key, &(${styp_v}[]) { ${zero} } ) ) ); + } + array_free(&${keys_tmp}); ' + } else { + return ' + o = cJSON_CreateObject(); + Array_${styp} ${keys_tmp} = map_keys(&val); + for (int i = 0; i < ${keys_tmp}.len; ++i) { + ${key} + cJSON_AddItemToObject(o, (char*) key.str, ${fn_name_v} ( *(${styp_v}*) map_get(&val, &key, &(${styp_v}[]) { ${zero} } ) ) ); + } + array_free(&${keys_tmp}); +' + } }