From 5b98cde81129f5596e66f105ca4119bbb9b39f45 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Mon, 29 Jun 2020 17:47:01 +0200 Subject: [PATCH] cgen: free temp strings --- vlib/v/gen/cgen.v | 157 ++++++-------------------------------- vlib/v/gen/comptime.v | 2 +- vlib/v/gen/str.v | 172 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 193 insertions(+), 138 deletions(-) diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 18aa8d7cd7..318c9b8fc7 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -84,12 +84,14 @@ mut: is_builtin_mod bool hotcode_fn_names []string fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last* - cur_fn &ast.FnDecl + cur_fn &ast.FnDecl = 0 cur_generic_type table.Type // `int`, `string`, etc in `foo()` sql_i int sql_stmt_name string sql_side SqlExprSide // left or right, to distinguish idents in `name == name` inside_vweb_tmpl bool + inside_return bool + strs_to_free string } const ( @@ -261,6 +263,10 @@ pub fn (mut g Gen) init() { if g.pref.is_test || 'test' in g.pref.compile_defines { g.comptime_defines.writeln('#define _VTEST (1)') } + if g.pref.autofree { + g.comptime_defines.writeln('#define _VAUTOFREE (1)') + // g.comptime_defines.writeln('unsigned char* g_cur_str;') + } if g.pref.is_livemain || g.pref.is_liveshared { g.generate_hotcode_reloading_declarations() } @@ -569,6 +575,16 @@ fn (mut g Gen) stmts(stmts []ast.Stmt) { fn (mut g Gen) stmt(node ast.Stmt) { g.stmt_path_pos << g.out.len + defer { + // If have temporary string exprs to free after this statement, do it. e.g.: + // `foo('a' + 'b')` => `tmp := 'a' + 'b'; foo(tmp); string_free(&tmp);` + if false && g.pref.autofree { + if g.strs_to_free != '' { + g.writeln(g.strs_to_free) + g.strs_to_free = '' + } + } + } // println('cgen.stmt()') // g.writeln('//// stmt start') match node { @@ -1592,26 +1608,7 @@ fn (mut g Gen) expr(node ast.Expr) { g.sql_select_expr(node) } ast.StringLiteral { - if node.is_raw { - escaped_val := node.val.replace_each(['"', '\\"', '\\', '\\\\']) - g.write('tos_lit("$escaped_val")') - return - } - escaped_val := node.val.replace_each(['"', '\\"', '\r\n', '\\n', '\n', '\\n']) - if g.is_c_call || node.language == .c { - // In C calls we have to generate C strings - // `C.printf("hi")` => `printf("hi");` - g.write('"$escaped_val"') - } else { - // TODO calculate the literal's length in V, it's a bit tricky with all the - // escape characters. - // Clang and GCC optimize `strlen("lorem ipsum")` to `11` - // g.write('tos4("$escaped_val", strlen("$escaped_val"))') - // g.write('tos4("$escaped_val", $it.val.len)') - // g.write('_SLIT("$escaped_val")') - g.write('tos_lit("$escaped_val")') - // g.write('tos_lit("$escaped_val")') - } + g.string_literal(node) } ast.StringInterLiteral { g.string_inter_literal(node) @@ -2308,6 +2305,10 @@ fn (mut g Gen) return_statement(node ast.Return) { return } } + g.inside_return = true + defer { + g.inside_return = false + } // got to do a correct check for multireturn sym := g.table.get_type_symbol(g.fn_decl.return_type) fn_return_is_multi := sym.kind == .multi_return @@ -2945,120 +2946,6 @@ fn (g Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol { return types_sorted } -fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { - g.write('_STR("') - // Build the string with % - mut end_string := false - for i, val in node.vals { - escaped_val := val.replace_each(['"', '\\"', '\r\n', '\\n', '\n', '\\n', '%', '%%']) - if i >= node.exprs.len { - if escaped_val.len > 0 { - end_string = true - g.write('\\000') - g.write(escaped_val) - } - break - } - g.write(escaped_val) - // write correct format specifier to intermediate string - g.write('%') - fspec := node.fmts[i] - mut fmt := if node.pluss[i] { '+' } else { '' } - if node.fills[i] && node.fwidths[i] >= 0 { - fmt = '${fmt}0' - } - if node.fwidths[i] != 0 { - fmt = '$fmt${node.fwidths[i]}' - } - if node.precisions[i] != 0 { - fmt = '${fmt}.${node.precisions[i]}' - } - if fspec == `s` { - if node.fwidths[i] == 0 { - g.write('.*s') - } else { - g.write('*.*s') - } - } else if node.expr_types[i].is_float() { - g.write('$fmt${fspec:c}') - } else if node.expr_types[i].is_pointer() { - if fspec == `p` { - g.write('${fmt}p') - } else { - g.write('$fmt"PRI${fspec:c}PTR"') - } - } else if node.expr_types[i].is_int() { - if fspec == `c` { - g.write('${fmt}c') - } else { - g.write('$fmt"PRI${fspec:c}') - if node.expr_types[i] in [table.i8_type, table.byte_type] { - g.write('8') - } else if node.expr_types[i] in [table.i16_type, table.u16_type] { - g.write('16') - } else if node.expr_types[i] in [table.i64_type, table.u64_type] { - g.write('64') - } else { - g.write('32') - } - g.write('"') - } - } else { - // TODO: better check this case - g.write('$fmt"PRId32"') - } - if i < node.exprs.len - 1 { - g.write('\\000') - } - } - num_string_parts := if end_string { node.exprs.len + 1 } else { node.exprs.len } - g.write('", $num_string_parts, ') - // Build args - for i, expr in node.exprs { - if node.expr_types[i] == table.string_type { - if g.inside_vweb_tmpl { - g.write('vweb__filter(') - g.expr(expr) - g.write(')') - } else { - g.expr(expr) - } - } else if node.expr_types[i] == table.bool_type { - g.expr(expr) - g.write(' ? _SLIT("true") : _SLIT("false")') - } else if node.expr_types[i].is_number() || node.expr_types[i].is_pointer() || - node.fmts[i] == `d` { - if node.expr_types[i].is_signed() && node.fmts[i] in [`x`, `X`, `o`] { - // convert to unsigned first befors C's integer propagation strikes - if node.expr_types[i] == table.i8_type { - g.write('(byte)(') - } else if node.expr_types[i] == table.i16_type { - g.write('(u16)(') - } else if node.expr_types[i] == table.int_type { - g.write('(u32)(') - } else { - g.write('(u64)(') - } - g.expr(expr) - g.write(')') - } else { - g.expr(expr) - } - } else if node.fmts[i] == `s` { - g.gen_expr_to_string(expr, node.expr_types[i]) - } else { - g.expr(expr) - } - if node.fmts[i] == `s` && node.fwidths[i] != 0 { - g.write(', ${node.fwidths[i]}') - } - if i < node.exprs.len - 1 { - g.write(', ') - } - } - g.write(')') -} - fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool { sym := g.table.get_type_symbol(etype) sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() diff --git a/vlib/v/gen/comptime.v b/vlib/v/gen/comptime.v index 3ee15ae9bf..c30b51f01d 100644 --- a/vlib/v/gen/comptime.v +++ b/vlib/v/gen/comptime.v @@ -20,7 +20,7 @@ fn (g &Gen) comptime_call(node ast.ComptimeCall) { } } } - g.writeln('vweb__Context_html(&app->vweb, _tmpl_res_$g.fn_decl.name)') + g.writeln('vweb__Context_html(&app->vweb, _tmpl_res_$g.fn_decl.name); string_free(&_tmpl_res_$g.fn_decl.name);') return } g.writeln('// $' + 'method call. sym="$node.sym.name"') diff --git a/vlib/v/gen/str.v b/vlib/v/gen/str.v index 5ecb8df188..7b026864d9 100644 --- a/vlib/v/gen/str.v +++ b/vlib/v/gen/str.v @@ -1,8 +1,10 @@ // Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. -// Use of this source code is governed by an MIT license -// that can be found in the LICENSE file. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. module gen +import v.ast +import v.table + fn (mut g Gen) write_str_fn_definitions() { // _STR function can't be defined in vlib g.writeln(" @@ -85,6 +87,9 @@ string _STR(const char *fmt, int nfmts, ...) { #ifdef DEBUG_ALLOC //puts('_STR:'); puts(buf); +#endif +#if _VAUTOFREE + //g_cur_str = (byteptr)buf; #endif return tos2((byteptr)buf); } @@ -110,3 +115,166 @@ string _STR_TMP(const char *fmt, ...) { ") } + +fn (mut g Gen) string_literal(node ast.StringLiteral) { + if node.is_raw { + escaped_val := node.val.replace_each(['"', '\\"', '\\', '\\\\']) + g.write('tos_lit("$escaped_val")') + return + } + escaped_val := node.val.replace_each(['"', '\\"', '\r\n', '\\n', '\n', '\\n']) + if g.is_c_call || node.language == .c { + // In C calls we have to generate C strings + // `C.printf("hi")` => `printf("hi");` + g.write('"$escaped_val"') + } else { + // TODO calculate the literal's length in V, it's a bit tricky with all the + // escape characters. + // Clang and GCC optimize `strlen("lorem ipsum")` to `11` + // g.write('tos4("$escaped_val", strlen("$escaped_val"))') + // g.write('tos4("$escaped_val", $it.val.len)') + // g.write('_SLIT("$escaped_val")') + g.write('tos_lit("$escaped_val")') + // g.write('tos_lit("$escaped_val")') + } +} + +fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { + mut cur_line := '' + mut tmp := '' + free := g.pref.autofree && !g.inside_return && + g.inside_ternary == 0 && g.cur_fn != 0 && + g.cur_fn.name != '' + if free { + // Save the string expr in a temporary variable, so that it can be removed after the call. + tmp = g.new_tmp_var() + /* + scope := g.file.scope.innermost(node.pos.pos) + scope.register(tmp, ast.Var{ + name: tmp + typ: table.string_type + }) + */ + // g.insert_before_stmt('// str tmp var\nstring $tmp = ') + cur_line = g.go_before_stmt(0) + g.writeln('// free _str') + g.write('string $tmp = ') + g.strs_to_free += 'string_free(&$tmp); /*tmp str*/' + } + g.write('_STR("') + // Build the string with % + mut end_string := false + for i, val in node.vals { + escaped_val := val.replace_each(['"', '\\"', '\r\n', '\\n', '\n', '\\n', '%', '%%']) + if i >= node.exprs.len { + if escaped_val.len > 0 { + end_string = true + g.write('\\000') + g.write(escaped_val) + } + break + } + g.write(escaped_val) + // write correct format specifier to intermediate string + g.write('%') + fspec := node.fmts[i] + mut fmt := if node.pluss[i] { '+' } else { '' } + if node.fills[i] && node.fwidths[i] >= 0 { + fmt = '${fmt}0' + } + if node.fwidths[i] != 0 { + fmt = '$fmt${node.fwidths[i]}' + } + if node.precisions[i] != 0 { + fmt = '${fmt}.${node.precisions[i]}' + } + if fspec == `s` { + if node.fwidths[i] == 0 { + g.write('.*s') + } else { + g.write('*.*s') + } + } else if node.expr_types[i].is_float() { + g.write('$fmt${fspec:c}') + } else if node.expr_types[i].is_pointer() { + if fspec == `p` { + g.write('${fmt}p') + } else { + g.write('$fmt"PRI${fspec:c}PTR"') + } + } else if node.expr_types[i].is_int() { + if fspec == `c` { + g.write('${fmt}c') + } else { + g.write('$fmt"PRI${fspec:c}') + if node.expr_types[i] in [table.i8_type, table.byte_type] { + g.write('8') + } else if node.expr_types[i] in [table.i16_type, table.u16_type] { + g.write('16') + } else if node.expr_types[i] in [table.i64_type, table.u64_type] { + g.write('64') + } else { + g.write('32') + } + g.write('"') + } + } else { + // TODO: better check this case + g.write('$fmt"PRId32"') + } + if i < node.exprs.len - 1 { + g.write('\\000') + } + } + num_string_parts := if end_string { node.exprs.len + 1 } else { node.exprs.len } + g.write('", $num_string_parts, ') + // Build args + for i, expr in node.exprs { + if node.expr_types[i] == table.string_type { + if g.inside_vweb_tmpl { + g.write('vweb__filter(') + g.expr(expr) + g.write(')') + } else { + g.expr(expr) + } + } else if node.expr_types[i] == table.bool_type { + g.expr(expr) + g.write(' ? _SLIT("true") : _SLIT("false")') + } else if node.expr_types[i].is_number() || node.expr_types[i].is_pointer() || + node.fmts[i] == `d` { + if node.expr_types[i].is_signed() && node.fmts[i] in [`x`, `X`, `o`] { + // convert to unsigned first befors C's integer propagation strikes + if node.expr_types[i] == table.i8_type { + g.write('(byte)(') + } else if node.expr_types[i] == table.i16_type { + g.write('(u16)(') + } else if node.expr_types[i] == table.int_type { + g.write('(u32)(') + } else { + g.write('(u64)(') + } + g.expr(expr) + g.write(')') + } else { + g.expr(expr) + } + } else if node.fmts[i] == `s` { + g.gen_expr_to_string(expr, node.expr_types[i]) + } else { + g.expr(expr) + } + if node.fmts[i] == `s` && node.fwidths[i] != 0 { + g.write(', ${node.fwidths[i]}') + } + if i < node.exprs.len - 1 { + g.write(', ') + } + } + g.write(')') + if free { + g.writeln(';') + g.write(cur_line) + g.write(tmp) + } +}