v2 / vlib / v / gen / c / auto_free_methods.v
331 lines · 310 sloc · 10.2 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
3module c
4
5import v.ast
6import strings
7
8@[inline]
9fn (mut g Gen) register_free_method(typ ast.Type) {
10 if g.type_has_unresolved_generic_parts(typ) {
11 return
12 }
13 if typ.has_flag(.shared_f) {
14 g.get_free_method(typ.clear_flag(.shared_f).set_nr_muls(0))
15 } else {
16 g.get_free_method(typ)
17 }
18}
19
20fn (mut g Gen) get_free_method(typ ast.Type) string {
21 if typ in g.autofree_methods {
22 return g.autofree_methods[typ]
23 }
24 mut sym := g.table.sym(g.unwrap_generic(typ))
25 if mut sym.info is ast.Alias {
26 if sym.info.is_import {
27 sym = g.table.sym(sym.info.parent_type)
28 }
29 }
30 styp := g.styp(typ).replace('*', '')
31 fn_name := styp_to_free_fn_name(styp)
32 if sym.has_method_with_generic_parent('free') {
33 g.autofree_methods[typ] = fn_name
34 return fn_name
35 }
36 g.autofree_methods[typ] = fn_name
37 return fn_name
38}
39
40fn (mut g Gen) gen_free_methods() {
41 for typ, _ in g.autofree_methods {
42 if g.type_has_unresolved_generic_parts(typ) {
43 continue
44 }
45 g.gen_free_method(typ)
46 }
47}
48
49fn (mut g Gen) gen_free_method(typ ast.Type) string {
50 styp := g.styp(typ).replace('*', '')
51 mut fn_name := styp_to_free_fn_name(styp)
52 if g.type_has_unresolved_generic_parts(typ) {
53 return fn_name
54 }
55 deref_typ := if typ.has_flag(.option) { typ } else { typ.set_nr_muls(0) }
56 if deref_typ in g.generated_free_methods {
57 return fn_name
58 }
59 // Also check by C function name to prevent duplicate definitions when
60 // different type IDs map to the same C name (e.g. nested array types).
61 if fn_name in g.generated_free_fn_names {
62 return fn_name
63 }
64 g.generated_free_methods[deref_typ] = true
65 g.generated_free_fn_names[fn_name] = true
66
67 objtyp := g.unwrap_generic(typ)
68 mut sym := g.table.sym(objtyp)
69 if mut sym.info is ast.Alias {
70 if sym.info.is_import {
71 sym = g.table.sym(sym.info.parent_type)
72 }
73 }
74 if sym.kind != .interface && sym.has_method_with_generic_parent('free') {
75 return fn_name
76 }
77
78 match mut sym.info {
79 ast.Struct {
80 g.gen_free_for_struct(objtyp, sym.info, styp, fn_name, sym.is_builtin())
81 }
82 ast.Array {
83 g.gen_free_for_array(sym.info, styp, fn_name)
84 }
85 ast.Map {
86 g.gen_free_for_map(objtyp, styp, fn_name)
87 }
88 ast.Interface {
89 g.gen_free_for_interface(sym, sym.info, styp, fn_name)
90 }
91 ast.SumType {
92 g.gen_free_for_sumtype(sym.info, styp, fn_name)
93 }
94 else {
95 println(g.table.type_str(typ))
96 // print_backtrace()
97 println("could not generate free method '${fn_name}' for type '${styp}'")
98 // verror("could not generate free method '${fn_name}' for type '${styp}'")
99 }
100 }
101
102 return fn_name
103}
104
105fn (mut g Gen) gen_free_for_interface(sym ast.TypeSymbol, info ast.Interface, styp string, fn_name string) {
106 g.definitions.writeln('${g.static_non_parallel}void ${fn_name}(${styp}* it);')
107 mut fn_builder := strings.new_builder(128)
108 defer {
109 g.auto_fn_definitions << fn_builder.str()
110 }
111 fn_builder.writeln('${g.static_non_parallel}void ${fn_name}(${styp}* it) {')
112 for t in info.types {
113 typ_ := g.unwrap_generic(t)
114 sub_sym := g.table.sym(typ_)
115 if sub_sym.kind !in [.string, .array, .map, .struct] {
116 continue
117 }
118 if !sub_sym.has_method_with_generic_parent('free') {
119 continue
120 }
121 type_styp := g.gen_type_name_for_free_call(typ_)
122 free_fn_name := if sub_sym.is_builtin() {
123 'builtin__${type_styp}_free'
124 } else {
125 '${type_styp}_free'
126 }
127 fn_builder.writeln('\tif (it->_typ == _${sym.cname}_${sub_sym.cname}_index) { ${free_fn_name}(it->_${sub_sym.cname}); return; }')
128 }
129 fn_builder.writeln('}')
130}
131
132fn (mut g Gen) gen_free_for_sumtype(info ast.SumType, styp string, fn_name string) {
133 g.definitions.writeln('${g.static_non_parallel}void ${fn_name}(${styp}* it);')
134 mut fn_builder := strings.new_builder(256)
135 defer {
136 g.auto_fn_definitions << fn_builder.str()
137 }
138 fn_builder.writeln('${g.static_non_parallel}void ${fn_name}(${styp}* it) {')
139 mut idxs := []ast.Type{}
140 for variant in info.variants {
141 if variant in idxs {
142 continue
143 }
144 idxs << variant
145 free_typ := variant.clear_flag(.option)
146 free_sym := g.table.sym(g.unwrap_generic(free_typ))
147 variant_name := g.get_sumtype_variant_name(variant, free_sym)
148 variant_ptr := 'it->_${variant_name}'
149 fn_builder.writeln('\tif (it->_typ == ${g.type_sidx(variant)}) {')
150 if variant.has_flag(.option) {
151 match free_sym.kind {
152 .string {
153 fn_builder.writeln('\t\tif (${variant_ptr}->state != 2) {')
154 fn_builder.writeln('\t\t\tbuiltin__string_free((${g.base_type(variant)}*)${variant_ptr}->data);')
155 fn_builder.writeln('\t\t}')
156 }
157 .array, .map, .struct, .sum_type, .interface {
158 mut free_fn_name := if free_sym.has_method('free') {
159 '${g.gen_type_name_for_free_call(free_typ)}_free'
160 } else {
161 g.gen_free_method(free_typ)
162 }
163 if free_sym.is_builtin() {
164 free_fn_name = 'builtin__${free_fn_name}'
165 }
166 fn_builder.writeln('\t\tif (${variant_ptr}->state != 2) {')
167 fn_builder.writeln('\t\t\t${free_fn_name}((${g.base_type(variant)}*)${variant_ptr}->data);')
168 fn_builder.writeln('\t\t}')
169 }
170 else {}
171 }
172 } else {
173 match free_sym.kind {
174 .string {
175 fn_builder.writeln('\t\tbuiltin__string_free(${variant_ptr});')
176 }
177 .array, .map, .struct, .sum_type, .interface {
178 mut free_fn_name := if free_sym.has_method('free') {
179 '${g.gen_type_name_for_free_call(free_typ)}_free'
180 } else {
181 g.gen_free_method(free_typ)
182 }
183 if free_sym.is_builtin() {
184 free_fn_name = 'builtin__${free_fn_name}'
185 }
186 fn_builder.writeln('\t\t${free_fn_name}(${variant_ptr});')
187 }
188 else {}
189 }
190 }
191 // Sumtypes box their active payload in heap memory via memdup/HEAP.
192 fn_builder.writeln('\t\tbuiltin___v_free(${variant_ptr});')
193 fn_builder.writeln('\t\treturn;')
194 fn_builder.writeln('\t}')
195 }
196 fn_builder.writeln('}')
197}
198
199fn (mut g Gen) gen_free_for_struct(typ ast.Type, info ast.Struct, styp string, ofn_name string, sym_is_builtin bool) {
200 mut fn_name := ofn_name
201 if sym_is_builtin {
202 fn_name = 'builtin__${fn_name}'
203 }
204 g.definitions.writeln('${g.static_non_parallel}void ${fn_name}(${styp}* it);')
205 mut fn_builder := strings.new_builder(128)
206 defer {
207 g.auto_fn_definitions << fn_builder.str()
208 }
209 fn_builder.writeln('${g.static_non_parallel}void ${fn_name}(${styp}* it) {')
210 for field in info.fields {
211 field_name := c_name(field.name)
212 sym := g.table.sym(g.unwrap_generic(field.typ))
213
214 if sym.kind !in [.string, .array, .map, .struct, .sum_type] {
215 continue
216 }
217 field_styp := g.gen_type_name_for_free_call(field.typ)
218 is_struct_option := typ.has_flag(.option)
219 free_method_typ := if field.typ.has_flag(.shared_f) {
220 field.typ.clear_flag(.shared_f).set_nr_muls(0)
221 } else {
222 field.typ
223 }
224 mut field_styp_fn_name := if sym.has_method('free') {
225 '${field_styp}_free'
226 } else {
227 g.gen_free_method(free_method_typ)
228 }
229 if sym.is_builtin() {
230 field_styp_fn_name = 'builtin__${field_styp_fn_name}'
231 }
232 is_field_option := field.typ.has_flag(.option)
233 expects_opt := field_styp_fn_name.starts_with('_option_')
234 if field.typ.has_flag(.shared_f) {
235 fn_builder.writeln('\t${field_styp_fn_name}(&(it->${field_name}->val));')
236 } else if is_struct_option {
237 opt_styp := g.base_type(typ)
238 prefix := if field.typ.is_ptr() { '' } else { '&' }
239 if is_field_option {
240 opt_field_styp := if expects_opt {
241 g.styp(field.typ)
242 } else {
243 g.base_type(field.typ)
244 }
245 suffix := if expects_opt { '' } else { '.data' }
246
247 fn_builder.writeln('\tif (((${opt_styp}*)&it->data)->${field_name}.state != 2) {')
248 fn_builder.writeln('\t\t${field_styp_fn_name}((${opt_field_styp}*)${prefix}((${opt_styp}*)&it->data)->${field_name}${suffix});')
249 fn_builder.writeln('\t}')
250 } else {
251 fn_builder.writeln('\t${field_styp_fn_name}(${prefix}((${opt_styp}*)&it->data)->${field_name});')
252 }
253 } else {
254 if is_field_option {
255 opt_field_styp := if expects_opt {
256 g.styp(field.typ)
257 } else {
258 g.base_type(field.typ)
259 }
260 suffix := if expects_opt { '' } else { '.data' }
261
262 fn_builder.writeln('\tif (it->${field_name}.state != 2) {')
263 fn_builder.writeln('\t\t${field_styp_fn_name}((${opt_field_styp}*)&(it->${field_name}${suffix}));')
264 fn_builder.writeln('\t}')
265 } else {
266 prefix := if field.typ.is_ptr() { '' } else { '&' }
267 fn_builder.writeln('\t${field_styp_fn_name}(${prefix}(it->${field_name}));')
268 }
269 }
270 }
271 fn_builder.writeln('}')
272}
273
274fn (mut g Gen) gen_type_name_for_free_call(typ ast.Type) string {
275 mut styp := g.styp(typ.set_nr_muls(0).clear_flag(.option)).replace('*', '')
276 if styp.starts_with('__shared') {
277 styp = styp.all_after('__shared__')
278 }
279 return styp
280}
281
282fn (mut g Gen) gen_free_for_array(info ast.Array, styp string, fn_name string) {
283 g.definitions.writeln('${g.static_non_parallel}void ${fn_name}(${styp}* it);')
284 mut fn_builder := strings.new_builder(128)
285 defer {
286 g.auto_fn_definitions << fn_builder.str()
287 }
288 fn_builder.writeln('${g.static_non_parallel}void ${fn_name}(${styp}* it) {')
289
290 sym := g.table.sym(g.unwrap_generic(info.elem_type))
291 if sym.kind in [.string, .array, .map, .struct, .sum_type] {
292 fn_builder.writeln('\tfor (${ast.int_type_name} i = 0; i < it->len; i++) {')
293
294 mut elem_styp := g.styp(info.elem_type).replace('*', '')
295 mut elem_styp_fn_name := if sym.has_method('free') {
296 '${elem_styp}_free'
297 } else {
298 g.gen_free_method(info.elem_type)
299 }
300 if sym.is_builtin() {
301 elem_styp_fn_name = 'builtin__${elem_styp_fn_name}'
302 }
303 fn_builder.writeln('\t\t${elem_styp_fn_name}(&(((${elem_styp}*)it->data)[i]));')
304 fn_builder.writeln('\t}')
305 }
306 fn_builder.writeln('\tbuiltin__array_free(it);')
307 fn_builder.writeln('}')
308}
309
310fn (mut g Gen) gen_free_for_map(typ ast.Type, styp string, fn_name string) {
311 g.definitions.writeln('${g.static_non_parallel}void ${fn_name}(${styp}* it);')
312 mut fn_builder := strings.new_builder(128)
313 defer {
314 g.auto_fn_definitions << fn_builder.str()
315 }
316 fn_builder.writeln('${g.static_non_parallel}void ${fn_name}(${styp}* it) {')
317
318 if typ.has_flag(.option) {
319 fn_builder.writeln('\tif (it->state != 2) {')
320 fn_builder.writeln('\t\tbuiltin__map_free((map*)&it->data);')
321 fn_builder.writeln('\t}')
322 } else {
323 fn_builder.writeln('\tbuiltin__map_free(it);')
324 }
325 fn_builder.writeln('}')
326}
327
328@[inline]
329fn styp_to_free_fn_name(styp string) string {
330 return styp.replace_each(['*', '', '.', '__', ' ', '__']) + '_free'
331}
332