From 1aec041371f7a9ba74ef5169ead5127c505b0e59 Mon Sep 17 00:00:00 2001 From: Ned Palacios Date: Tue, 29 Sep 2020 09:15:00 +0800 Subject: [PATCH] json: fix encode/decode support for generic structs (#6489) --- vlib/json/json_test.v | 21 ++++++++++++++++++++- vlib/v/gen/cgen.v | 14 +++++++++++--- vlib/v/gen/fn.v | 10 ++++++---- vlib/v/gen/json.v | 4 ++-- vlib/v/tests/generics_test.v | 14 ++++++++++++++ vlib/x/json2/json2.v | 9 +++++++++ 6 files changed, 62 insertions(+), 10 deletions(-) diff --git a/vlib/json/json_test.v b/vlib/json/json_test.v index c7db774a1e..6603b792bd 100644 --- a/vlib/json/json_test.v +++ b/vlib/json/json_test.v @@ -218,7 +218,7 @@ fn test_nested_type() { assert data2.countries[i].cities[j].name == data.countries[i].cities[j].name } } - + for key, user in data.users { assert data2.users[key].age == user.age assert data2.users[key].nums == user.nums @@ -235,6 +235,25 @@ fn test_nested_type() { } } +struct Foo { +pub: + name string + data T +} + +fn test_generic_struct() { + foo_int := Foo{'bar', 12} + foo_enc := json.encode(foo_int) + assert foo_enc == '{"name":"bar","data":12}' + + foo_dec := json.decode(Foo, foo_enc) or { + exit(1) + } + + assert foo_dec.name == 'bar' + assert foo_dec.data == 12 +} + fn test_errors() { invalid_array := fn () { data := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":{"name":"Donlon"},"name":"KU"}],"users":{"Foo":{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},"Boo":{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}},"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}' diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 403ff9f25e..f05e4781af 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -482,9 +482,9 @@ static inline $opt_el_type __Option_${styp}_popval($styp ch) { } } -// cc_type returns the Cleaned Concrete Type name, *without ptr*, -// i.e. it's always just Cat, not Cat_ptr: -fn (g &Gen) cc_type(t table.Type) string { +// TODO: merge cc_type and cc_type2 +// cc_type but without the `struct` prefix +fn (g &Gen) cc_type2(t table.Type) string { sym := g.table.get_type_symbol(g.unwrap_generic(t)) mut styp := util.no_dots(sym.name) if sym.kind == .struct_ { @@ -501,6 +501,14 @@ fn (g &Gen) cc_type(t table.Type) string { styp = styp.replace('<', '_T_').replace('>', '').replace(',', '_') } } + return styp +} + +// cc_type returns the Cleaned Concrete Type name, *without ptr*, +// i.e. it's always just Cat, not Cat_ptr: +fn (g &Gen) cc_type(t table.Type) string { + sym := g.table.get_type_symbol(g.unwrap_generic(t)) + mut styp := g.cc_type2(t) if styp.starts_with('C__') { styp = styp[3..] if sym.kind == .struct_ { diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index b9f3c9cca6..8c11e67d65 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -50,7 +50,8 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) { name = util.replace_op(name) } if it.is_method { - name = g.table.get_type_symbol(it.receiver.typ).name + '_' + name + name = g.cc_type2(it.receiver.typ) + '_' + name + // name = g.table.get_type_symbol(it.receiver.typ).name + '_' + name } if it.language == .c { name = util.no_dots(name) @@ -336,7 +337,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) { // mut receiver_type_name := g.cc_type(node.receiver_type) // mut receiver_type_name := g.typ(node.receiver_type) typ_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver_type)) - mut receiver_type_name := util.no_dots(typ_sym.name) + // mut receiver_type_name := util.no_dots(typ_sym.name) + mut receiver_type_name := util.no_dots(g.cc_type2(g.unwrap_generic(node.receiver_type))) if typ_sym.kind == .interface_ { // Speaker_name_table[s._interface_idx].speak(s._object) g.write('${c_name(receiver_type_name)}_name_table[') @@ -494,7 +496,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { cur_line := g.go_before_stmt(0) if is_json_encode { g.gen_json_for_type(node.args[0].typ) - json_type_str = g.table.get_type_symbol(node.args[0].typ).name + json_type_str = g.typ(node.args[0].typ) // `json__encode` => `json__encode_User` encode_name := c_name(name) + '_' + util.no_dots(json_type_str) g.writeln('// json.encode') @@ -507,7 +509,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { } else { ast_type := node.args[0].expr as ast.Type // `json.decode(User, s)` => json.decode_User(s) - typ := c_name(g.table.get_type_symbol(ast_type.typ).name) + typ := c_name(g.typ(ast_type.typ)) fn_name := c_name(name) + '_' + typ g.gen_json_for_type(ast_type.typ) g.writeln('// json.decode') diff --git a/vlib/v/gen/json.v b/vlib/v/gen/json.v index 3322860ae5..bb6e931d39 100644 --- a/vlib/v/gen/json.v +++ b/vlib/v/gen/json.v @@ -37,7 +37,7 @@ fn (mut g Gen) gen_json_for_type(typ table.Type) { // decode_TYPE funcs receive an actual cJSON* object to decode // cJSON_Parse(str) call is added by the compiler // Code gen decoder - dec_fn_name := js_dec_name(sym.name) + dec_fn_name := js_dec_name(styp) // Make sure that this optional type actually exists g.register_optional(typ) dec_fn_dec := 'Option_$styp ${dec_fn_name}(cJSON* root)' @@ -58,7 +58,7 @@ $dec_fn_dec { g.json_forward_decls.writeln('$dec_fn_dec;') // Code gen encoder // encode_TYPE funcs receive an object to encode - enc_fn_name := js_enc_name(sym.name) + enc_fn_name := js_enc_name(styp) enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)' g.json_forward_decls.writeln('$enc_fn_dec;\n') enc.writeln(' diff --git a/vlib/v/tests/generics_test.v b/vlib/v/tests/generics_test.v index 0d6e4ce459..2bc004638a 100644 --- a/vlib/v/tests/generics_test.v +++ b/vlib/v/tests/generics_test.v @@ -237,6 +237,20 @@ fn test_generic_struct() { // println(x.model.name) } +struct Foo { +pub: + data T +} + +fn (f Foo) value() string { + return f.data.str() +} + +fn test_generic_struct_method() { + foo_int := Foo{2} + assert foo_int.value() == '2' +} + /* struct Abc{ x int y int z int } diff --git a/vlib/x/json2/json2.v b/vlib/x/json2/json2.v index 43b0d85bcc..534a6e1166 100644 --- a/vlib/x/json2/json2.v +++ b/vlib/x/json2/json2.v @@ -122,3 +122,12 @@ pub fn (f Any) arr() []Any { return [f] } + +// Use `Any` as a bool +pub fn (f Any) bool() bool { + match f { + bool { return *f } + string { return (*f).bool() } + else { return false } + } +}