From f638caef39c3986ced69d3e2672f0ace0da244c8 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Wed, 6 May 2020 19:03:44 +0300 Subject: [PATCH] compiler: v -autofree can now compile itself --- vlib/builtin/array.v | 8 +++- vlib/builtin/js/array.v | 8 +++- vlib/builtin/js/string.v | 16 +++---- vlib/builtin/map.v | 2 +- vlib/builtin/string.v | 19 ++++----- vlib/v/ast/ast.v | 1 + vlib/v/gen/cgen.v | 90 +++++++++++++++++++++++++--------------- vlib/v/gen/fn.v | 23 +++++++--- vlib/v/parser/fn.v | 2 + 9 files changed, 106 insertions(+), 63 deletions(-) diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index 56aa9991d6..803409caa0 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -212,6 +212,12 @@ fn (a array) slice2(start, _end int, end_max bool) array { return a.slice(start, end) } +// array.clone_static returns an independent copy of a given array +// It should be used only in -autofree generated code. +fn (a array) clone_static() array { + return a.clone() +} + // array.clone returns an independent copy of a given array pub fn (a &array) clone() array { mut size := a.cap * a.element_size @@ -304,7 +310,7 @@ pub fn (a array) reverse() array { // pub fn (a []int) free() { [unsafe_fn] -pub fn (a array) free() { +pub fn (a &array) free() { // if a.is_slice { // return // } diff --git a/vlib/builtin/js/array.v b/vlib/builtin/js/array.v index 11c26a2017..56e6cef8ab 100644 --- a/vlib/builtin/js/array.v +++ b/vlib/builtin/js/array.v @@ -98,11 +98,17 @@ pub fn (a array) reverse() array { return a } +// array.clone_static returns an independent copy of a given array +// It should be used only in -autofree generated code. +fn (a array) clone_static() array { + return a.clone() +} + pub fn (a array) clone() array { return a } -pub fn (a array) free() { +pub fn (a &array) free() { } // "[ 'a', 'b', 'c' ]" diff --git a/vlib/builtin/js/string.v b/vlib/builtin/js/string.v index 31afb2dde0..a64818e3ee 100644 --- a/vlib/builtin/js/string.v +++ b/vlib/builtin/js/string.v @@ -26,6 +26,11 @@ pub fn tos(s byteptr) string { } } +// string.clone_static returns an independent copy of a given array +// It should be used only in -autofree generated code. +fn (a string) clone_static() string { + return a.clone() +} pub fn (a string) clone() string { return a @@ -241,18 +246,9 @@ pub fn (c byte) is_letter() bool { return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) } -pub fn (s string) free() { +pub fn (s &string) free() { } -/* -fn (arr []string) free() { - for s in arr { - s.free() - } - C.free(arr.data) -} -*/ - // all_before('23:34:45.234', '.') == '23:34:45' pub fn (s string) all_before(dot string) string { pos := s.index(dot) diff --git a/vlib/builtin/map.v b/vlib/builtin/map.v index a76de46231..1534b76f3f 100644 --- a/vlib/builtin/map.v +++ b/vlib/builtin/map.v @@ -413,7 +413,7 @@ pub fn (m &map) keys() []string { } [unsafe_fn] -pub fn (m map) free() { +pub fn (m &map) free() { free(m.metas) for i := u32(0); i < m.key_values.size; i++ { if m.key_values.keys[i].str == 0 { diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 85d728c8b4..1adc990d1a 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -104,6 +104,12 @@ pub fn tos3(s charptr) string { } } +// string.clone_static returns an independent copy of a given array +// It should be used only in -autofree generated code. +fn (a string) clone_static() string { + return a.clone() +} + pub fn (a string) clone() string { mut b := string{ len: a.len @@ -1141,7 +1147,7 @@ pub fn (u ustring) at(idx int) string { return u.substr(idx, idx + 1) } -fn (u ustring) free() { +fn (u &ustring) free() { u.runes.free() } @@ -1165,19 +1171,10 @@ pub fn (c byte) is_letter() bool { return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) } -pub fn (s string) free() { +pub fn (s &string) free() { free(s.str) } -/* -fn (arr []string) free() { - for s in arr { - s.free() - } - C.free(arr.data) -} -*/ - // all_before('23:34:45.234', '.') == '23:34:45' pub fn (s string) all_before(dot string) string { pos := s.index(dot) or { diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index e7cd4eaf00..7bc7c79a15 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -278,6 +278,7 @@ pub: name string expr Expr is_mut bool + is_arg bool // fn args should not be autofreed mut: typ table.Type pos token.Position diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index ddf04c0570..0dc0d26bf7 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -134,12 +134,12 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string for file in files { g.file = file // println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len') - building_v := true && (g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v')) +// building_v := true && (g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v')) is_test := g.file.path.ends_with('.vv') || g.file.path.ends_with('_test.v') if g.file.path.ends_with('_test.v') { g.is_test = is_test } - if g.file.path == '' || is_test || building_v || !g.pref.autofree { + if g.file.path == '' || is_test || !g.pref.autofree { // cgen test or building V // println('autofree=false') g.autofree = false @@ -646,6 +646,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { } ast.Return { g.write_defer_stmts_when_needed() + g.write_autofree_stmts_when_needed(it) g.return_statement(it) } ast.StructDecl { @@ -980,7 +981,7 @@ fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym table.TypeSymbol, ad if add_eq { g.write('=') } - g.write(' array_clone(&') + g.write(' array_clone_static(') g.expr(val) g.write(')') } else if g.autofree && right_sym.kind == .string && is_ident { @@ -988,15 +989,16 @@ fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym table.TypeSymbol, ad g.write('=') } // `str1 = str2` => `str1 = str2.clone()` - g.write(' string_clone(') + g.write(' string_clone_static(') g.expr(val) g.write(')') } return true } -fn (mut g Gen) free_scope_vars(pos int) { - println('free_scope_vars($pos)') +fn (mut g Gen) autofree_scope_vars(pos int) string { + // eprintln('> free_scope_vars($pos)') + mut freeing_code := '' scope := g.file.scope.innermost(pos) for _, obj in scope.objects { match obj { @@ -1006,34 +1008,56 @@ fn (mut g Gen) free_scope_vars(pos int) { // continue // } v := *it - println(v.name) - // println(v.typ) - sym := g.table.get_type_symbol(v.typ) is_optional := v.typ.flag_is(.optional) - if sym.kind == .array && !is_optional { - g.writeln('\tarray_free($v.name); // autofreed') - } - if sym.kind == .string && !is_optional { - // Don't free simple string literals. - t := typeof(v.expr) - match v.expr { - ast.StringLiteral { - g.writeln('// str literal') - continue - } - else { - // NOTE/TODO: assign_stmt multi returns variables have no expr - // since the type comes from the called fns return type - g.writeln('// other ' + t) - continue - } - } - g.writeln('string_free($v.name); // autofreed') + if is_optional { + // TODO: free optionals + continue } + freeing_code += g.autofree_variable(v) } else {} } } + return freeing_code +} + +fn (g &Gen) autofree_variable(v ast.Var) string { + sym := g.table.get_type_symbol(v.typ) + // eprintln(' > var name: ${v.name:-20s} | is_arg: ${v.is_arg.str():6} | var type: ${int(v.typ):8} | type_name: ${sym.name:-33s}') + if sym.kind == .array { + return g.autofree_var_call('array_free', v) + } + if sym.kind == .string { + // Don't free simple string literals. + t := typeof(v.expr) + match v.expr { + ast.StringLiteral { + return '// str literal\n' + } + else { + // NOTE/TODO: assign_stmt multi returns variables have no expr + // since the type comes from the called fns return type + return '// other ' + t + '\n' + } + } + return g.autofree_var_call('string_free', v) + } + if sym.has_method('free') { + return g.autofree_var_call(c_name(sym.name) + '_free', v) + } + return '' +} + +fn (g &Gen) autofree_var_call(free_fn_name string, v ast.Var) string { + if v.is_arg { + // fn args should not be autofreed + return '' + } + if v.typ.is_ptr() { + return '\t${free_fn_name}($v.name); // autofreed ptr var\n' + }else{ + return '\t${free_fn_name}(&$v.name); // autofreed var\n' + } } fn (mut g Gen) expr(node ast.Expr) { @@ -2221,10 +2245,10 @@ fn (mut g Gen) write_init_function() { g.writeln('void _vcleanup() {') // g.writeln('puts("cleaning up...");') if g.is_importing_os() { - g.writeln('free(_const_os__args.data);') - g.writeln('string_free(_const_os__wd_at_startup);') + g.writeln('array_free(&_const_os__args);') + g.writeln('string_free(&_const_os__wd_at_startup);') } - g.writeln('free(_const_strconv__ftoa__powers_of_10.data);') + g.writeln('array_free(&_const_strconv__ftoa__powers_of_10);') g.writeln('}') } } @@ -3215,7 +3239,7 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp, str_fn_name string) { g.auto_str_funcs.writeln('\tstring tmp1 = string_add(tos3("${styp}("), tos3(${typename}_str((${convertor})it).str));') } g.auto_str_funcs.writeln('\tstring tmp2 = string_add(tmp1, tos3(")"));') - g.auto_str_funcs.writeln('\tstring_free(tmp1);') + g.auto_str_funcs.writeln('\tstring_free(&tmp1);') g.auto_str_funcs.writeln('\treturn tmp2;') g.auto_str_funcs.writeln('}') } @@ -3324,7 +3348,7 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp, str_fn_name string) { g.auto_str_funcs.writeln('\t\tstrings__Builder_write(&sb, x);') if g.pref.autofree && info.elem_type != table.bool_type { // 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);') } g.auto_str_funcs.writeln('\t\tif (i < a.len-1) {') g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write(&sb, tos3(", "));') diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index cd6eea3fd7..59af1906a9 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -159,11 +159,6 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { } g.stmts(it.stmts) // //////////// - if g.autofree { - // println('\n\ncalling free for fn $it.name') - g.free_scope_vars(it.body_pos.pos) - } - // ///////// if is_main { if g.autofree { g.writeln('\t_vcleanup();') @@ -173,6 +168,11 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { } } g.write_defer_stmts_when_needed() + // ///////// + if g.autofree { + // TODO: remove this, when g.write_autofree_stmts_when_needed works properly + g.writeln( g.autofree_scope_vars(it.body_pos.pos) ) + } if is_main { g.writeln('\treturn 0;') } @@ -183,6 +183,17 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { } } +fn (mut g Gen) write_autofree_stmts_when_needed(r ast.Return) { + // TODO: write_autofree_stmts_when_needed should account for the current local scope vars. + // TODO: write_autofree_stmts_when_needed should not free the returned variables. + // It may require rewriting g.return_statement to assign the expressions + // to temporary variables, then protecting *them* from autofreeing ... + g.writeln('/* autofreeings before return: -------') + //g.write( g.autofree_scope_vars(r.pos.pos) ) + g.write( g.autofree_scope_vars(g.fn_decl.body_pos.pos) ) + g.writeln('--------------------------------------------------- */') +} + fn (mut g Gen) write_defer_stmts_when_needed() { if g.defer_stmts.len > 0 { g.write_defer_stmts() @@ -440,7 +451,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { // tmps << tmp g.write('string $tmp = ${str_fn_name}(') g.expr(node.args[0].expr) - g.writeln('); ${print_method}($tmp); string_free($tmp); //MEM2 $styp') + g.writeln('); ${print_method}($tmp); string_free(&$tmp); //MEM2 $styp') } else { expr := node.args[0].expr is_var := match expr { diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 5c4dbb35cc..33340eca59 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -175,6 +175,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { is_mut: arg.is_mut pos: p.tok.position() is_used: true + is_arg: true }) // Do not allow `mut` with simple types // TODO move to checker? @@ -280,6 +281,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn { typ: arg.typ pos: p.tok.position() is_used: true + is_arg: true }) } mut return_type := table.void_type