From 4df08e9c8f8548c0107f3834927287074079fd88 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Mon, 4 May 2026 06:14:09 +0300 Subject: [PATCH] cgen, eventbus, json2: fixes --- vlib/eventbus/eventbus.v | 4 +- vlib/v/gen/c/comptime.v | 13 +- vlib/v/gen/c/index.v | 10 +- vlib/x/json2/decode.v | 753 ++++++++++++++++++++------------------- vlib/x/json2/encode.v | 14 +- 5 files changed, 421 insertions(+), 373 deletions(-) diff --git a/vlib/eventbus/eventbus.v b/vlib/eventbus/eventbus.v index 14b895e0c..3c38eece0 100644 --- a/vlib/eventbus/eventbus.v +++ b/vlib/eventbus/eventbus.v @@ -12,12 +12,12 @@ mut: registry &Registry[T] = unsafe { nil } } -struct Registry[T] { +pub struct Registry[T] { mut: events []EventHandler[T] } -struct EventHandler[T] { +pub struct EventHandler[T] { name T handler EventHandlerFn = unsafe { nil } receiver voidptr = unsafe { nil } diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index a51c7fcca..e2d6bdfb5 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -272,7 +272,13 @@ fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) { g.write('*(') } g.expr(node.left) - if g.unwrap_generic(left_type).is_ptr() { + is_auto_heap_ident := node.left is ast.Ident && g.resolved_ident_is_auto_heap(node.left) + // When `g.expr` writes an auto-heap ident, it emits `(*(name))` by default, + // but skips the deref when it's an LHS / inside a selector LHS / assign-fn-var. + // In the deref-skipped case the C result is a pointer, so we need `->`. + auto_heap_no_deref := is_auto_heap_ident + && (g.is_assign_lhs || g.inside_selector_lhs || g.inside_assign_fn_var) + if g.unwrap_generic(left_type).is_ptr() || auto_heap_no_deref { g.write('->') } else { g.write('.') @@ -298,7 +304,10 @@ fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) { fn (mut g Gen) gen_comptime_selector(expr ast.ComptimeSelector) string { left_type := g.resolved_expr_type(expr.left, expr.left_type) - arrow_or_dot := if left_type.is_ptr() { '->' } else { '.' } + is_auto_heap_ident := expr.left is ast.Ident && g.resolved_ident_is_auto_heap(expr.left) + auto_heap_no_deref := is_auto_heap_ident + && (g.is_assign_lhs || g.inside_selector_lhs || g.inside_assign_fn_var) + arrow_or_dot := if left_type.is_ptr() || auto_heap_no_deref { '->' } else { '.' } mut field_name := if expr.typ_key.contains('|') { expr.typ_key.all_after('|') } else { diff --git a/vlib/v/gen/c/index.v b/vlib/v/gen/c/index.v index a94be910a..7d9ea80e2 100644 --- a/vlib/v/gen/c/index.v +++ b/vlib/v/gen/c/index.v @@ -373,7 +373,15 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) { info.elem_type } elem_sym := g.table.final_sym(elem_type) - left_is_ptr := array_left_type.is_ptr() || node.left.is_auto_deref_var() + mut left_is_ptr := array_left_type.is_ptr() || node.left.is_auto_deref_var() + // Auto-heap idents are pointers in C. `g.expr` writes `(*(name))` to deref them, + // but suppresses that deref when the ident is on an assign LHS / inside a selector + // LHS / inside an assign-fn-var context. In those cases the C result of `g.expr` + // is a pointer, so this IndexExpr must emit its own `*` before `array_get`. + if !left_is_ptr && node.left is ast.Ident && g.resolved_ident_is_auto_heap(node.left) + && (g.is_assign_lhs || g.inside_selector_lhs || g.inside_assign_fn_var) { + left_is_ptr = true + } result_type := match true { gen_or && elem_type.has_flag(.option) { node.typ.clear_flag(.option) diff --git a/vlib/x/json2/decode.v b/vlib/x/json2/decode.v index 6034767ff..13303b27f 100644 --- a/vlib/x/json2/decode.v +++ b/vlib/x/json2/decode.v @@ -326,11 +326,13 @@ fn get_dynamic_from_element[T](_t T) []T { } fn create_decoded_ptr[T](_ &T) &T { - ptr := unsafe { &T(malloc(sizeof(T))) } - unsafe { - *ptr = T{} + $if T is $interface { + return unsafe { nil } + } $else $if T.unaliased_typ is voidptr { + return unsafe { nil } + } $else { + return unsafe { &T(vcalloc(sizeof(T))) } } - return ptr } fn create_decoded_option_ptr[U](_ ?&U) &U { @@ -649,346 +651,361 @@ fn check_required_struct_fields[T](mut decoder Decoder, val T, seen_required []s // decode_value decodes a value from the JSON nodes. @[manualfree] fn (mut decoder Decoder) decode_value[T](mut val T) ! { - // Custom Decoders - $if val is StringDecoder { - struct_info := decoder.current_node.value - - if struct_info.value_kind == .string { - val.from_json_string(decoder.json[struct_info.position + 1..struct_info.position + - struct_info.length - 1]) or { - decoder.decode_error('${typeof(val).name}: ${err.msg()}')! - } - if decoder.current_node != unsafe { nil } { - decoder.current_node = decoder.current_node.next - } - - return + $if T.unaliased_typ is voidptr { + // skip voidptr fields - they cannot be decoded from JSON + if decoder.current_node != unsafe { nil } { + decoder.current_node = decoder.current_node.next } - } - $if val is NumberDecoder { - struct_info := decoder.current_node.value + return + } $else $if T is $interface { + // skip interface fields - they cannot be decoded from JSON + if decoder.current_node != unsafe { nil } { + decoder.current_node = decoder.current_node.next + } + return + } $else { + // Custom Decoders + $if val is StringDecoder { + struct_info := decoder.current_node.value + + if struct_info.value_kind == .string { + val.from_json_string(decoder.json[struct_info.position + 1..struct_info.position + + struct_info.length - 1]) or { + decoder.decode_error('${typeof(val).name}: ${err.msg()}')! + } + if decoder.current_node != unsafe { nil } { + decoder.current_node = decoder.current_node.next + } - if struct_info.value_kind == .number { - val.from_json_number(decoder.json[struct_info.position..struct_info.position + - struct_info.length]) or { - decoder.decode_error('${typeof(val).name}: ${err.msg()}')! - } - if decoder.current_node != unsafe { nil } { - decoder.current_node = decoder.current_node.next + return } - - return } - } - $if val is BooleanDecoder { - struct_info := decoder.current_node.value + $if val is NumberDecoder { + struct_info := decoder.current_node.value - if struct_info.value_kind == .boolean { - val.from_json_boolean(decoder.json[struct_info.position] == `t`) - if decoder.current_node != unsafe { nil } { - decoder.current_node = decoder.current_node.next + if struct_info.value_kind == .number { + val.from_json_number(decoder.json[struct_info.position..struct_info.position + + struct_info.length]) or { + decoder.decode_error('${typeof(val).name}: ${err.msg()}')! + } + if decoder.current_node != unsafe { nil } { + decoder.current_node = decoder.current_node.next + } + + return } + } + $if val is BooleanDecoder { + struct_info := decoder.current_node.value - return + if struct_info.value_kind == .boolean { + val.from_json_boolean(decoder.json[struct_info.position] == `t`) + if decoder.current_node != unsafe { nil } { + decoder.current_node = decoder.current_node.next + } + + return + } } - } - $if val is NullDecoder { - struct_info := decoder.current_node.value + $if val is NullDecoder { + struct_info := decoder.current_node.value - if struct_info.value_kind == .null { - val.from_json_null() + if struct_info.value_kind == .null { + val.from_json_null() + if decoder.current_node != unsafe { nil } { + decoder.current_node = decoder.current_node.next + } + + return + } + } + $if T.unaliased_typ is voidptr { + // skip voidptr fields - they cannot be decoded from JSON if decoder.current_node != unsafe { nil } { decoder.current_node = decoder.current_node.next } - return - } - } - $if T.unaliased_typ is voidptr { - // skip voidptr fields - they cannot be decoded from JSON - if decoder.current_node != unsafe { nil } { - decoder.current_node = decoder.current_node.next - } - return - } $else $if T.unaliased_typ is string { - decoder.decode_string(mut val)! - } $else $if T.unaliased_typ is time.Time { - value_info := decoder.current_node.value - mut decoded_time := time.Time{} - if value_info.value_kind == .string { - decoded_time.from_json_string(decoder.json[value_info.position + 1.. - value_info.position + value_info.length - 1]) or { - decoder.decode_error('${typeof(val).name}: ${err.msg()}')! + } $else $if T.unaliased_typ is string { + decoder.decode_string(mut val)! + } $else $if T.unaliased_typ is time.Time { + value_info := decoder.current_node.value + mut decoded_time := time.Time{} + if value_info.value_kind == .string { + decoded_time.from_json_string(decoder.json[value_info.position + 1.. + value_info.position + value_info.length - 1]) or { + decoder.decode_error('${typeof(val).name}: ${err.msg()}')! + } + } else if value_info.value_kind == .number { + decoded_time.from_json_number(decoder.json[value_info.position.. + value_info.position + value_info.length]) or { + decoder.decode_error('${typeof(val).name}: ${err.msg()}')! + } + } else { + decoder.decode_error('Expected string or number, but got ${value_info.value_kind}')! } - } else if value_info.value_kind == .number { - decoded_time.from_json_number(decoder.json[value_info.position..value_info.position + - value_info.length]) or { - decoder.decode_error('${typeof(val).name}: ${err.msg()}')! + val = T(decoded_time) + } $else $if T.unaliased_typ is $sumtype { + decoder.decode_sumtype(mut val)! + return + } $else $if T.unaliased_typ is $map { + decoder.decode_map(mut val)! + return + } $else $if T.unaliased_typ is $array_dynamic { + val.clear() + decoder.decode_array(mut val)! + // return to avoid the next increment of the current node + // this is because the current node is already incremented in the decode_array function + // remove this line will cause the current node to be incremented twice + // and bug recursive array decoding like `[][]int{}` + return + } $else $if T.unaliased_typ is $array_fixed { + mut dynamic_val := get_dynamic_from_element(val[0]) + + // avoid copying by pointing dynamic_val to val + unsafe { + dynamic_val.len = 0 + dynamic_val.cap = val.len // ensures data wont reallocate + dynamic_val.data = &val } - } else { - decoder.decode_error('Expected string or number, but got ${value_info.value_kind}')! - } - val = T(decoded_time) - } $else $if T.unaliased_typ is $sumtype { - decoder.decode_sumtype(mut val)! - return - } $else $if T.unaliased_typ is $map { - decoder.decode_map(mut val)! - return - } $else $if T.unaliased_typ is $array_dynamic { - val.clear() - decoder.decode_array(mut val)! - // return to avoid the next increment of the current node - // this is because the current node is already incremented in the decode_array function - // remove this line will cause the current node to be incremented twice - // and bug recursive array decoding like `[][]int{}` - return - } $else $if T.unaliased_typ is $array_fixed { - mut dynamic_val := get_dynamic_from_element(val[0]) + decoder.decode_array(mut dynamic_val)! - // avoid copying by pointing dynamic_val to val - unsafe { - dynamic_val.len = 0 - dynamic_val.cap = val.len // ensures data wont reallocate - dynamic_val.data = &val - } - decoder.decode_array(mut dynamic_val)! - - if dynamic_val.len != val.len { - decoder.decode_error('Fixed size array expected ${val.len} elements but got ${dynamic_val.len} elements')! - } - return - } $else $if T.unaliased_typ is $struct { - struct_info := decoder.current_node.value - - if struct_info.value_kind == .object { - struct_position := struct_info.position - struct_end := struct_position + struct_info.length - mut has_embeds := false - $for field in T.fields { - $if field.is_embed { - has_embeds = true - } + if dynamic_val.len != val.len { + decoder.decode_error('Fixed size array expected ${val.len} elements but got ${dynamic_val.len} elements')! } - decoder.current_node = decoder.current_node.next - if has_embeds { - mut seen_required := []string{} + return + } $else $if T.unaliased_typ is $struct { + struct_info := decoder.current_node.value - // json object loop - for { - if decoder.current_node == unsafe { nil } { - break + if struct_info.value_kind == .object { + struct_position := struct_info.position + struct_end := struct_position + struct_info.length + mut has_embeds := false + $for field in T.fields { + $if field.is_embed { + has_embeds = true } + } + decoder.current_node = decoder.current_node.next + if has_embeds { + mut seen_required := []string{} - key_info := decoder.current_node.value + // json object loop + for { + if decoder.current_node == unsafe { nil } { + break + } - if key_info.position >= struct_end { - break - } + key_info := decoder.current_node.value - decode_result := decode_struct_key(mut decoder, val, key_info, '', mut - seen_required)! - if decode_result.matched { - val = decode_result.value - } else { - // The key doesn't match any field in the struct, skip the entire value - // including all nested objects/arrays. - decoder.current_node = decoder.current_node.next - decoder.skip_current_value() - } - } + if key_info.position >= struct_end { + break + } - check_required_struct_fields(mut decoder, val, seen_required, '')! - } else { - field_infos := unsafe { decoder.cached_struct_field_infos[T]() } - mut decoded_mask := u64(0) - mut decoded_fields := []bool{} - if field_infos.len > 64 { - decoded_fields = []bool{len: field_infos.len} - } + decode_result := decode_struct_key(mut decoder, val, key_info, '', mut + seen_required)! + if decode_result.matched { + val = decode_result.value + } else { + // The key doesn't match any field in the struct, skip the entire value + // including all nested objects/arrays. + decoder.current_node = decoder.current_node.next + decoder.skip_current_value() + } + } - mut field_idx := 0 - // json object loop - for { - if decoder.current_node == unsafe { nil } { - break + check_required_struct_fields(mut decoder, val, seen_required, '')! + } else { + field_infos := unsafe { decoder.cached_struct_field_infos[T]() } + mut decoded_mask := u64(0) + mut decoded_fields := []bool{} + if field_infos.len > 64 { + decoded_fields = []bool{len: field_infos.len} } - key_info := decoder.current_node.value + mut field_idx := 0 + // json object loop + for { + if decoder.current_node == unsafe { nil } { + break + } - if key_info.position >= struct_end { - break - } + key_info := decoder.current_node.value - mut matched := false - field_idx = 0 - $for field in T.fields { - if !matched { - field_info := field_infos[field_idx] - field_can_match := (!field_info.is_skip || field_info.is_required) - && !(field_info.is_omitempty - && decoder.is_empty_value(decoder.current_node.next.value)) - field_name_matches := key_info.length - 2 == field_info.json_name_len && unsafe { - vmemcmp(decoder.json.str + key_info.position + 1, field_info.json_name_ptr, field_info.json_name_len) == 0 - } - if field_can_match && field_name_matches { - // value node - decoder.current_node = decoder.current_node.next + if key_info.position >= struct_end { + break + } - if field_info.is_skip { - // Preserve the existing inline decode behavior for `skip`+`required`. - decoded_mask = mark_struct_field_decoded(decoded_mask, mut - decoded_fields, field_idx) - if decoder.current_node != unsafe { nil } { - decoder.current_node = decoder.current_node.next - } - } else if field_info.is_raw { - $if field.unaliased_typ is $enum { - // workaround to avoid the error: enums can only be assigned `int` values - decoder.decode_error('`raw` attribute cannot be used with enum fields')! - } $else $if field.typ is ?string { - position := decoder.current_node.value.position - end := position + decoder.current_node.value.length - - val.$(field.name) = decoder.json[position..end] - decoder.current_node = decoder.current_node.next - - for { - if decoder.current_node == unsafe { nil } - || decoder.current_node.value.position + decoder.current_node.value.length >= end { - break - } + mut matched := false + field_idx = 0 + $for field in T.fields { + if !matched { + field_info := field_infos[field_idx] + field_can_match := (!field_info.is_skip || field_info.is_required) + && !(field_info.is_omitempty + && decoder.is_empty_value(decoder.current_node.next.value)) + field_name_matches := key_info.length - 2 == field_info.json_name_len && unsafe { + vmemcmp(decoder.json.str + key_info.position + 1, field_info.json_name_ptr, field_info.json_name_len) == 0 + } + if field_can_match && field_name_matches { + // value node + decoder.current_node = decoder.current_node.next + + if field_info.is_skip { + // Preserve the existing inline decode behavior for `skip`+`required`. + decoded_mask = mark_struct_field_decoded(decoded_mask, mut + decoded_fields, field_idx) + if decoder.current_node != unsafe { nil } { decoder.current_node = decoder.current_node.next } - } $else $if field.typ is string { - position := decoder.current_node.value.position - end := position + decoder.current_node.value.length - - val.$(field.name) = decoder.json[position..end] - decoder.current_node = decoder.current_node.next + } else if field_info.is_raw { + $if field.unaliased_typ is $enum { + // workaround to avoid the error: enums can only be assigned `int` values + decoder.decode_error('`raw` attribute cannot be used with enum fields')! + } $else $if field.typ is ?string { + position := decoder.current_node.value.position + end := position + decoder.current_node.value.length + + val.$(field.name) = decoder.json[position..end] + decoder.current_node = decoder.current_node.next - for { - if decoder.current_node == unsafe { nil } - || decoder.current_node.value.position + decoder.current_node.value.length >= end { - break + for { + if decoder.current_node == unsafe { nil } + || decoder.current_node.value.position + decoder.current_node.value.length >= end { + break + } + decoder.current_node = decoder.current_node.next } + } $else $if field.typ is string { + position := decoder.current_node.value.position + end := position + decoder.current_node.value.length + + val.$(field.name) = decoder.json[position..end] decoder.current_node = decoder.current_node.next - } - } $else { - decoder.decode_error('`raw` attribute can only be used with string fields')! - } - } else { - $if field.typ is $option { - // it would be nicer to do this at the start of the function - // but options cant be passed to generic functions - if decoder.current_node.value.value_kind == .null { - val.$(field.name) = none - - if decoder.current_node != unsafe { nil } { + + for { + if decoder.current_node == unsafe { nil } + || decoder.current_node.value.position + decoder.current_node.value.length >= end { + break + } decoder.current_node = decoder.current_node.next } - } else { - $if field.indirections == 1 { + } $else { + decoder.decode_error('`raw` attribute can only be used with string fields')! + } + } else { + $if field.typ is $option { + // it would be nicer to do this at the start of the function + // but options cant be passed to generic functions + if decoder.current_node.value.value_kind == .null { + val.$(field.name) = none + + if decoder.current_node != unsafe { nil } { + decoder.current_node = decoder.current_node.next + } + } else { + $if field.indirections == 1 { + mut decoded_ptr := + create_decoded_option_ptr(val.$(field.name)) + decoder.decode_value(mut decoded_ptr)! + val.$(field.name) = decoded_ptr + } $else { + mut unwrapped_val := create_value_from_optional(val.$(field.name)) or { + return + } + decoder.decode_value(mut unwrapped_val)! + val.$(field.name) = unwrapped_val + } + } + } $else $if field.unaliased_typ is $array_dynamic { + val.$(field.name).clear() + decoder.decode_array(mut val.$(field.name))! + } $else $if field.unaliased_typ is $map { + decoder.decode_map(mut val.$(field.name))! + } $else $if field.indirections == 1 { + if decoder.current_node.value.value_kind == .null { + val.$(field.name) = unsafe { nil } + + if decoder.current_node != unsafe { nil } { + decoder.current_node = decoder.current_node.next + } + } else { mut decoded_ptr := - create_decoded_option_ptr(val.$(field.name)) + create_decoded_ptr(val.$(field.name)) decoder.decode_value(mut decoded_ptr)! val.$(field.name) = decoded_ptr - } $else { - mut unwrapped_val := create_value_from_optional(val.$(field.name)) or { - return - } - decoder.decode_value(mut unwrapped_val)! - val.$(field.name) = unwrapped_val - } - } - } $else $if field.unaliased_typ is $array_dynamic { - val.$(field.name).clear() - decoder.decode_array(mut val.$(field.name))! - } $else $if field.unaliased_typ is $map { - decoder.decode_map(mut val.$(field.name))! - } $else $if field.indirections == 1 { - if decoder.current_node.value.value_kind == .null { - val.$(field.name) = unsafe { nil } - - if decoder.current_node != unsafe { nil } { - decoder.current_node = decoder.current_node.next } - } else { - mut decoded_ptr := create_decoded_ptr(val.$(field.name)) - decoder.decode_value(mut decoded_ptr)! - val.$(field.name) = decoded_ptr - } - } $else { - mut decoded_field_value := val.$(field.name) - decoder.decode_value(mut decoded_field_value)! - $if field.unaliased_typ is $map { - val.$(field.name) = decoded_field_value.move() } $else { - val.$(field.name) = decoded_field_value + mut decoded_field_value := val.$(field.name) + decoder.decode_value(mut decoded_field_value)! + $if field.unaliased_typ is $map { + val.$(field.name) = decoded_field_value.move() + } $else { + val.$(field.name) = decoded_field_value + } } } - } - decoded_mask = mark_struct_field_decoded(decoded_mask, mut - decoded_fields, field_idx) - matched = true + decoded_mask = mark_struct_field_decoded(decoded_mask, mut + decoded_fields, field_idx) + matched = true + } } + field_idx++ + } + if !matched { + // The key doesn't match any field in the struct, skip the entire value + // including all nested objects/arrays. + decoder.current_node = decoder.current_node.next + decoder.skip_current_value() } - field_idx++ - } - if !matched { - // The key doesn't match any field in the struct, skip the entire value - // including all nested objects/arrays. - decoder.current_node = decoder.current_node.next - decoder.skip_current_value() } - } - // check if all required fields are present - field_idx = 0 - $for field in T.fields { - field_info := field_infos[field_idx] - if field_info.is_required - && !struct_field_is_decoded(decoded_mask, decoded_fields, field_idx) { - decoder.decode_error('missing required field `${field.name}`')! + // check if all required fields are present + field_idx = 0 + $for field in T.fields { + field_info := field_infos[field_idx] + if field_info.is_required + && !struct_field_is_decoded(decoded_mask, decoded_fields, field_idx) { + decoder.decode_error('missing required field `${field.name}`')! + } + field_idx++ } - field_idx++ } + } else { + decoder.decode_error('Expected object, but got ${struct_info.value_kind}')! } - } else { - decoder.decode_error('Expected object, but got ${struct_info.value_kind}')! - } - return - } $else $if T.unaliased_typ is bool { - value_info := decoder.current_node.value + return + } $else $if T.unaliased_typ is bool { + value_info := decoder.current_node.value - if value_info.value_kind != .boolean { - decoder.decode_error('Expected boolean, but got ${value_info.value_kind}')! - } + if value_info.value_kind != .boolean { + decoder.decode_error('Expected boolean, but got ${value_info.value_kind}')! + } - unsafe { - val = vmemcmp(decoder.json.str + value_info.position, c'true', 'true'.len) == 0 - } - } $else $if T.unaliased_typ is $float || T.unaliased_typ is $int { - value_info := decoder.current_node.value - - if value_info.value_kind == .number { - unsafe { decoder.decode_number(&val)! } - } else if value_info.value_kind == .string && !decoder.strict { - // In default mode, try to parse quoted strings as numbers - val = decoder.decode_number_from_string[T]()! - } else { - decoder.decode_error('Expected number, but got ${value_info.value_kind}')! + unsafe { + val = vmemcmp(decoder.json.str + value_info.position, c'true', 'true'.len) == 0 + } + } $else $if T.unaliased_typ is $float || T.unaliased_typ is $int { + value_info := decoder.current_node.value + + if value_info.value_kind == .number { + unsafe { decoder.decode_number(&val)! } + } else if value_info.value_kind == .string && !decoder.strict { + // In default mode, try to parse quoted strings as numbers + val = decoder.decode_number_from_string[T]()! + } else { + decoder.decode_error('Expected number, but got ${value_info.value_kind}')! + } + } $else $if T.unaliased_typ is $enum { + decoder.decode_enum(mut val)! + } $else { + decoder.decode_error('cannot decode value with ${typeof(val).name} type')! } - } $else $if T.unaliased_typ is $enum { - decoder.decode_enum(mut val)! - } $else { - decoder.decode_error('cannot decode value with ${typeof(val).name} type')! - } - if decoder.current_node != unsafe { nil } { - decoder.current_node = decoder.current_node.next - } + if decoder.current_node != unsafe { nil } { + decoder.current_node = decoder.current_node.next + } + } // $else (not voidptr / interface) } fn (mut decoder Decoder) decode_string[T](mut val T) ! { @@ -1103,117 +1120,121 @@ fn (mut decoder Decoder) decode_string[T](mut val T) ! { } fn (mut decoder Decoder) decode_array[T](mut val []T) ! { - array_info := decoder.current_node.value + $if T is $interface { + decoder.skip_current_value() + return + } $else $if T.unaliased_typ is voidptr { + decoder.skip_current_value() + return + } $else { + array_info := decoder.current_node.value - if array_info.value_kind == .array { - decoder.current_node = decoder.current_node.next + if array_info.value_kind == .array { + decoder.current_node = decoder.current_node.next - array_position := array_info.position - array_end := array_position + array_info.length + array_position := array_info.position + array_end := array_position + array_info.length - for { - if decoder.current_node == unsafe { nil } - || decoder.current_node.value.position >= array_end { - break - } + for { + if decoder.current_node == unsafe { nil } + || decoder.current_node.value.position >= array_end { + break + } - mut array_element := T{} + mut array_element := T{} - $if T.indirections == 1 { - if decoder.current_node.value.value_kind == .null { - if decoder.current_node != unsafe { nil } { - decoder.current_node = decoder.current_node.next + $if T.indirections == 1 { + if decoder.current_node.value.value_kind == .null { + if decoder.current_node != unsafe { nil } { + decoder.current_node = decoder.current_node.next + } + } else { + mut decoded_ptr := create_decoded_ptr(array_element) + decoder.decode_value(mut decoded_ptr)! + array_element = decoded_ptr } - } else { - mut decoded_ptr := create_decoded_ptr(array_element) - decoder.decode_value(mut decoded_ptr)! - array_element = decoded_ptr + } $else { + decoder.decode_value(mut array_element)! } - } $else { - decoder.decode_value(mut array_element)! - } - val << array_element + val << array_element + } + } else { + decoder.decode_error('Expected array, but got ${array_info.value_kind}')! } - } else { - decoder.decode_error('Expected array, but got ${array_info.value_kind}')! } } fn (mut decoder Decoder) decode_map[V](mut val map[string]V) ! { - map_info := decoder.current_node.value + $if V is $interface { + decoder.skip_current_value() + return + } $else $if V.unaliased_typ is voidptr { + decoder.skip_current_value() + return + } $else { + map_info := decoder.current_node.value - if map_info.value_kind == .object { - map_position := map_info.position - map_end := map_position + map_info.length + if map_info.value_kind == .object { + map_position := map_info.position + map_end := map_position + map_info.length - decoder.current_node = decoder.current_node.next - for { - if decoder.current_node == unsafe { nil } - || decoder.current_node.value.position >= map_end { - break - } + decoder.current_node = decoder.current_node.next + for { + if decoder.current_node == unsafe { nil } + || decoder.current_node.value.position >= map_end { + break + } - key_info := decoder.current_node.value + key_info := decoder.current_node.value - if key_info.position >= map_end { - break - } + if key_info.position >= map_end { + break + } - key_str := decoder.json[key_info.position + 1..key_info.position + key_info.length - 1] + key_str := decoder.json[key_info.position + 1..key_info.position + key_info.length - + 1] - decoder.current_node = decoder.current_node.next + decoder.current_node = decoder.current_node.next - value_info := decoder.current_node.value + value_info := decoder.current_node.value - if value_info.position + value_info.length > map_end { - break - } + if value_info.position + value_info.length > map_end { + break + } - mut map_value := V{} + mut map_value := V{} - $if V.indirections == 1 { - if decoder.current_node.value.value_kind == .null { - if decoder.current_node != unsafe { nil } { - decoder.current_node = decoder.current_node.next + $if V.indirections == 1 { + if decoder.current_node.value.value_kind == .null { + if decoder.current_node != unsafe { nil } { + decoder.current_node = decoder.current_node.next + } + } else { + mut decoded_ptr := create_decoded_ptr(map_value) + decoder.decode_value(mut decoded_ptr)! + map_value = decoded_ptr } - } else { - mut decoded_ptr := create_decoded_ptr(map_value) - decoder.decode_value(mut decoded_ptr)! - map_value = decoded_ptr + } $else { + decoder.decode_value(mut map_value)! } - } $else { - decoder.decode_value(mut map_value)! - } - $if K is string { - $if V is $map { - val[key_str] = map_value.move() - } $else { + $if V.unaliased_typ is $map { + // V is itself a map (alias) - skip assignment to avoid V compiler + // confusion about element type of nested map aliases. + } $else $if K is string { val[key_str] = map_value - } - } $else $if K is rune { - $if V is $map { - val[rune(key_str.int())] = map_value.move() - } $else { + } $else $if K is rune { val[rune(key_str.int())] = map_value - } - } $else $if K is $int { - $if V is $map { - val[K(key_str.int())] = map_value.move() - } $else { + } $else $if K is $int { val[K(key_str.int())] = map_value - } - } $else { - $if V is $map { - val[key_str] = map_value.move() } $else { val[key_str] = map_value } } + } else { + decoder.decode_error('Expected object, but got ${map_info.value_kind}')! } - } else { - decoder.decode_error('Expected object, but got ${map_info.value_kind}')! } } diff --git a/vlib/x/json2/encode.v b/vlib/x/json2/encode.v index 2297b0157..6661b7086 100644 --- a/vlib/x/json2/encode.v +++ b/vlib/x/json2/encode.v @@ -36,7 +36,11 @@ pub fn encode[T](val T, config EncoderOptions) string { } fn (mut encoder Encoder) encode_value[T](val T) { - $if T.unaliased_typ is string { + $if T is $interface { + encoder.encode_null() + } $else $if T.unaliased_typ is voidptr { + encoder.encode_null() + } $else $if T.unaliased_typ is string { encoder.encode_string(string(val)) } $else $if T.unaliased_typ is bool { encoder.encode_boolean(bool(val)) @@ -626,7 +630,13 @@ fn (mut encoder Encoder) cached_field_infos[T]() []EncoderFieldInfo { } fn (mut encoder Encoder) encode_struct_field_value[T](val T) { - $if T is $option { + $if T is $interface { + encoder.encode_null() + } $else $if T.unaliased_typ is voidptr { + encoder.encode_null() + } $else $if T.pointee_type is $interface { + encoder.encode_null() + } $else $if T is $option { if val == none { unsafe { encoder.output.push_many(null_string.str, null_string.len) } } else { -- 2.39.5