From 39ad6da506f335bd9e2812dad9025bd219e91923 Mon Sep 17 00:00:00 2001 From: yuyi Date: Sat, 18 Sep 2021 14:38:42 +0800 Subject: [PATCH] checker, cgen: generate .free() methods for custom structs automatically (#11529) --- vlib/v/checker/checker.v | 2 + vlib/v/gen/c/auto_free_methods.v | 65 ++++++++++++++++++++++++++++++++ vlib/v/gen/c/fn.v | 6 +++ vlib/v/tests/autogen_free_test.v | 17 +++++++++ 4 files changed, 90 insertions(+) create mode 100644 vlib/v/gen/c/auto_free_methods.v create mode 100644 vlib/v/tests/autogen_free_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index c3029186d8..9b7149269b 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2472,6 +2472,8 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } c.fail_if_unreadable(node.left, left_type, 'receiver') return ast.string_type + } else if method_name == 'free' { + return ast.void_type } // call struct field fn type // TODO: can we use SelectorExpr for all? this dosent really belong here diff --git a/vlib/v/gen/c/auto_free_methods.v b/vlib/v/gen/c/auto_free_methods.v new file mode 100644 index 0000000000..bcd11841f5 --- /dev/null +++ b/vlib/v/gen/c/auto_free_methods.v @@ -0,0 +1,65 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module c + +import v.ast +import strings + +fn (mut g Gen) gen_free_method_for_type(typ ast.Type) string { + styp := g.typ(typ).replace('*', '') + mut sym := g.table.get_type_symbol(g.unwrap_generic(typ)) + mut fn_name := styp_to_free_fn_name(styp) + if mut sym.info is ast.Alias { + if sym.info.is_import { + sym = g.table.get_type_symbol(sym.info.parent_type) + } + } + if sym.kind == .map { + return 'map_free' + } else if sym.kind == .array { + return 'array_free' + } + + if sym.has_method('free') { + return fn_name + } + match mut sym.info { + ast.Struct { + g.gen_free_for_struct(sym.info, styp, fn_name) + } + else { + println(g.table.type_str(typ)) + verror("could not generate free method '$fn_name' for type '$styp'") + } + } + return fn_name +} + +fn (mut g Gen) gen_free_for_struct(info ast.Struct, styp string, fn_name string) { + g.type_definitions.writeln('void ${fn_name}($styp* it); // auto') + mut fn_builder := strings.new_builder(512) + defer { + g.auto_fn_definitions << fn_builder.str() + } + fn_builder.writeln('void ${fn_name}($styp* it) {') + for field in info.fields { + sym := g.table.get_type_symbol(g.unwrap_generic(field.typ)) + + if sym.kind !in [.string, .array, .map, .struct_] { + continue + } + mut field_styp := g.typ(field.typ).replace('*', '') + field_styp_fn_name := if sym.has_method('free') { + '${field_styp}_free' + } else { + g.gen_free_method_for_type(field.typ) + } + fn_builder.writeln('\t${field_styp_fn_name}(&(it->$field.name));') + } + fn_builder.writeln('}') +} + +[inline] +fn styp_to_free_fn_name(styp string) string { + return styp.replace_each(['*', '', '.', '__', ' ', '__']) + '_free' +} diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index f1759e5241..799866e20e 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -796,6 +796,12 @@ fn (mut g Gen) method_call(node ast.CallExpr) { rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0) } g.gen_str_method_for_type(rec_type) + } else if node.name == 'free' { + mut rec_type := node.receiver_type + if rec_type.has_flag(.shared_f) { + rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0) + } + g.gen_free_method_for_type(rec_type) } mut has_cast := false if left_sym.kind == .map && node.name in ['clone', 'move'] { diff --git a/vlib/v/tests/autogen_free_test.v b/vlib/v/tests/autogen_free_test.v new file mode 100644 index 0000000000..f22fd1d2b1 --- /dev/null +++ b/vlib/v/tests/autogen_free_test.v @@ -0,0 +1,17 @@ +struct Info { + name string + notes []string + maps map[int]int + info SubInfo +} + +struct SubInfo { + path string + files []string +} + +fn test_autogen_free() { + info := &Info{} + info.free() + assert true +}