autofree: lots of fixes
parent
b015033c53
commit
3410705974
|
@ -216,16 +216,16 @@ jobs:
|
||||||
# github-token: ${{ secrets.GITHUB_TOKEN }}
|
# github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
ubuntu-autofree-selfcompile:
|
# ubuntu-autofree-selfcompile:
|
||||||
runs-on: ubuntu-18.04
|
# runs-on: ubuntu-18.04
|
||||||
env:
|
# env:
|
||||||
VFLAGS: -cc gcc
|
# VFLAGS: -cc gcc
|
||||||
steps:
|
# steps:
|
||||||
- uses: actions/checkout@v2
|
# - uses: actions/checkout@v2
|
||||||
- name: Build V
|
# - name: Build V
|
||||||
run: make -j4
|
# run: make -j4
|
||||||
- name: V self compilation with -autofree
|
# - name: V self compilation with -autofree
|
||||||
run: ./v -o v2 -autofree cmd/v && ./v2 -o v3 -autofree cmd/v && ./v3 -o v4 -autofree cmd/v
|
# run: ./v -o v2 -autofree cmd/v && ./v2 -o v3 -autofree cmd/v && ./v3 -o v4 -autofree cmd/v
|
||||||
|
|
||||||
|
|
||||||
# Ubuntu docker pre-built container
|
# Ubuntu docker pre-built container
|
||||||
|
|
|
@ -153,52 +153,52 @@ fn unescape(s_ string, mode EncodingMode) ?string {
|
||||||
for i := 0; i < s.len; {
|
for i := 0; i < s.len; {
|
||||||
x := s[i]
|
x := s[i]
|
||||||
match x {
|
match x {
|
||||||
`%` {
|
`%` {
|
||||||
if s == '' {
|
if s == '' {
|
||||||
break
|
break
|
||||||
}
|
|
||||||
n++
|
|
||||||
if i + 2 >= s.len || !ishex(s[i + 1]) || !ishex(s[i + 2]) {
|
|
||||||
s = s[i..]
|
|
||||||
if s.len > 3 {
|
|
||||||
s = s[..3]
|
|
||||||
}
|
|
||||||
return error(error_msg(err_msg_escape, s))
|
|
||||||
}
|
|
||||||
// Per https://tools.ietf.org/html/rfc3986#page-21
|
|
||||||
// in the host component %-encoding can only be used
|
|
||||||
// for non-ASCII bytes.
|
|
||||||
// But https://tools.ietf.org/html/rfc6874#section-2
|
|
||||||
// introduces %25 being allowed to escape a percent sign
|
|
||||||
// in IPv6 scoped-address literals. Yay.
|
|
||||||
if mode == .encode_host && unhex(s[i + 1]) < 8 && s[i..i + 3] != '%25' {
|
|
||||||
return error(error_msg(err_msg_escape, s[i..i + 3]))
|
|
||||||
}
|
|
||||||
if mode == .encode_zone {
|
|
||||||
// RFC 6874 says basically 'anything goes' for zone identifiers
|
|
||||||
// and that even non-ASCII can be redundantly escaped,
|
|
||||||
// but it seems prudent to restrict %-escaped bytes here to those
|
|
||||||
// that are valid host name bytes in their unescaped form.
|
|
||||||
// That is, you can use escaping in the zone identifier but not
|
|
||||||
// to introduce bytes you couldn't just write directly.
|
|
||||||
// But Windows puts spaces here! Yay.
|
|
||||||
v := ( (unhex(s[i + 1])<<byte(4)) | unhex(s[i + 2]))
|
|
||||||
if s[i..i + 3] != '%25' && v != ` ` && should_escape(v, .encode_host) {
|
|
||||||
error(error_msg(err_msg_escape, s[i..i + 3]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i += 3
|
|
||||||
}
|
}
|
||||||
`+` {
|
n++
|
||||||
has_plus = mode == .encode_query_component
|
if i + 2 >= s.len || !ishex(s[i + 1]) || !ishex(s[i + 2]) {
|
||||||
i++
|
s = s[i..]
|
||||||
}
|
if s.len > 3 {
|
||||||
else {
|
s = s[..3]
|
||||||
if (mode == .encode_host || mode == .encode_zone) && s[i] < 0x80 && should_escape(s[i], mode) {
|
|
||||||
error(error_msg('unescape: invalid character in host name', s[i..i + 1]))
|
|
||||||
}
|
}
|
||||||
i++
|
return error(error_msg(err_msg_escape, s))
|
||||||
}}
|
}
|
||||||
|
// Per https://tools.ietf.org/html/rfc3986#page-21
|
||||||
|
// in the host component %-encoding can only be used
|
||||||
|
// for non-ASCII bytes.
|
||||||
|
// But https://tools.ietf.org/html/rfc6874#section-2
|
||||||
|
// introduces %25 being allowed to escape a percent sign
|
||||||
|
// in IPv6 scoped-address literals. Yay.
|
||||||
|
if mode == .encode_host && unhex(s[i + 1]) < 8 && s[i..i + 3] != '%25' {
|
||||||
|
return error(error_msg(err_msg_escape, s[i..i + 3]))
|
||||||
|
}
|
||||||
|
if mode == .encode_zone {
|
||||||
|
// RFC 6874 says basically 'anything goes' for zone identifiers
|
||||||
|
// and that even non-ASCII can be redundantly escaped,
|
||||||
|
// but it seems prudent to restrict %-escaped bytes here to those
|
||||||
|
// that are valid host name bytes in their unescaped form.
|
||||||
|
// That is, you can use escaping in the zone identifier but not
|
||||||
|
// to introduce bytes you couldn't just write directly.
|
||||||
|
// But Windows puts spaces here! Yay.
|
||||||
|
v := ( (unhex(s[i + 1])<<byte(4)) | unhex(s[i + 2]))
|
||||||
|
if s[i..i + 3] != '%25' && v != ` ` && should_escape(v, .encode_host) {
|
||||||
|
error(error_msg(err_msg_escape, s[i..i + 3]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 3
|
||||||
|
}
|
||||||
|
`+` {
|
||||||
|
has_plus = mode == .encode_query_component
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (mode == .encode_host || mode == .encode_zone) && s[i] < 0x80 && should_escape(s[i], mode) {
|
||||||
|
error(error_msg('unescape: invalid character in host name', s[i..i + 1]))
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
if n == 0 && !has_plus {
|
if n == 0 && !has_plus {
|
||||||
return s
|
return s
|
||||||
|
|
|
@ -10,11 +10,11 @@ import v.errors
|
||||||
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
|
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
|
||||||
|
|
||||||
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr |
|
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr |
|
||||||
CharLiteral | ChanInit | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr |
|
ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral |
|
||||||
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr |
|
Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr |
|
||||||
None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf |
|
MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr |
|
||||||
SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type | TypeOf | UnsafeExpr
|
SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type |
|
||||||
|
TypeOf | UnsafeExpr
|
||||||
|
|
||||||
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | CompIf | ConstDecl |
|
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | CompIf | ConstDecl |
|
||||||
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl |
|
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl |
|
||||||
|
@ -158,10 +158,10 @@ pub mut:
|
||||||
|
|
||||||
pub struct ConstDecl {
|
pub struct ConstDecl {
|
||||||
pub:
|
pub:
|
||||||
is_pub bool
|
is_pub bool
|
||||||
pos token.Position
|
pos token.Position
|
||||||
pub mut:
|
pub mut:
|
||||||
fields []ConstField
|
fields []ConstField
|
||||||
end_comments []Comment
|
end_comments []Comment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,16 +289,26 @@ pub mut:
|
||||||
return_type table.Type
|
return_type table.Type
|
||||||
should_be_skipped bool
|
should_be_skipped bool
|
||||||
generic_type table.Type // TODO array, to support multiple types
|
generic_type table.Type // TODO array, to support multiple types
|
||||||
|
// autofree_vars []AutofreeArgVar
|
||||||
|
// autofree_vars_ids []int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub struct AutofreeArgVar {
|
||||||
|
name string
|
||||||
|
idx int
|
||||||
|
}
|
||||||
|
*/
|
||||||
pub struct CallArg {
|
pub struct CallArg {
|
||||||
pub:
|
pub:
|
||||||
is_mut bool
|
is_mut bool
|
||||||
share table.ShareType
|
share table.ShareType
|
||||||
expr Expr
|
expr Expr
|
||||||
comments []Comment
|
comments []Comment
|
||||||
pub mut:
|
pub mut:
|
||||||
typ table.Type
|
typ table.Type
|
||||||
|
is_tmp_autofree bool // for autofree
|
||||||
|
// tmp_name string // for autofree
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Return {
|
pub struct Return {
|
||||||
|
@ -761,12 +771,12 @@ pub mut:
|
||||||
|
|
||||||
pub struct ChanInit {
|
pub struct ChanInit {
|
||||||
pub:
|
pub:
|
||||||
pos token.Position
|
pos token.Position
|
||||||
cap_expr Expr
|
cap_expr Expr
|
||||||
has_cap bool
|
has_cap bool
|
||||||
pub mut:
|
pub mut:
|
||||||
typ table.Type
|
typ table.Type
|
||||||
elem_type table.Type
|
elem_type table.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MapInit {
|
pub struct MapInit {
|
||||||
|
@ -1069,9 +1079,9 @@ pub fn (expr Expr) position() token.Position {
|
||||||
|
|
||||||
pub fn (expr Expr) is_lvalue() bool {
|
pub fn (expr Expr) is_lvalue() bool {
|
||||||
match expr {
|
match expr {
|
||||||
Ident {return true}
|
Ident { return true }
|
||||||
IndexExpr {return expr.left.is_lvalue()}
|
IndexExpr { return expr.left.is_lvalue() }
|
||||||
SelectorExpr {return expr.expr.is_lvalue()}
|
SelectorExpr { return expr.expr.is_lvalue() }
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -846,10 +846,22 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) {
|
||||||
|
|
||||||
pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
|
pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
|
||||||
c.stmts(call_expr.or_block.stmts)
|
c.stmts(call_expr.or_block.stmts)
|
||||||
if call_expr.is_method {
|
typ := if call_expr.is_method { c.call_method(call_expr) } else { c.call_fn(call_expr) }
|
||||||
return c.call_method(call_expr)
|
// autofree
|
||||||
|
free_tmp_arg_vars := c.pref.autofree && c.pref.experimental && !c.is_builtin_mod &&
|
||||||
|
call_expr.args.len > 0 && !call_expr.args[0].typ.has_flag(.optional)
|
||||||
|
if free_tmp_arg_vars {
|
||||||
|
for i, arg in call_expr.args {
|
||||||
|
if arg.typ != table.string_type {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if arg.expr is ast.Ident || arg.expr is ast.StringLiteral {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
call_expr.args[i].is_tmp_autofree = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return c.call_fn(call_expr)
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_expr ast.CallExpr) {
|
fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_expr ast.CallExpr) {
|
||||||
|
@ -2228,7 +2240,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
ast.Module {
|
ast.Module {
|
||||||
c.mod = node.name
|
c.mod = node.name
|
||||||
c.is_builtin_mod = node.name == 'builtin'
|
c.is_builtin_mod = node.name in ['builtin', 'os', 'strconv']
|
||||||
c.check_valid_snake_case(node.name, 'module name', node.pos)
|
c.check_valid_snake_case(node.name, 'module name', node.pos)
|
||||||
}
|
}
|
||||||
ast.Return {
|
ast.Return {
|
||||||
|
|
|
@ -53,7 +53,8 @@ mut:
|
||||||
file ast.File
|
file ast.File
|
||||||
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
|
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
|
||||||
last_fn_c_name string
|
last_fn_c_name string
|
||||||
tmp_count int
|
tmp_count int // counter for unique tmp vars (_tmp1, tmp2 etc)
|
||||||
|
tmp_count2 int // a separate tmp var counter for autofree fn calls
|
||||||
variadic_args map[string]int
|
variadic_args map[string]int
|
||||||
is_c_call bool // e.g. `C.printf("v")`
|
is_c_call bool // e.g. `C.printf("v")`
|
||||||
is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
|
is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
|
||||||
|
@ -106,9 +107,10 @@ mut:
|
||||||
comptime_var_type_map map[string]table.Type
|
comptime_var_type_map map[string]table.Type
|
||||||
match_sumtype_exprs []ast.Expr
|
match_sumtype_exprs []ast.Expr
|
||||||
match_sumtype_syms []table.TypeSymbol
|
match_sumtype_syms []table.TypeSymbol
|
||||||
tmp_arg_vars_to_free []string
|
// tmp_arg_vars_to_free []string
|
||||||
called_fn_name string
|
called_fn_name string
|
||||||
cur_mod string
|
cur_mod string
|
||||||
|
is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -663,6 +665,12 @@ pub fn (mut g Gen) new_tmp_var() string {
|
||||||
return '_t$g.tmp_count'
|
return '_t$g.tmp_count'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub fn (mut g Gen) new_tmp_var2() string {
|
||||||
|
g.tmp_count2++
|
||||||
|
return '_tt$g.tmp_count2'
|
||||||
|
}
|
||||||
|
*/
|
||||||
pub fn (mut g Gen) reset_tmp_count() {
|
pub fn (mut g Gen) reset_tmp_count() {
|
||||||
g.tmp_count = 0
|
g.tmp_count = 0
|
||||||
}
|
}
|
||||||
|
@ -717,8 +725,9 @@ fn (mut g Gen) write_v_source_line_info(pos token.Position) {
|
||||||
|
|
||||||
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 {
|
defer {
|
||||||
// If 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.pref.autofree {
|
||||||
if g.strs_to_free != '' {
|
if g.strs_to_free != '' {
|
||||||
|
@ -727,6 +736,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
// println('cgen.stmt()')
|
// println('cgen.stmt()')
|
||||||
// g.writeln('//// stmt start')
|
// g.writeln('//// stmt start')
|
||||||
match node {
|
match node {
|
||||||
|
@ -932,7 +942,8 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||||
// definitions are sorted and added in write_types
|
// definitions are sorted and added in write_types
|
||||||
}
|
}
|
||||||
ast.Module {
|
ast.Module {
|
||||||
g.is_builtin_mod = node.name == 'builtin'
|
// g.is_builtin_mod = node.name == 'builtin'
|
||||||
|
g.is_builtin_mod = node.name in ['builtin', 'os', 'strconv']
|
||||||
g.cur_mod = node.name
|
g.cur_mod = node.name
|
||||||
}
|
}
|
||||||
ast.Return {
|
ast.Return {
|
||||||
|
|
|
@ -340,7 +340,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||||
g.write('${dot}_object')
|
g.write('${dot}_object')
|
||||||
if node.args.len > 0 {
|
if node.args.len > 0 {
|
||||||
g.write(', ')
|
g.write(', ')
|
||||||
g.call_args(node.args, node.expected_arg_types)
|
// g.call_args(node.args, node.expected_arg_types) // , [])
|
||||||
|
g.call_args(node)
|
||||||
}
|
}
|
||||||
g.write(')')
|
g.write(')')
|
||||||
return
|
return
|
||||||
|
@ -449,7 +450,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// ///////
|
// ///////
|
||||||
g.call_args(node.args, node.expected_arg_types)
|
// g.call_args(node.args, node.expected_arg_types) // , [])
|
||||||
|
g.call_args(node)
|
||||||
g.write(')')
|
g.write(')')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,7 +487,8 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||||
encode_name := c_name(name) + '_' + util.no_dots(json_type_str)
|
encode_name := c_name(name) + '_' + util.no_dots(json_type_str)
|
||||||
g.writeln('// json.encode')
|
g.writeln('// json.encode')
|
||||||
g.write('cJSON* $json_obj = ${encode_name}(')
|
g.write('cJSON* $json_obj = ${encode_name}(')
|
||||||
g.call_args(node.args, node.expected_arg_types)
|
// g.call_args(node.args, node.expected_arg_types) // , [])
|
||||||
|
g.call_args(node)
|
||||||
g.writeln(');')
|
g.writeln(');')
|
||||||
tmp2 = g.new_tmp_var()
|
tmp2 = g.new_tmp_var()
|
||||||
g.writeln('string $tmp2 = json__json_print($json_obj);')
|
g.writeln('string $tmp2 = json__json_print($json_obj);')
|
||||||
|
@ -499,7 +502,10 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||||
g.write('cJSON* $json_obj = json__json_parse(')
|
g.write('cJSON* $json_obj = json__json_parse(')
|
||||||
// Skip the first argument in json.decode which is a type
|
// Skip the first argument in json.decode which is a type
|
||||||
// its name was already used to generate the function call
|
// its name was already used to generate the function call
|
||||||
g.call_args(node.args[1..], node.expected_arg_types)
|
// g.call_args(node.args[1..], node.expected_arg_types) // , [])
|
||||||
|
g.is_js_call = true
|
||||||
|
g.call_args(node)
|
||||||
|
g.is_js_call = false
|
||||||
g.writeln(');')
|
g.writeln(');')
|
||||||
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);')
|
||||||
|
@ -522,27 +528,26 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||||
}
|
}
|
||||||
// Create a temporary var for each argument in order to free it (only if it's a complex expression,
|
// Create a temporary var 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)`
|
||||||
free_tmp_arg_vars := g.autofree && g.pref.experimental && !g.is_builtin_mod && g.cur_mod !=
|
mut free_tmp_arg_vars := g.autofree && g.pref.experimental && !g.is_builtin_mod &&
|
||||||
'os' && node.args.len > 0 && !node.args[0].typ.has_flag(.optional)
|
node.args.len > 0 && !node.args[0].typ.has_flag(.optional) // TODO copy pasta checker.v
|
||||||
|
mut cur_line := ''
|
||||||
if free_tmp_arg_vars {
|
if free_tmp_arg_vars {
|
||||||
g.tmp_arg_vars_to_free = []string{cap: 10} // TODO perf
|
free_tmp_arg_vars = false // set the flag to true only if we have at least one arg to free
|
||||||
|
g.tmp_count2++
|
||||||
for i, arg in node.args {
|
for i, arg in node.args {
|
||||||
if arg.typ != table.string_type {
|
if !arg.is_tmp_autofree {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if arg.expr is ast.Ident || arg.expr is ast.StringLiteral {
|
free_tmp_arg_vars = true
|
||||||
continue
|
// t := g.new_tmp_var() + '_arg_expr_${name}_$i'
|
||||||
}
|
fn_name := node.name.replace('.', '_')
|
||||||
t := g.new_tmp_var() + '_arg_expr_${name}_$i' // g.new_tmp_var()
|
t := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
|
||||||
g.called_fn_name = name
|
g.called_fn_name = name
|
||||||
/*
|
|
||||||
g.write('string $t = ')
|
|
||||||
g.expr(arg.expr)
|
|
||||||
g.writeln('; // to free $i ')
|
|
||||||
*/
|
|
||||||
str_expr := g.write_expr_to_string(arg.expr)
|
str_expr := g.write_expr_to_string(arg.expr)
|
||||||
g.insert_before_stmt('string $t = $str_expr; // new. to free $i ')
|
// g.insert_before_stmt('string $t = $str_expr; // new. to free $i ')
|
||||||
g.tmp_arg_vars_to_free << t // i
|
cur_line = g.go_before_stmt(0)
|
||||||
|
// println('cur line ="$cur_line"')
|
||||||
|
g.writeln('string $t = $str_expr; // new. to free $i ')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Handle `print(x)`
|
// Handle `print(x)`
|
||||||
|
@ -602,31 +607,48 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||||
} else if g.pref.is_debug && node.name == 'panic' {
|
} else if g.pref.is_debug && node.name == 'panic' {
|
||||||
paline, pafile, pamod, pafn := g.panic_debug_info(node.pos)
|
paline, pafile, pamod, pafn := g.panic_debug_info(node.pos)
|
||||||
g.write('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ')
|
g.write('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ')
|
||||||
g.call_args(node.args, node.expected_arg_types)
|
// g.call_args(node.args, node.expected_arg_types) // , [])
|
||||||
|
g.call_args(node)
|
||||||
g.write(')')
|
g.write(')')
|
||||||
} else {
|
} else {
|
||||||
|
// Simple function call
|
||||||
|
if free_tmp_arg_vars {
|
||||||
|
// g.writeln(';')
|
||||||
|
g.write(cur_line + ' /* cur line*/')
|
||||||
|
}
|
||||||
g.write('${g.get_ternary_name(name)}(')
|
g.write('${g.get_ternary_name(name)}(')
|
||||||
if g.is_json_fn {
|
if g.is_json_fn {
|
||||||
g.write(json_obj)
|
g.write(json_obj)
|
||||||
} else {
|
} else {
|
||||||
g.call_args(node.args, node.expected_arg_types)
|
// g.call_args(node.args, node.expected_arg_types) // , tmp_arg_vars_to_free)
|
||||||
|
g.call_args(node)
|
||||||
}
|
}
|
||||||
g.write(')')
|
g.write(')')
|
||||||
}
|
}
|
||||||
g.is_c_call = false
|
g.is_c_call = false
|
||||||
g.is_json_fn = false
|
g.is_json_fn = false
|
||||||
if free_tmp_arg_vars && g.tmp_arg_vars_to_free.len > 0 {
|
if free_tmp_arg_vars { // && tmp_arg_vars_to_free.len > 0 {
|
||||||
|
// g.writeln(';')
|
||||||
|
// g.write(cur_line + ' /* cur line*/')
|
||||||
|
// g.write(tmp)
|
||||||
// Now free the tmp arg vars right after the function call
|
// Now free the tmp arg vars right after the function call
|
||||||
g.writeln(';')
|
g.writeln(';')
|
||||||
for tmp in g.tmp_arg_vars_to_free {
|
for i, arg in node.args {
|
||||||
g.writeln('string_free(&$tmp);')
|
if arg.is_tmp_autofree {
|
||||||
|
fn_name := node.name.replace('.', '_')
|
||||||
|
tmp := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
|
||||||
|
g.writeln('string_free(&$tmp);')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g.writeln('')
|
g.writeln('')
|
||||||
g.tmp_arg_vars_to_free.clear()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
|
// fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type, tmp_arg_vars_to_free []string) {
|
||||||
|
// fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
|
||||||
|
fn (mut g Gen) call_args(node ast.CallExpr) {
|
||||||
|
args := if g.is_js_call { node.args[1..] } else { node.args }
|
||||||
|
expected_types := node.expected_arg_types
|
||||||
is_variadic := expected_types.len > 0 && expected_types[expected_types.len - 1].has_flag(.variadic)
|
is_variadic := expected_types.len > 0 && expected_types[expected_types.len - 1].has_flag(.variadic)
|
||||||
is_forwarding_varg := args.len > 0 && args[args.len - 1].typ.has_flag(.variadic)
|
is_forwarding_varg := args.len > 0 && args[args.len - 1].typ.has_flag(.variadic)
|
||||||
gen_vargs := is_variadic && !is_forwarding_varg
|
gen_vargs := is_variadic && !is_forwarding_varg
|
||||||
|
@ -635,7 +657,8 @@ fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
use_tmp_var_autofree := g.autofree && g.pref.experimental && arg.typ == table.string_type &&
|
use_tmp_var_autofree := g.autofree && g.pref.experimental && arg.typ == table.string_type &&
|
||||||
g.tmp_arg_vars_to_free.len > 0
|
arg.is_tmp_autofree
|
||||||
|
// g.write('/* af=$arg.is_tmp_autofree */')
|
||||||
mut is_interface := false
|
mut is_interface := false
|
||||||
// some c fn definitions dont have args (cfns.v) or are not updated in checker
|
// some c fn definitions dont have args (cfns.v) or are not updated in checker
|
||||||
// when these are fixed we wont need this check
|
// when these are fixed we wont need this check
|
||||||
|
@ -654,14 +677,14 @@ fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
|
||||||
if is_interface {
|
if is_interface {
|
||||||
g.expr(arg.expr)
|
g.expr(arg.expr)
|
||||||
} else if use_tmp_var_autofree {
|
} else if use_tmp_var_autofree {
|
||||||
for name in g.tmp_arg_vars_to_free {
|
if arg.is_tmp_autofree {
|
||||||
// We saved expressions in temp variables so that they can be freed later.
|
// We saved expressions in temp variables so that they can be freed later.
|
||||||
// `foo(str + str2) => x := str + str2; foo(x); x.free()`
|
// `foo(str + str2) => x := str + str2; foo(x); x.free()`
|
||||||
// g.write('_arg_expr_${g.called_fn_name}_$i')
|
// g.write('_arg_expr_${g.called_fn_name}_$i')
|
||||||
// Use these variables here.
|
// Use these variables here.
|
||||||
if name.contains('_$i') {
|
fn_name := node.name.replace('.', '_')
|
||||||
g.write(name)
|
name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
|
||||||
}
|
g.write('/*af arg*/' + name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
g.ref_or_deref_arg(arg, expected_types[i])
|
g.ref_or_deref_arg(arg, expected_types[i])
|
||||||
|
@ -669,11 +692,9 @@ fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
|
||||||
} else {
|
} else {
|
||||||
if use_tmp_var_autofree {
|
if use_tmp_var_autofree {
|
||||||
// TODO copypasta, move to an inline fn
|
// TODO copypasta, move to an inline fn
|
||||||
for name in g.tmp_arg_vars_to_free {
|
fn_name := node.name.replace('.', '_')
|
||||||
if name.contains('_$i') {
|
name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
|
||||||
g.write(name)
|
g.write('/*af arg2*/' + name)
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
g.expr(arg.expr)
|
g.expr(arg.expr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.check(.lpar)
|
p.check(.lpar)
|
||||||
args := p.call_args()
|
mut args := p.call_args()
|
||||||
last_pos := p.tok.position()
|
last_pos := p.tok.position()
|
||||||
p.check(.rpar)
|
p.check(.rpar)
|
||||||
// ! in mutable methods
|
// ! in mutable methods
|
||||||
|
|
|
@ -15,12 +15,17 @@ fn foo() {
|
||||||
// nums.free() // this should result in a double free and a CI error
|
// nums.free() // this should result in a double free and a CI error
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_strings(s, p string) {
|
fn handle_strings(s, p string) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_int(n int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn str_tmp_expr() {
|
fn str_tmp_expr() {
|
||||||
println('a' + 'b') // tmp expression result must be freed
|
println('a' + 'b') // tmp expression result must be freed
|
||||||
handle_strings('c' + 'd', 'e' + 'f')
|
handle_strings('c' + 'd', 'e' + 'f') // multiple tmp expressions must be freed
|
||||||
|
// handle_int(handle_strings('x' + 'y', 'f')) // exprs 2 levels deep must bee freed
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
|
|
Loading…
Reference in New Issue