cgen: free temp strings
parent
9d845262b7
commit
5b98cde811
|
@ -84,12 +84,14 @@ mut:
|
||||||
is_builtin_mod bool
|
is_builtin_mod bool
|
||||||
hotcode_fn_names []string
|
hotcode_fn_names []string
|
||||||
fn_main &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
|
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<T>()`
|
cur_generic_type table.Type // `int`, `string`, etc in `foo<T>()`
|
||||||
sql_i int
|
sql_i int
|
||||||
sql_stmt_name string
|
sql_stmt_name string
|
||||||
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
|
||||||
inside_vweb_tmpl bool
|
inside_vweb_tmpl bool
|
||||||
|
inside_return bool
|
||||||
|
strs_to_free string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -261,6 +263,10 @@ pub fn (mut g Gen) init() {
|
||||||
if g.pref.is_test || 'test' in g.pref.compile_defines {
|
if g.pref.is_test || 'test' in g.pref.compile_defines {
|
||||||
g.comptime_defines.writeln('#define _VTEST (1)')
|
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 {
|
if g.pref.is_livemain || g.pref.is_liveshared {
|
||||||
g.generate_hotcode_reloading_declarations()
|
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) {
|
fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
g.stmt_path_pos << g.out.len
|
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()')
|
// println('cgen.stmt()')
|
||||||
// g.writeln('//// stmt start')
|
// g.writeln('//// stmt start')
|
||||||
match node {
|
match node {
|
||||||
|
@ -1592,26 +1608,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
g.sql_select_expr(node)
|
g.sql_select_expr(node)
|
||||||
}
|
}
|
||||||
ast.StringLiteral {
|
ast.StringLiteral {
|
||||||
if node.is_raw {
|
g.string_literal(node)
|
||||||
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")')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ast.StringInterLiteral {
|
ast.StringInterLiteral {
|
||||||
g.string_inter_literal(node)
|
g.string_inter_literal(node)
|
||||||
|
@ -2308,6 +2305,10 @@ fn (mut g Gen) return_statement(node ast.Return) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
g.inside_return = true
|
||||||
|
defer {
|
||||||
|
g.inside_return = false
|
||||||
|
}
|
||||||
// got to do a correct check for multireturn
|
// got to do a correct check for multireturn
|
||||||
sym := g.table.get_type_symbol(g.fn_decl.return_type)
|
sym := g.table.get_type_symbol(g.fn_decl.return_type)
|
||||||
fn_return_is_multi := sym.kind == .multi_return
|
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
|
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 {
|
fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) ?bool {
|
||||||
sym := g.table.get_type_symbol(etype)
|
sym := g.table.get_type_symbol(etype)
|
||||||
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
|
sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
|
||||||
|
|
|
@ -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
|
return
|
||||||
}
|
}
|
||||||
g.writeln('// $' + 'method call. sym="$node.sym.name"')
|
g.writeln('// $' + 'method call. sym="$node.sym.name"')
|
||||||
|
|
172
vlib/v/gen/str.v
172
vlib/v/gen/str.v
|
@ -1,8 +1,10 @@
|
||||||
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
|
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
|
||||||
// Use of this source code is governed by an MIT license
|
// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
|
||||||
// that can be found in the LICENSE file.
|
|
||||||
module gen
|
module gen
|
||||||
|
|
||||||
|
import v.ast
|
||||||
|
import v.table
|
||||||
|
|
||||||
fn (mut g Gen) write_str_fn_definitions() {
|
fn (mut g Gen) write_str_fn_definitions() {
|
||||||
// _STR function can't be defined in vlib
|
// _STR function can't be defined in vlib
|
||||||
g.writeln("
|
g.writeln("
|
||||||
|
@ -85,6 +87,9 @@ string _STR(const char *fmt, int nfmts, ...) {
|
||||||
#ifdef DEBUG_ALLOC
|
#ifdef DEBUG_ALLOC
|
||||||
//puts('_STR:');
|
//puts('_STR:');
|
||||||
puts(buf);
|
puts(buf);
|
||||||
|
#endif
|
||||||
|
#if _VAUTOFREE
|
||||||
|
//g_cur_str = (byteptr)buf;
|
||||||
#endif
|
#endif
|
||||||
return tos2((byteptr)buf);
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue