gen: implement a `[manualfree]` tag, for functions, that want to do their own memory management
parent
06bcd404b0
commit
083dc23db8
|
@ -7,6 +7,7 @@
|
||||||
- `byte.str()` has been fixed and works like with all other numbers. `byte.ascii_str()` has been added.
|
- `byte.str()` has been fixed and works like with all other numbers. `byte.ascii_str()` has been added.
|
||||||
- Smart cast in for loops: `for mut x is string {}`.
|
- Smart cast in for loops: `for mut x is string {}`.
|
||||||
- `[noinit]` struct attribute to disallow direct struct initialization with `Foo{}`.
|
- `[noinit]` struct attribute to disallow direct struct initialization with `Foo{}`.
|
||||||
|
- `[manualfree]` attribute for functions, that want to do their own memory management.
|
||||||
|
|
||||||
## V 0.2.1
|
## V 0.2.1
|
||||||
*30 Dec 2020*
|
*30 Dec 2020*
|
||||||
|
|
|
@ -67,3 +67,10 @@ These build flags are enabled on `build` and `run` as long as the backend is set
|
||||||
|
|
||||||
-keepc
|
-keepc
|
||||||
Do not remove the temporary .tmp.c and .tmp.c.rsp files. Also do not use a random prefix for them, so they would be fixed and predictable.
|
Do not remove the temporary .tmp.c and .tmp.c.rsp files. Also do not use a random prefix for them, so they would be fixed and predictable.
|
||||||
|
|
||||||
|
-autofree
|
||||||
|
Free memory used in functions automatically.
|
||||||
|
|
||||||
|
-manualfree
|
||||||
|
Do not free memory used in functions (the developer has to put x.free() and unsafe{free(x)} calls manually in this mode).
|
||||||
|
Some short lived applications, like compilers and other CLI tools are more performant without autofree.
|
||||||
|
|
|
@ -2517,7 +2517,8 @@ Python, Go, or Java, except there's no heavy GC tracing everything or expensive
|
||||||
each object.
|
each object.
|
||||||
|
|
||||||
For developers willing to have more low level control, autofree can be disabled with
|
For developers willing to have more low level control, autofree can be disabled with
|
||||||
`-noautofree`.
|
`-manualfree`, or by adding a `[manualfree]` on each function that wants manage its
|
||||||
|
memory manually.
|
||||||
|
|
||||||
Note: right now autofree is hidden behind the -autofree flag. It will be enabled by
|
Note: right now autofree is hidden behind the -autofree flag. It will be enabled by
|
||||||
default in V 0.3.
|
default in V 0.3.
|
||||||
|
|
|
@ -296,6 +296,7 @@ pub:
|
||||||
is_pub bool
|
is_pub bool
|
||||||
is_variadic bool
|
is_variadic bool
|
||||||
is_anon bool
|
is_anon bool
|
||||||
|
is_manualfree bool // true, when [manualfree] is used on a fn
|
||||||
receiver Field
|
receiver Field
|
||||||
receiver_pos token.Position // `(u User)` in `fn (u User) name()` position
|
receiver_pos token.Position // `(u User)` in `fn (u User) name()` position
|
||||||
is_method bool
|
is_method bool
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
module gen
|
module gen
|
||||||
|
|
||||||
import v.table
|
import v.table
|
||||||
import v.pref
|
|
||||||
import v.util
|
import v.util
|
||||||
|
|
||||||
fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp string, str_fn_name string) {
|
fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp string, str_fn_name string) {
|
||||||
|
@ -230,7 +229,7 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, x);')
|
g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, x);')
|
||||||
if g.pref.autofree && typ != table.bool_type {
|
if g.is_autofree && typ != table.bool_type {
|
||||||
// no need to free "true"/"false" literals
|
// no need to free "true"/"false" literals
|
||||||
g.auto_str_funcs.writeln('\t\tstring_free(&x);')
|
g.auto_str_funcs.writeln('\t\tstring_free(&x);')
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ mut:
|
||||||
stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement
|
stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement
|
||||||
skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements)
|
skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements)
|
||||||
right_is_opt bool
|
right_is_opt bool
|
||||||
autofree bool
|
is_autofree bool // false, inside the bodies of fns marked with [manualfree], otherwise === g.pref.autofree
|
||||||
indent int
|
indent int
|
||||||
empty_line bool
|
empty_line bool
|
||||||
is_test bool
|
is_test bool
|
||||||
|
@ -186,7 +186,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
|
||||||
table: table
|
table: table
|
||||||
pref: pref
|
pref: pref
|
||||||
fn_decl: 0
|
fn_decl: 0
|
||||||
autofree: true
|
is_autofree: true
|
||||||
indent: -1
|
indent: -1
|
||||||
module_built: module_built
|
module_built: module_built
|
||||||
timers: util.new_timers(timers_should_print)
|
timers: util.new_timers(timers_should_print)
|
||||||
|
@ -216,9 +216,9 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
|
||||||
if g.file.path == '' || !g.pref.autofree {
|
if g.file.path == '' || !g.pref.autofree {
|
||||||
// cgen test or building V
|
// cgen test or building V
|
||||||
// println('autofree=false')
|
// println('autofree=false')
|
||||||
g.autofree = false
|
g.is_autofree = false
|
||||||
} else {
|
} else {
|
||||||
g.autofree = true
|
g.is_autofree = true
|
||||||
autofree_used = true
|
autofree_used = true
|
||||||
}
|
}
|
||||||
// anon fn may include assert and thus this needs
|
// anon fn may include assert and thus this needs
|
||||||
|
@ -232,7 +232,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
|
||||||
}
|
}
|
||||||
g.timers.start('cgen common')
|
g.timers.start('cgen common')
|
||||||
if autofree_used {
|
if autofree_used {
|
||||||
g.autofree = true // so that void _vcleanup is generated
|
g.is_autofree = true // so that void _vcleanup is generated
|
||||||
}
|
}
|
||||||
// to make sure type idx's are the same in cached mods
|
// to make sure type idx's are the same in cached mods
|
||||||
if g.pref.build_mode == .build_module {
|
if g.pref.build_mode == .build_module {
|
||||||
|
@ -799,7 +799,7 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) {
|
||||||
g.write('')
|
g.write('')
|
||||||
g.write(')')
|
g.write(')')
|
||||||
}
|
}
|
||||||
if g.pref.autofree && !g.inside_vweb_tmpl && stmts.len > 0 {
|
if g.is_autofree && !g.inside_vweb_tmpl && stmts.len > 0 {
|
||||||
// use the first stmt to get the scope
|
// use the first stmt to get the scope
|
||||||
stmt := stmts[0]
|
stmt := stmts[0]
|
||||||
// stmt := stmts[stmts.len-1]
|
// stmt := stmts[stmts.len-1]
|
||||||
|
@ -878,7 +878,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// continue or break
|
// continue or break
|
||||||
if g.pref.autofree && !g.is_builtin_mod {
|
if g.is_autofree && !g.is_builtin_mod {
|
||||||
g.writeln('// free before continue/break')
|
g.writeln('// free before continue/break')
|
||||||
g.autofree_scope_vars_stop(node.pos.pos - 1, node.pos.line_nr, true,
|
g.autofree_scope_vars_stop(node.pos.pos - 1, node.pos.line_nr, true,
|
||||||
g.branch_parent_pos)
|
g.branch_parent_pos)
|
||||||
|
@ -935,7 +935,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
ast.ExprStmt {
|
ast.ExprStmt {
|
||||||
g.write_v_source_line_info(node.pos)
|
g.write_v_source_line_info(node.pos)
|
||||||
// af := g.pref.autofree && node.expr is ast.CallExpr && !g.is_builtin_mod
|
// af := g.autofree && node.expr is ast.CallExpr && !g.is_builtin_mod
|
||||||
// if af {
|
// if af {
|
||||||
// g.autofree_call_pregen(node.expr as ast.CallExpr)
|
// g.autofree_call_pregen(node.expr as ast.CallExpr)
|
||||||
// }
|
// }
|
||||||
|
@ -1129,9 +1129,9 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
ast.Return {
|
ast.Return {
|
||||||
g.write_defer_stmts_when_needed()
|
g.write_defer_stmts_when_needed()
|
||||||
// af := g.pref.autofree && node.exprs.len > 0 && node.exprs[0] is ast.CallExpr && !g.is_builtin_mod
|
// af := g.autofree && node.exprs.len > 0 && node.exprs[0] is ast.CallExpr && !g.is_builtin_mod
|
||||||
/*
|
/*
|
||||||
af := g.pref.autofree && !g.is_builtin_mod
|
af := g.autofree && !g.is_builtin_mod
|
||||||
if false && af {
|
if false && af {
|
||||||
g.writeln('// ast.Return free')
|
g.writeln('// ast.Return free')
|
||||||
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
||||||
|
@ -1172,7 +1172,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
// If we have temporary string exprs to free after this statement, do it. e.g.:
|
// If we have temporary string exprs to free after this statement, do it. e.g.:
|
||||||
// `foo('a' + 'b')` => `tmp := 'a' + 'b'; foo(tmp); string_free(&tmp);`
|
// `foo('a' + 'b')` => `tmp := 'a' + 'b'; foo(tmp); string_free(&tmp);`
|
||||||
if g.pref.autofree {
|
if g.is_autofree {
|
||||||
// if node is ast.ExprStmt {&& node.expr is ast.CallExpr {
|
// if node is ast.ExprStmt {&& node.expr is ast.CallExpr {
|
||||||
if node !is ast.FnDecl {
|
if node !is ast.FnDecl {
|
||||||
// p := node.position()
|
// p := node.position()
|
||||||
|
@ -1584,7 +1584,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
||||||
}
|
}
|
||||||
// Free the old value assigned to this string var (only if it's `str = [new value]`
|
// Free the old value assigned to this string var (only if it's `str = [new value]`
|
||||||
// or `x.str = [new value]` )
|
// or `x.str = [new value]` )
|
||||||
mut af := g.pref.autofree && !g.is_builtin_mod && assign_stmt.op == .assign && assign_stmt.left_types.len ==
|
mut af := g.is_autofree && !g.is_builtin_mod && assign_stmt.op == .assign && assign_stmt.left_types.len ==
|
||||||
1 &&
|
1 &&
|
||||||
(assign_stmt.left[0] is ast.Ident || assign_stmt.left[0] is ast.SelectorExpr)
|
(assign_stmt.left[0] is ast.Ident || assign_stmt.left[0] is ast.SelectorExpr)
|
||||||
// assign_stmt.left_types[0] in [table.string_type, table.array_type] &&
|
// assign_stmt.left_types[0] in [table.string_type, table.array_type] &&
|
||||||
|
@ -1623,7 +1623,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
||||||
}
|
}
|
||||||
// Autofree tmp arg vars
|
// Autofree tmp arg vars
|
||||||
// first_right := assign_stmt.right[0]
|
// first_right := assign_stmt.right[0]
|
||||||
// af := g.pref.autofree && first_right is ast.CallExpr && !g.is_builtin_mod
|
// af := g.autofree && first_right is ast.CallExpr && !g.is_builtin_mod
|
||||||
// if af {
|
// if af {
|
||||||
// g.autofree_call_pregen(first_right as ast.CallExpr)
|
// g.autofree_call_pregen(first_right as ast.CallExpr)
|
||||||
// }
|
// }
|
||||||
|
@ -1641,7 +1641,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
||||||
// }
|
// }
|
||||||
// int pos = *(int*)_t190.data;
|
// int pos = *(int*)_t190.data;
|
||||||
mut tmp_opt := ''
|
mut tmp_opt := ''
|
||||||
is_optional := g.pref.autofree &&
|
is_optional := g.is_autofree &&
|
||||||
(assign_stmt.op in [.decl_assign, .assign]) && assign_stmt.left_types.len == 1 && assign_stmt.right[0] is
|
(assign_stmt.op in [.decl_assign, .assign]) && assign_stmt.left_types.len == 1 && assign_stmt.right[0] is
|
||||||
ast.CallExpr
|
ast.CallExpr
|
||||||
if is_optional {
|
if is_optional {
|
||||||
|
@ -1727,7 +1727,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
||||||
if left.left_type.is_ptr() {
|
if left.left_type.is_ptr() {
|
||||||
g.write('*')
|
g.write('*')
|
||||||
}
|
}
|
||||||
needs_clone := info.elem_type == table.string_type && g.pref.autofree
|
needs_clone := info.elem_type == table.string_type && g.is_autofree
|
||||||
if needs_clone {
|
if needs_clone {
|
||||||
g.write('/*1*/string_clone(')
|
g.write('/*1*/string_clone(')
|
||||||
}
|
}
|
||||||
|
@ -1969,7 +1969,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
||||||
g.write(', ')
|
g.write(', ')
|
||||||
}
|
}
|
||||||
mut cloned := false
|
mut cloned := false
|
||||||
if g.autofree && right_sym.kind in [.array, .string] {
|
if g.is_autofree && right_sym.kind in [.array, .string] {
|
||||||
if g.gen_clone_assignment(val, right_sym, false) {
|
if g.gen_clone_assignment(val, right_sym, false) {
|
||||||
cloned = true
|
cloned = true
|
||||||
}
|
}
|
||||||
|
@ -1980,7 +1980,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
||||||
// `pos := s.index(...
|
// `pos := s.index(...
|
||||||
// `int pos = *(int)_t10.data;`
|
// `int pos = *(int)_t10.data;`
|
||||||
g.write('*($styp*)')
|
g.write('*($styp*)')
|
||||||
if g.pref.autofree {
|
if g.is_autofree {
|
||||||
g.write(tmp_opt + '.data/*FFz*/')
|
g.write(tmp_opt + '.data/*FFz*/')
|
||||||
g.right_is_opt = false
|
g.right_is_opt = false
|
||||||
g.is_assign_rhs = false
|
g.is_assign_rhs = false
|
||||||
|
@ -2037,7 +2037,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if unwrap_optional {
|
if unwrap_optional {
|
||||||
if g.pref.autofree {
|
if g.is_autofree {
|
||||||
// g.write(tmp_opt + '/*FF*/')
|
// g.write(tmp_opt + '/*FF*/')
|
||||||
} else {
|
} else {
|
||||||
g.write('.data')
|
g.write('.data')
|
||||||
|
@ -2150,7 +2150,7 @@ fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym table.TypeSymbol, ad
|
||||||
if val !is ast.Ident && val !is ast.SelectorExpr {
|
if val !is ast.Ident && val !is ast.SelectorExpr {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if g.autofree && right_sym.kind == .array {
|
if g.is_autofree && right_sym.kind == .array {
|
||||||
// `arr1 = arr2` => `arr1 = arr2.clone()`
|
// `arr1 = arr2` => `arr1 = arr2.clone()`
|
||||||
if add_eq {
|
if add_eq {
|
||||||
g.write('=')
|
g.write('=')
|
||||||
|
@ -2158,7 +2158,7 @@ fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym table.TypeSymbol, ad
|
||||||
g.write(' array_clone_static(')
|
g.write(' array_clone_static(')
|
||||||
g.expr(val)
|
g.expr(val)
|
||||||
g.write(')')
|
g.write(')')
|
||||||
} else if g.autofree && right_sym.kind == .string {
|
} else if g.is_autofree && right_sym.kind == .string {
|
||||||
if add_eq {
|
if add_eq {
|
||||||
g.write('=')
|
g.write('=')
|
||||||
}
|
}
|
||||||
|
@ -2407,8 +2407,8 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
// if g.fileis('1.strings') {
|
// if g.fileis('1.strings') {
|
||||||
// println('before:' + node.autofree_pregen)
|
// println('before:' + node.autofree_pregen)
|
||||||
// }
|
// }
|
||||||
if g.pref.autofree && !g.is_builtin_mod && !g.is_js_call && g.strs_to_free0.len ==
|
if g.is_autofree && !g.is_builtin_mod && !g.is_js_call && g.strs_to_free0.len == 0 &&
|
||||||
0 && !g.inside_lambda { // && g.inside_ternary ==
|
!g.inside_lambda { // && g.inside_ternary ==
|
||||||
// if len != 0, that means we are handling call expr inside call expr (arg)
|
// if len != 0, that means we are handling call expr inside call expr (arg)
|
||||||
// and it'll get messed up here, since it's handled recursively in autofree_call_pregen()
|
// and it'll get messed up here, since it's handled recursively in autofree_call_pregen()
|
||||||
// so just skip it
|
// so just skip it
|
||||||
|
@ -2419,9 +2419,9 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
g.strs_to_free0 = []
|
g.strs_to_free0 = []
|
||||||
// println('pos=$node.pos.pos')
|
// println('pos=$node.pos.pos')
|
||||||
}
|
}
|
||||||
// if g.pref.autofree && node.autofree_pregen != '' { // g.strs_to_free0.len != 0 {
|
// if g.autofree && node.autofree_pregen != '' { // g.strs_to_free0.len != 0 {
|
||||||
/*
|
/*
|
||||||
if g.pref.autofree {
|
if g.autofree {
|
||||||
s := g.autofree_pregen[node.pos.pos.str()]
|
s := g.autofree_pregen[node.pos.pos.str()]
|
||||||
if s != '' {
|
if s != '' {
|
||||||
// g.insert_before_stmt('/*START2*/' + g.strs_to_free0.join('\n') + '/*END*/')
|
// g.insert_before_stmt('/*START2*/' + g.strs_to_free0.join('\n') + '/*END*/')
|
||||||
|
@ -3167,7 +3167,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
||||||
if elem_sym.kind == .interface_ && node.right_type != info.elem_type {
|
if elem_sym.kind == .interface_ && node.right_type != info.elem_type {
|
||||||
g.interface_call(node.right_type, info.elem_type)
|
g.interface_call(node.right_type, info.elem_type)
|
||||||
}
|
}
|
||||||
// if g.pref.autofree
|
// if g.autofree
|
||||||
needs_clone := info.elem_type == table.string_type && !g.is_builtin_mod
|
needs_clone := info.elem_type == table.string_type && !g.is_builtin_mod
|
||||||
if needs_clone {
|
if needs_clone {
|
||||||
g.write('string_clone(')
|
g.write('string_clone(')
|
||||||
|
@ -3713,12 +3713,12 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
|
||||||
// Always use this in -autofree, since ?: can have tmp expressions that have to be freed.
|
// Always use this in -autofree, since ?: can have tmp expressions that have to be freed.
|
||||||
first_branch := node.branches[0]
|
first_branch := node.branches[0]
|
||||||
needs_tmp_var := node.is_expr &&
|
needs_tmp_var := node.is_expr &&
|
||||||
(g.pref.autofree || (g.pref.experimental &&
|
(g.is_autofree || (g.pref.experimental &&
|
||||||
(first_branch.stmts.len > 1 || (first_branch.stmts[0] is ast.ExprStmt &&
|
(first_branch.stmts.len > 1 || (first_branch.stmts[0] is ast.ExprStmt &&
|
||||||
(first_branch.stmts[0] as ast.ExprStmt).expr is ast.IfExpr))))
|
(first_branch.stmts[0] as ast.ExprStmt).expr is ast.IfExpr))))
|
||||||
/*
|
/*
|
||||||
needs_tmp_var := node.is_expr &&
|
needs_tmp_var := node.is_expr &&
|
||||||
(g.pref.autofree || g.pref.experimental) &&
|
(g.autofree || g.pref.experimental) &&
|
||||||
(node.branches[0].stmts.len > 1 || node.branches[0].stmts[0] is ast.IfExpr)
|
(node.branches[0].stmts.len > 1 || node.branches[0].stmts[0] is ast.IfExpr)
|
||||||
*/
|
*/
|
||||||
tmp := if needs_tmp_var { g.new_tmp_var() } else { '' }
|
tmp := if needs_tmp_var { g.new_tmp_var() } else { '' }
|
||||||
|
@ -3957,7 +3957,7 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
|
||||||
.function { 'voidptr*' }
|
.function { 'voidptr*' }
|
||||||
else { '$elem_type_str*' }
|
else { '$elem_type_str*' }
|
||||||
}
|
}
|
||||||
needs_clone := info.elem_type == table.string_type_idx && g.pref.autofree &&
|
needs_clone := info.elem_type == table.string_type_idx && g.is_autofree &&
|
||||||
!g.is_assign_lhs
|
!g.is_assign_lhs
|
||||||
if needs_clone {
|
if needs_clone {
|
||||||
g.write('/*2*/string_clone(')
|
g.write('/*2*/string_clone(')
|
||||||
|
@ -4141,7 +4141,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
|
||||||
g.writeln('$styp $tmp = {.ok = true};')
|
g.writeln('$styp $tmp = {.ok = true};')
|
||||||
g.writeln('return $tmp;')
|
g.writeln('return $tmp;')
|
||||||
} else {
|
} else {
|
||||||
if g.pref.autofree && !g.is_builtin_mod {
|
if g.is_autofree && !g.is_builtin_mod {
|
||||||
g.writeln('// free before return (no values returned)')
|
g.writeln('// free before return (no values returned)')
|
||||||
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
||||||
}
|
}
|
||||||
|
@ -4275,7 +4275,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
|
||||||
g.writeln('return $opt_tmp;')
|
g.writeln('return $opt_tmp;')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
free := g.pref.autofree && !g.is_builtin_mod // node.exprs[0] is ast.CallExpr
|
free := g.is_autofree && !g.is_builtin_mod // node.exprs[0] is ast.CallExpr
|
||||||
mut tmp := ''
|
mut tmp := ''
|
||||||
if free {
|
if free {
|
||||||
// `return foo(a, b, c)`
|
// `return foo(a, b, c)`
|
||||||
|
@ -4404,7 +4404,7 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, val string, typ ta
|
||||||
cname := '_const_$name'
|
cname := '_const_$name'
|
||||||
g.definitions.writeln('$styp $cname; // inited later')
|
g.definitions.writeln('$styp $cname; // inited later')
|
||||||
g.inits[mod].writeln('\t$cname = $val;')
|
g.inits[mod].writeln('\t$cname = $val;')
|
||||||
if g.pref.autofree {
|
if g.is_autofree {
|
||||||
if styp.starts_with('array_') {
|
if styp.starts_with('array_') {
|
||||||
g.cleanups[mod].writeln('\tarray_free(&$cname);')
|
g.cleanups[mod].writeln('\tarray_free(&$cname);')
|
||||||
}
|
}
|
||||||
|
@ -4491,7 +4491,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
||||||
}
|
}
|
||||||
field_type_sym := g.table.get_type_symbol(field.typ)
|
field_type_sym := g.table.get_type_symbol(field.typ)
|
||||||
mut cloned := false
|
mut cloned := false
|
||||||
if g.autofree && !field.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
|
if g.is_autofree && !field.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
|
||||||
g.write('/*clone1*/')
|
g.write('/*clone1*/')
|
||||||
if g.gen_clone_assignment(field.expr, field_type_sym, false) {
|
if g.gen_clone_assignment(field.expr, field_type_sym, false) {
|
||||||
cloned = true
|
cloned = true
|
||||||
|
@ -4560,7 +4560,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
||||||
mut cloned := false
|
mut cloned := false
|
||||||
is_interface := expected_field_type_sym.kind == .interface_ &&
|
is_interface := expected_field_type_sym.kind == .interface_ &&
|
||||||
field_type_sym.kind != .interface_
|
field_type_sym.kind != .interface_
|
||||||
if g.autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
|
if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
|
||||||
g.write('/*clone1*/')
|
g.write('/*clone1*/')
|
||||||
if g.gen_clone_assignment(sfield.expr, field_type_sym, false) {
|
if g.gen_clone_assignment(sfield.expr, field_type_sym, false) {
|
||||||
cloned = true
|
cloned = true
|
||||||
|
@ -4705,7 +4705,7 @@ fn (mut g Gen) write_init_function() {
|
||||||
} else {
|
} else {
|
||||||
g.writeln('void _vinit() {')
|
g.writeln('void _vinit() {')
|
||||||
}
|
}
|
||||||
if g.pref.autofree {
|
if g.is_autofree {
|
||||||
// Pre-allocate the string buffer
|
// Pre-allocate the string buffer
|
||||||
// s_str_buf_size := os.getenv('V_STRBUF_MB')
|
// s_str_buf_size := os.getenv('V_STRBUF_MB')
|
||||||
// mb_size := if s_str_buf_size == '' { 1 } else { s_str_buf_size.int() }
|
// mb_size := if s_str_buf_size == '' { 1 } else { s_str_buf_size.int() }
|
||||||
|
@ -4735,7 +4735,7 @@ fn (mut g Gen) write_init_function() {
|
||||||
if g.pref.printfn_list.len > 0 && '_vinit' in g.pref.printfn_list {
|
if g.pref.printfn_list.len > 0 && '_vinit' in g.pref.printfn_list {
|
||||||
println(g.out.after(fn_vinit_start_pos))
|
println(g.out.after(fn_vinit_start_pos))
|
||||||
}
|
}
|
||||||
if g.autofree {
|
if g.is_autofree {
|
||||||
fn_vcleanup_start_pos := g.out.len
|
fn_vcleanup_start_pos := g.out.len
|
||||||
g.writeln('void _vcleanup() {')
|
g.writeln('void _vcleanup() {')
|
||||||
// g.writeln('puts("cleaning up...");')
|
// g.writeln('puts("cleaning up...");')
|
||||||
|
|
|
@ -75,7 +75,7 @@ fn (mut g Gen) gen_c_main_header() {
|
||||||
g.writeln('')
|
g.writeln('')
|
||||||
}
|
}
|
||||||
if g.is_importing_os() {
|
if g.is_importing_os() {
|
||||||
if g.autofree {
|
if g.is_autofree {
|
||||||
g.writeln('free(_const_os__args.data); // empty, inited in _vinit()')
|
g.writeln('free(_const_os__args.data); // empty, inited in _vinit()')
|
||||||
}
|
}
|
||||||
if g.pref.os == .windows {
|
if g.pref.os == .windows {
|
||||||
|
@ -90,7 +90,7 @@ fn (mut g Gen) gen_c_main_header() {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) gen_c_main_footer() {
|
pub fn (mut g Gen) gen_c_main_footer() {
|
||||||
if g.autofree {
|
if g.is_autofree {
|
||||||
g.writeln('\t_vcleanup();')
|
g.writeln('\t_vcleanup();')
|
||||||
}
|
}
|
||||||
g.writeln('\treturn 0;')
|
g.writeln('\treturn 0;')
|
||||||
|
@ -99,7 +99,7 @@ pub fn (mut g Gen) gen_c_main_footer() {
|
||||||
|
|
||||||
pub fn (mut g Gen) gen_c_android_sokol_main() {
|
pub fn (mut g Gen) gen_c_android_sokol_main() {
|
||||||
// Weave autofree into sokol lifecycle callback(s)
|
// Weave autofree into sokol lifecycle callback(s)
|
||||||
if g.autofree {
|
if g.is_autofree {
|
||||||
g.writeln('// Wrapping cleanup/free callbacks for sokol to include _vcleanup()
|
g.writeln('// Wrapping cleanup/free callbacks for sokol to include _vcleanup()
|
||||||
void (*_vsokol_user_cleanup_ptr)(void);
|
void (*_vsokol_user_cleanup_ptr)(void);
|
||||||
void (*_vsokol_user_cleanup_cb_ptr)(void *);
|
void (*_vsokol_user_cleanup_cb_ptr)(void *);
|
||||||
|
@ -126,7 +126,7 @@ sapp_desc sokol_main(int argc, char* argv[]) {
|
||||||
_vinit();
|
_vinit();
|
||||||
main__main();
|
main__main();
|
||||||
')
|
')
|
||||||
if g.autofree {
|
if g.is_autofree {
|
||||||
g.writeln(' // Wrap user provided cleanup/free functions for sokol to be able to call _vcleanup()
|
g.writeln(' // Wrap user provided cleanup/free functions for sokol to be able to call _vcleanup()
|
||||||
if (g_desc.cleanup_cb) {
|
if (g_desc.cleanup_cb) {
|
||||||
_vsokol_user_cleanup_ptr = g_desc.cleanup_cb;
|
_vsokol_user_cleanup_ptr = g_desc.cleanup_cb;
|
||||||
|
@ -170,7 +170,7 @@ pub fn (mut g Gen) write_tests_main() {
|
||||||
g.writeln('\tmain__BenchedTests_end_testing(&bt);')
|
g.writeln('\tmain__BenchedTests_end_testing(&bt);')
|
||||||
}
|
}
|
||||||
g.writeln('')
|
g.writeln('')
|
||||||
if g.autofree {
|
if g.is_autofree {
|
||||||
g.writeln('\t_vcleanup();')
|
g.writeln('\t_vcleanup();')
|
||||||
}
|
}
|
||||||
g.writeln('\treturn g_test_fails > 0;')
|
g.writeln('\treturn g_test_fails > 0;')
|
||||||
|
|
|
@ -16,6 +16,15 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
g.returned_var_name = ''
|
g.returned_var_name = ''
|
||||||
|
//
|
||||||
|
old_g_autofree := g.is_autofree
|
||||||
|
if it.is_manualfree {
|
||||||
|
g.is_autofree = false
|
||||||
|
}
|
||||||
|
defer {
|
||||||
|
g.is_autofree = old_g_autofree
|
||||||
|
}
|
||||||
|
//
|
||||||
// if g.fileis('vweb.v') {
|
// if g.fileis('vweb.v') {
|
||||||
// println('\ngen_fn_decl() $it.name $it.is_generic $g.cur_generic_type')
|
// println('\ngen_fn_decl() $it.name $it.is_generic $g.cur_generic_type')
|
||||||
// }
|
// }
|
||||||
|
@ -263,12 +272,12 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
||||||
defer {
|
defer {
|
||||||
g.inside_call = false
|
g.inside_call = false
|
||||||
}
|
}
|
||||||
gen_or := node.or_block.kind != .absent && !g.pref.autofree
|
gen_or := node.or_block.kind != .absent && !g.is_autofree
|
||||||
// if gen_or {
|
// if gen_or {
|
||||||
// g.writeln('/*start*/')
|
// g.writeln('/*start*/')
|
||||||
// }
|
// }
|
||||||
is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs
|
is_gen_or_and_assign_rhs := gen_or && g.is_assign_rhs
|
||||||
cur_line := if is_gen_or_and_assign_rhs && !g.pref.autofree {
|
cur_line := if is_gen_or_and_assign_rhs && !g.is_autofree {
|
||||||
line := g.go_before_stmt(0)
|
line := g.go_before_stmt(0)
|
||||||
g.out.write(tabs[g.indent])
|
g.out.write(tabs[g.indent])
|
||||||
line
|
line
|
||||||
|
@ -290,8 +299,8 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
||||||
} else {
|
} else {
|
||||||
g.fn_call(node)
|
g.fn_call(node)
|
||||||
}
|
}
|
||||||
if gen_or { // && !g.pref.autofree {
|
if gen_or { // && !g.autofree {
|
||||||
if !g.pref.autofree {
|
if !g.is_autofree {
|
||||||
g.or_block(tmp_opt, node.or_block, node.return_type)
|
g.or_block(tmp_opt, node.or_block, node.return_type)
|
||||||
}
|
}
|
||||||
if is_gen_or_and_assign_rhs {
|
if is_gen_or_and_assign_rhs {
|
||||||
|
@ -557,7 +566,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||||
tmp2 = g.new_tmp_var()
|
tmp2 = g.new_tmp_var()
|
||||||
g.writeln('Option_$typ $tmp2 = $fn_name ($json_obj);')
|
g.writeln('Option_$typ $tmp2 = $fn_name ($json_obj);')
|
||||||
}
|
}
|
||||||
if !g.pref.autofree {
|
if !g.is_autofree {
|
||||||
g.write('cJSON_Delete($json_obj); //del')
|
g.write('cJSON_Delete($json_obj); //del')
|
||||||
}
|
}
|
||||||
g.write('\n$cur_line')
|
g.write('\n$cur_line')
|
||||||
|
@ -594,7 +603,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||||
// check if alias parent also not a string
|
// check if alias parent also not a string
|
||||||
if typ != table.string_type {
|
if typ != table.string_type {
|
||||||
expr := node.args[0].expr
|
expr := node.args[0].expr
|
||||||
if g.autofree && !typ.has_flag(.optional) {
|
if g.is_autofree && !typ.has_flag(.optional) {
|
||||||
// Create a temporary variable so that the value can be freed
|
// Create a temporary variable so that the value can be freed
|
||||||
tmp := g.new_tmp_var()
|
tmp := g.new_tmp_var()
|
||||||
// tmps << tmp
|
// tmps << tmp
|
||||||
|
@ -640,7 +649,7 @@ fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) {
|
||||||
// g.writeln('// autofree_call_pregen()')
|
// g.writeln('// autofree_call_pregen()')
|
||||||
// Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression,
|
// Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression,
|
||||||
// like `foo(get_string())` or `foo(a + b)`
|
// like `foo(get_string())` or `foo(a + b)`
|
||||||
mut free_tmp_arg_vars := g.autofree && !g.is_builtin_mod && node.args.len > 0 && !node.args[0].typ.has_flag(.optional) // TODO copy pasta checker.v
|
mut free_tmp_arg_vars := g.is_autofree && !g.is_builtin_mod && node.args.len > 0 && !node.args[0].typ.has_flag(.optional) // TODO copy pasta checker.v
|
||||||
if !free_tmp_arg_vars {
|
if !free_tmp_arg_vars {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -775,7 +784,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
|
||||||
if is_variadic && i == expected_types.len - 1 {
|
if is_variadic && i == expected_types.len - 1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
use_tmp_var_autofree := g.autofree && arg.typ == table.string_type && arg.is_tmp_autofree &&
|
use_tmp_var_autofree := g.is_autofree && arg.typ == table.string_type && arg.is_tmp_autofree &&
|
||||||
!g.inside_const && !g.is_builtin_mod
|
!g.inside_const && !g.is_builtin_mod
|
||||||
// g.write('/* af=$arg.is_tmp_autofree */')
|
// g.write('/* af=$arg.is_tmp_autofree */')
|
||||||
mut is_interface := false
|
mut is_interface := false
|
||||||
|
|
|
@ -156,6 +156,7 @@ pub fn (mut p Parser) call_args() []ast.CallArg {
|
||||||
fn (mut p Parser) fn_decl() ast.FnDecl {
|
fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
p.top_level_statement_start()
|
p.top_level_statement_start()
|
||||||
start_pos := p.tok.position()
|
start_pos := p.tok.position()
|
||||||
|
is_manualfree := p.attrs.contains('manualfree')
|
||||||
is_deprecated := p.attrs.contains('deprecated')
|
is_deprecated := p.attrs.contains('deprecated')
|
||||||
is_direct_arr := p.attrs.contains('direct_array_access')
|
is_direct_arr := p.attrs.contains('direct_array_access')
|
||||||
mut is_unsafe := p.attrs.contains('unsafe')
|
mut is_unsafe := p.attrs.contains('unsafe')
|
||||||
|
@ -395,6 +396,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
stmts: stmts
|
stmts: stmts
|
||||||
return_type: return_type
|
return_type: return_type
|
||||||
params: params
|
params: params
|
||||||
|
is_manualfree: is_manualfree
|
||||||
is_deprecated: is_deprecated
|
is_deprecated: is_deprecated
|
||||||
is_direct_arr: is_direct_arr
|
is_direct_arr: is_direct_arr
|
||||||
is_pub: is_pub
|
is_pub: is_pub
|
||||||
|
|
|
@ -81,7 +81,6 @@ pub mut:
|
||||||
use_cache bool // = true
|
use_cache bool // = true
|
||||||
retry_compilation bool = true
|
retry_compilation bool = true
|
||||||
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
|
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
|
||||||
no_auto_free bool // `v -nofree` disable automatic `free()` insertion for better performance in some applications (e.g. compilers)
|
|
||||||
// TODO Convert this into a []string
|
// TODO Convert this into a []string
|
||||||
cflags string // Additional options which will be passed to the C compiler.
|
cflags string // Additional options which will be passed to the C compiler.
|
||||||
// For example, passing -cflags -Os will cause the C compiler to optimize the generated binaries for size.
|
// For example, passing -cflags -Os will cause the C compiler to optimize the generated binaries for size.
|
||||||
|
@ -92,7 +91,8 @@ pub mut:
|
||||||
ccompiler_type CompilerType // the type of the C compiler used
|
ccompiler_type CompilerType // the type of the C compiler used
|
||||||
third_party_option string
|
third_party_option string
|
||||||
building_v bool
|
building_v bool
|
||||||
autofree bool
|
autofree bool // `v -manualfree` => false, `v -autofree` => true; false by default for now.
|
||||||
|
// Disabling `free()` insertion results in better performance in some applications (e.g. compilers)
|
||||||
compress bool
|
compress bool
|
||||||
// skip_builtin bool // Skips re-compilation of the builtin module
|
// skip_builtin bool // Skips re-compilation of the builtin module
|
||||||
// to increase compilation time.
|
// to increase compilation time.
|
||||||
|
@ -213,6 +213,10 @@ pub fn parse_args(args []string) (&Preferences, string) {
|
||||||
res.autofree = true
|
res.autofree = true
|
||||||
res.build_options << arg
|
res.build_options << arg
|
||||||
}
|
}
|
||||||
|
'-manualfree' {
|
||||||
|
res.autofree = false
|
||||||
|
res.build_options << arg
|
||||||
|
}
|
||||||
'-compress' {
|
'-compress' {
|
||||||
res.compress = true
|
res.compress = true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue