builtin: fix leak in rune.str(), fix leaks in most assert x == y statements in tests (#11091)

pull/11176/head
Delyan Angelov 2021-08-13 18:37:34 +03:00 committed by GitHub
parent 0bd68bf5a2
commit 34d39ccb64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 152 additions and 22 deletions

View File

@ -55,6 +55,23 @@ pub:
rvalue string // the stringified *actual value* of the right side of a failed assertion
}
// free is used to free the memory occupied by the assertion meta data.
// It is called by cb_assertion_failed, and cb_assertion_ok in the preludes,
// once they are done with reporting/formatting the meta data.
[manualfree; unsafe]
pub fn (ami &VAssertMetaInfo) free() {
unsafe {
ami.fpath.free()
ami.fn_name.free()
ami.src.free()
ami.op.free()
ami.llabel.free()
ami.rlabel.free()
ami.lvalue.free()
ami.rvalue.free()
}
}
fn __print_assert_failure(i &VAssertMetaInfo) {
eprintln('$i.fpath:${i.line_nr + 1}: FAIL: fn $i.fn_name: assert $i.src')
if i.op.len > 0 && i.op != 'call' {

View File

@ -12,34 +12,38 @@ pub fn utf8_char_len(b byte) int {
pub fn utf32_to_str(code u32) string {
unsafe {
mut buffer := malloc_noscan(5)
return utf32_to_str_no_malloc(code, buffer)
res := utf32_to_str_no_malloc(code, buffer)
if res.len == 0 {
// the buffer was not used at all
free(buffer)
}
return res
}
}
[unsafe]
pub fn utf32_to_str_no_malloc(code u32, buf voidptr) string {
icode := int(code) // Prevents doing casts everywhere
mut res := ''
[manualfree; unsafe]
pub fn utf32_to_str_no_malloc(code u32, buf &byte) string {
unsafe {
icode := int(code) // Prevents doing casts everywhere
mut buffer := &byte(buf)
if icode <= 127 {
// 0x7F
buffer[0] = byte(icode)
buffer[1] = 0
res = tos(buffer, 1)
return tos(buffer, 1)
} else if icode <= 2047 {
// 0x7FF
buffer[0] = 192 | byte(icode >> 6) // 0xC0 - 110xxxxx
buffer[1] = 128 | byte(icode & 63) // 0x80 - 0x3F - 10xxxxxx
buffer[2] = 0
res = tos(buffer, 2)
return tos(buffer, 2)
} else if icode <= 65535 {
// 0xFFFF
buffer[0] = 224 | byte(icode >> 12) // 0xE0 - 1110xxxx
buffer[1] = 128 | (byte(icode >> 6) & 63) // 0x80 - 0x3F - 10xxxxxx
buffer[2] = 128 | byte(icode & 63) // 0x80 - 0x3F - 10xxxxxx
buffer[3] = 0
res = tos(buffer, 3)
return tos(buffer, 3)
}
// 0x10FFFF
else if icode <= 1114111 {
@ -48,11 +52,10 @@ pub fn utf32_to_str_no_malloc(code u32, buf voidptr) string {
buffer[2] = 128 | (byte(icode >> 6) & 63) // 0x80 - 0x3F - 10xxxxxx
buffer[3] = 128 | byte(icode & 63) // 0x80 - 0x3F - 10xxxxxx
buffer[4] = 0
res = tos(buffer, 4)
return tos(buffer, 4)
}
}
res.is_lit = 1 // let autofree know this string doesn't have to be freed
return res
return ''
}
// Convert utf8 to utf32

View File

@ -60,7 +60,7 @@ pub fn (mut cb Clipboard) get_text() string {
return ''
}
utf8_clip := C.darwin_get_pasteboard_text(cb.pb)
return unsafe { utf8_clip.vstring() }
return unsafe { tos_clone(&byte(utf8_clip)) }
}
// new_primary returns a new X11 `PRIMARY` type `Clipboard` instance allocated on the heap.

View File

@ -6,6 +6,7 @@ fn test_long_encoding() {
s_original := []byte{len: input_size, init: `a`}
s_encoded := base64.encode(s_original)
s_encoded_bytes := s_encoded.bytes()
s_decoded := base64.decode(s_encoded)
assert s_encoded.len > s_original.len
@ -13,6 +14,10 @@ fn test_long_encoding() {
ebuffer := unsafe { malloc(s_encoded.len) }
dbuffer := unsafe { malloc(s_decoded.len) }
defer {
unsafe { free(ebuffer) }
unsafe { free(dbuffer) }
}
//
encoded_size := base64.encode_in_buffer(s_original, ebuffer)
mut encoded_in_buf := []byte{len: encoded_size}
@ -27,7 +32,8 @@ fn test_long_encoding() {
assert encoded_in_buf[encoded_size - 3] == `W`
assert encoded_in_buf[encoded_size - 2] == `F`
assert encoded_in_buf[encoded_size - 1] == `h`
assert encoded_in_buf == s_encoded.bytes()
assert encoded_in_buf == s_encoded_bytes
decoded_size := base64.decode_in_buffer(s_encoded, dbuffer)
assert decoded_size == input_size

View File

@ -181,11 +181,13 @@ fn supports_escape_sequences(fd int) bool {
if vcolors_override == 'never' {
return false
}
if os.getenv('TERM') == 'dumb' {
env_term := os.getenv('TERM')
if env_term == 'dumb' {
return false
}
$if windows {
if os.getenv('ConEmuANSI') == 'ON' {
env_conemu := os.getenv('ConEmuANSI')
if env_conemu == 'ON' {
return true
}
// 4 is enable_virtual_terminal_processing

View File

@ -290,16 +290,17 @@ pub fn (x Expr) str() string {
}
CallExpr {
sargs := args2str(x.args)
propagate_suffix := if x.or_block.kind == .propagate { ' ?' } else { '' }
if x.is_method {
return '${x.left.str()}.${x.name}($sargs)'
return '${x.left.str()}.${x.name}($sargs)$propagate_suffix'
}
if x.name.starts_with('${x.mod}.') {
return util.strip_main_name('${x.name}($sargs)')
return util.strip_main_name('${x.name}($sargs)$propagate_suffix')
}
if x.mod == '' && x.name == '' {
return x.left.str() + '($sargs)'
return x.left.str() + '($sargs)$propagate_suffix'
}
return '${x.mod}.${x.name}($sargs)'
return '${x.mod}.${x.name}($sargs)$propagate_suffix'
}
CharLiteral {
return '`$x.val`'

View File

@ -101,6 +101,7 @@ fn (mut g Gen) gen_assert_metainfo(node ast.AssertStmt) string {
}
fn (mut g Gen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) {
// eprintln('> gen_assert_single_expr typ: $typ | expr: $expr | typeof(expr): ${typeof(expr)}')
unknown_value := '*unknown value*'
match expr {
ast.CastExpr, ast.IndexExpr, ast.MatchExpr {
@ -121,7 +122,29 @@ fn (mut g Gen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) {
g.write(ctoslit('$sym.name'))
}
else {
mut should_clone := true
if typ == ast.string_type && expr is ast.StringLiteral {
should_clone = false
}
if expr is ast.CTempVar {
if expr.orig is ast.CallExpr {
should_clone = false
if expr.orig.or_block.kind == .propagate {
should_clone = true
}
if expr.orig.is_method && expr.orig.args.len == 0
&& expr.orig.name == 'type_name' {
should_clone = true
}
}
}
if should_clone {
g.write('string_clone(')
}
g.gen_expr_to_string(expr, typ)
if should_clone {
g.write(')')
}
}
}
g.write(' /* typeof: ' + expr.type_name() + ' type: ' + typ.str() + ' */ ')

View File

@ -3625,7 +3625,7 @@ fn (mut g Gen) typeof_expr(node ast.TypeOf) {
if sym.kind == .sum_type {
// When encountering a .sum_type, typeof() should be done at runtime,
// because the subtype of the expression may change:
g.write('tos3( /* $sym.name */ v_typeof_sumtype_${sym.cname}( (')
g.write('charptr_vstring_literal( /* $sym.name */ v_typeof_sumtype_${sym.cname}( (')
g.expr(node.expr)
g.write(')._typ ))')
} else if sym.kind == .array_fixed {

View File

@ -745,14 +745,14 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
}
if left_sym.kind == .sum_type && node.name == 'type_name' {
g.write('tos3( /* $left_sym.name */ v_typeof_sumtype_${typ_sym.cname}( (')
g.write('charptr_vstring_literal( /* $left_sym.name */ v_typeof_sumtype_${typ_sym.cname}( (')
g.expr(node.left)
dot := if node.left_type.is_ptr() { '->' } else { '.' }
g.write(')${dot}_typ ))')
return
}
if left_sym.kind == .interface_ && node.name == 'type_name' {
g.write('tos3( /* $left_sym.name */ v_typeof_interface_${typ_sym.cname}( (')
g.write('charptr_vstring_literal( /* $left_sym.name */ v_typeof_interface_${typ_sym.cname}( (')
g.expr(node.left)
dot := if node.left_type.is_ptr() { '->' } else { '.' }
g.write(')${dot}_typ ))')

View File

@ -49,8 +49,10 @@ pub fn mark_used(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.F
// byteptr and charptr
'3.vstring',
'3.vstring_with_len',
'3.vstring_literal',
'4.vstring',
'4.vstring_with_len',
'4.vstring_literal',
// byte. methods
'9.str_escaped',
// string. methods

View File

@ -62,6 +62,7 @@ fn cb_assertion_failed(i &VAssertMetaInfo) {
eprintln(' $final_src')
}
eprintln('')
unsafe { i.free() }
}
fn cb_assertion_ok(i &VAssertMetaInfo) {
@ -85,6 +86,7 @@ fn cb_assertion_ok(i &VAssertMetaInfo) {
}
println('$final_funcname ($final_filepath)')
*/
unsafe { i.free() }
}
fn cb_propagate_test_error(line_nr int, file string, mod string, fn_name string, errmsg string) {

View File

@ -0,0 +1,59 @@
import encoding.base64
fn main() {
repeats := 1000
input_size := 3000
s_original := []byte{len: input_size, init: `a`}
s_encoded := base64.encode(s_original)
s_encoded_bytes := s_encoded.bytes()
s_decoded := base64.decode(s_encoded)
assert s_encoded.len > s_original.len
assert s_original == s_decoded
ebuffer := unsafe { malloc(s_encoded.len) }
dbuffer := unsafe { malloc(s_decoded.len) }
defer {
unsafe { free(ebuffer) }
unsafe { free(dbuffer) }
}
//
encoded_size := base64.encode_in_buffer(s_original, ebuffer)
mut encoded_in_buf := []byte{len: encoded_size}
unsafe { C.memcpy(encoded_in_buf.data, ebuffer, encoded_size) }
assert input_size * 4 / 3 == encoded_size
assert encoded_in_buf[0] == `Y`
assert encoded_in_buf[1] == `W`
assert encoded_in_buf[2] == `F`
assert encoded_in_buf[3] == `h`
assert encoded_in_buf[encoded_size - 4] == `Y`
assert encoded_in_buf[encoded_size - 3] == `W`
assert encoded_in_buf[encoded_size - 2] == `F`
assert encoded_in_buf[encoded_size - 1] == `h`
assert encoded_in_buf == s_encoded_bytes
decoded_size := base64.decode_in_buffer(s_encoded, dbuffer)
assert decoded_size == input_size
mut decoded_in_buf := []byte{len: decoded_size}
unsafe { C.memcpy(decoded_in_buf.data, dbuffer, decoded_size) }
assert decoded_in_buf == s_original
mut s := 0
for _ in 0 .. repeats {
resultsize := base64.encode_in_buffer(s_original, ebuffer)
s += resultsize
assert resultsize == s_encoded.len
}
for _ in 0 .. repeats {
resultsize := base64.decode_in_buffer(s_encoded, dbuffer)
s += resultsize
assert resultsize == s_decoded.len
}
println('Final s: $s')
// assert s == 39147008
}

View File

@ -0,0 +1,7 @@
fn main() {
unsafe {
mut buffer := malloc_noscan(5)
s := utf32_to_str_no_malloc(77, buffer)
println(s)
}
}

View File

@ -0,0 +1,4 @@
fn main() {
a := `Y`.str()
println(a)
}

View File

@ -0,0 +1,4 @@
fn main() {
a := 'Y'.str()
println(a)
}