builtin: fix leak in rune.str(), fix leaks in most assert x == y statements in tests (#11091)
							parent
							
								
									0bd68bf5a2
								
							
						
					
					
						commit
						34d39ccb64
					
				|  | @ -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' { | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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`' | ||||
|  |  | |||
|  | @ -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() + ' */ ') | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 ))') | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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
 | ||||
| } | ||||
|  | @ -0,0 +1,7 @@ | |||
| fn main() { | ||||
| 	unsafe { | ||||
| 		mut buffer := malloc_noscan(5) | ||||
| 		s := utf32_to_str_no_malloc(77, buffer) | ||||
| 		println(s) | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,4 @@ | |||
| fn main() { | ||||
| 	a := `Y`.str() | ||||
| 	println(a) | ||||
| } | ||||
|  | @ -0,0 +1,4 @@ | |||
| fn main() { | ||||
| 	a := 'Y'.str() | ||||
| 	println(a) | ||||
| } | ||||
		Loading…
	
		Reference in New Issue