From 1c3af091f7e778ea33f6a8741e51770dad7da977 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sat, 6 May 2023 21:54:43 -0300 Subject: [PATCH] cgen, json: fix option ptr declaration and dumping (#18119) --- vlib/v/gen/c/auto_str_methods.v | 22 +++++++++---- vlib/v/gen/c/cgen.v | 4 +++ vlib/v/gen/c/json.v | 50 +++++++++++++++++++++++++----- vlib/v/tests/option_ptr_ptr_test.v | 47 ++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 13 deletions(-) create mode 100644 vlib/v/tests/option_ptr_ptr_test.v diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v index 004e9a3953..4e6e8cb54e 100644 --- a/vlib/v/gen/c/auto_str_methods.v +++ b/vlib/v/gen/c/auto_str_methods.v @@ -185,15 +185,24 @@ fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string) g.auto_str_funcs.writeln('string indent_${str_fn_name}(${styp} it, int indent_count) {') g.auto_str_funcs.writeln('\tstring res;') g.auto_str_funcs.writeln('\tif (it.state == 0) {') + deref := if typ.is_ptr() { '**(${sym.cname}**)&' } else { '*(${sym.cname}*)' } if sym.kind == .string { - tmp_res := '${parent_str_fn_name}(*(${sym.cname}*)it.data)' - g.auto_str_funcs.writeln('\t\tres = ${str_intp_sq(tmp_res)};') + if typ.nr_muls() > 1 { + g.auto_str_funcs.writeln('\t\tres = ptr_str(*(${sym.cname}**)&it.data);') + } else { + tmp_res := '${parent_str_fn_name}(${deref}it.data)' + g.auto_str_funcs.writeln('\t\tres = ${str_intp_sq(tmp_res)};') + } } else if should_use_indent_func(sym.kind) && !sym_has_str_method { - g.auto_str_funcs.writeln('\t\tres = indent_${parent_str_fn_name}(*(${sym.cname}*)it.data, indent_count);') + g.auto_str_funcs.writeln('\t\tres = indent_${parent_str_fn_name}(${deref}it.data, indent_count);') } else if sym.kind == .function { g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}();') } else { - g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(*(${sym.cname}*)it.data);') + if typ.nr_muls() > 1 { + g.auto_str_funcs.writeln('\t\tres = ptr_str(*(${sym.cname}**)&it.data);') + } else { + g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(${deref}it.data);') + } } g.auto_str_funcs.writeln('\t\treturn ${str_intp_sub('Option(%%)', 'res')};') g.auto_str_funcs.writeln('\t}') @@ -962,13 +971,14 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, lang ast.Language, styp strin } else if ftyp_noshared.is_ptr() { // reference types can be "nil" if ftyp_noshared.has_flag(.option) { - funcprefix += 'isnil(&it.${field_name})' + funcprefix += 'isnil(&it.${field_name}) || isnil(&it.${field_name}.data)' } else { funcprefix += 'isnil(it.${field_name})' } funcprefix += ' ? _SLIT("nil") : ' // struct, floats and ints have a special case through the _str function - if sym.kind !in [.struct_, .alias, .enum_, .sum_type, .map, .interface_] + if !ftyp_noshared.has_flag(.option) + && sym.kind !in [.struct_, .alias, .enum_, .sum_type, .map, .interface_] && !field.typ.is_int_valptr() && !field.typ.is_float_valptr() { funcprefix += '*' } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 277599124f..24424e5e14 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -1099,6 +1099,8 @@ fn (g Gen) option_type_text(styp string, base string) string { 'u8' } else if base.starts_with('anon_fn') { 'void*' + } else if base.starts_with('_option_') { + base.replace('*', '') } else { if base.starts_with('struct ') && !base.ends_with('*') { '${base}*' } else { base } } @@ -1116,6 +1118,8 @@ fn (g Gen) result_type_text(styp string, base string) string { 'u8' } else if base.starts_with('anon_fn') { 'void*' + } else if base.starts_with('_option_') { + base.replace('*', '') } else { if base.starts_with('struct ') && !base.ends_with('*') { '${base}*' } else { base } } diff --git a/vlib/v/gen/c/json.v b/vlib/v/gen/c/json.v index c8be1cdbba..941d66b7aa 100644 --- a/vlib/v/gen/c/json.v +++ b/vlib/v/gen/c/json.v @@ -26,7 +26,7 @@ import strings fn (mut g Gen) gen_json_for_type(typ ast.Type) { utyp := g.unwrap_generic(typ) sym := g.table.sym(utyp) - if is_js_prim(sym.name) && !utyp.has_flag(.option) { + if is_js_prim(sym.name) && !utyp.has_flag(.option) && !typ.is_ptr() { return } g.json_types << utyp @@ -45,7 +45,11 @@ fn (mut g Gen) gen_jsons() { sym := g.table.sym(utyp) styp := g.typ(utyp) ret_styp := styp.replace('*', '_ptr') + if utyp.is_ptr() && utyp.has_flag(.option) { + g.register_option(utyp.set_nr_muls(0)) + } g.register_result(utyp) + // decode_TYPE funcs receive an actual cJSON* object to decode // cJSON_Parse(str) call is added by the compiler // Codegen decoder @@ -118,7 +122,9 @@ ${dec_fn_dec} { enc.writeln(' ${enc_fn_dec} { \tcJSON *o;') - if sym.kind == .array || sym.kind == .array_fixed { + if is_js_prim(sym.name) && utyp.is_ptr() { + g.gen_prim_enc_dec(utyp, mut enc, mut dec) + } else if sym.kind == .array || sym.kind == .array_fixed { array_size := if sym.kind == .array_fixed { (sym.info as ast.ArrayFixed).size } else { @@ -262,12 +268,42 @@ fn (mut g Gen) gen_enum_enc_dec(utyp ast.Type, sym ast.TypeSymbol, mut enc strin [inline] fn (mut g Gen) gen_prim_enc_dec(typ ast.Type, mut enc strings.Builder, mut dec strings.Builder) { - type_str := g.typ(typ.clear_flag(.option)) - encode_name := js_enc_name(type_str) - enc.writeln('\to = ${encode_name}(val);') + if typ.is_ptr() { + type_str := g.typ(typ.clear_flag(.option).set_nr_muls(typ.nr_muls() - 1)) + type_str_0 := g.typ(typ.clear_flag(.option).set_nr_muls(0)) + encode_name := js_enc_name(type_str_0) + dec_name := js_dec_name(type_str) + if typ.has_flag(.option) { + enc.writeln('\to = ${encode_name}(${'*'.repeat(typ.nr_muls() + 1)}(${type_str_0}${'*'.repeat(typ.nr_muls())}*)&val.data);') + } else { + enc.writeln('\to = ${encode_name}(${'*'.repeat(typ.nr_muls())}val);') + } - dec_name := js_dec_name(type_str) - dec.writeln('\tres = ${dec_name}(root);') + if typ.nr_muls() > 1 { + g.gen_json_for_type(typ.clear_flag(.option).set_nr_muls(typ.nr_muls() - 1)) + if typ.has_flag(.option) { + tmp_var := g.new_tmp_var() + dec.writeln('${type_str}* ${tmp_var} = HEAP(${type_str}, *(${type_str}*) ${dec_name}(root).data);') + dec.writeln('\t_option_ok(&(${type_str}*[]) { &(*(${tmp_var})) }, (${option_name}*)&res, sizeof(${type_str}*));') + } else { + dec.writeln('\tres = HEAP(${type_str}, *(${type_str}*) ${dec_name}(root).data);') + } + } else { + if typ.has_flag(.option) { + tmp_var := g.new_tmp_var() + dec.writeln('${type_str}* ${tmp_var} = HEAP(${type_str}, ${dec_name}(root));') + dec.writeln('\t_option_ok(&(${type_str}*[]) { &(*(${tmp_var})) }, (${option_name}*)&res, sizeof(${type_str}*));') + } else { + dec.writeln('\tres = HEAP(${type_str}, ${dec_name}(root));') + } + } + } else { + type_str := g.typ(typ.clear_flag(.option)) + encode_name := js_enc_name(type_str) + dec_name := js_dec_name(type_str) + enc.writeln('\to = ${encode_name}(val);') + dec.writeln('\tres = ${dec_name}(root);') + } } [inline] diff --git a/vlib/v/tests/option_ptr_ptr_test.v b/vlib/v/tests/option_ptr_ptr_test.v new file mode 100644 index 0000000000..47caced3d2 --- /dev/null +++ b/vlib/v/tests/option_ptr_ptr_test.v @@ -0,0 +1,47 @@ +import json + +[heap] +struct Foo { + a &int + b &string + c &f64 + d &&int + e &&&string +} + +struct FooOption { +mut: + a ?&int + b ?&string + c ?&f64 + d ?&&int + e ?&&&string +} + +fn test_ptr() { + data := '{ "a": 123, "b": "foo", "c": 1.2, "d": 321, "e": "bar"}' + foo := json.decode(Foo, data)! + println(foo) + + assert dump(*foo.a) == 123 + assert dump(*foo.b) == 'foo' + assert dump(*foo.c) == 1.2 + assert dump(**foo.d) == 321 + assert dump(***foo.e) == 'bar' + + assert dump(json.encode(foo)) == '{"a":123,"b":"foo","c":1.2,"d":321,"e":"bar"}' +} + +fn test_option_ptr() ? { + data := '{ "a": 123, "b": "foo", "c": 1.2, "d": 321, "e": "bar"}' + foo := json.decode(FooOption, data) or { return none } + println(foo) + + assert dump(*foo.a?) == 123 + assert dump(*foo.b?) == 'foo' + assert dump(*foo.c?) == 1.2 + assert dump(**foo.d?) == 321 + assert dump(***foo.e?) == 'bar' + + assert dump(json.encode(foo)) == '{"a":123,"b":"foo","c":1.2,"d":321,"e":"bar"}' +}