markused,builtin,strconv,vlib: reduce generated C sizes for compilers != tcc, for short programs, by simplifying the generation of backtraces, and reducing string interpolations in panics (#23380)

This commit is contained in:
Delyan Angelov 2025-01-06 08:23:56 +02:00 committed by GitHub
parent 738f847f89
commit e983d75b64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 278 additions and 191 deletions

View File

@ -194,7 +194,8 @@ fn (mut a array) ensure_cap(required int) {
return
}
if a.flags.has(.nogrow) {
panic('array.ensure_cap: array with the flag `.nogrow` cannot grow in size, array required new size: ${required}')
panic_n('array.ensure_cap: array with the flag `.nogrow` cannot grow in size, array required new size:',
required)
}
mut cap := if a.cap > 0 { i64(a.cap) } else { i64(2) }
for required > cap {
@ -205,7 +206,8 @@ fn (mut a array) ensure_cap(required int) {
// limit the capacity, since bigger values, will overflow the 32bit integer used to store it
cap = max_int
} else {
panic('array.ensure_cap: array needs to grow to cap = ${cap}, which is > 2^31')
panic_n('array.ensure_cap: array needs to grow to cap (which is > 2^31):',
cap)
}
}
new_size := u64(cap) * u64(a.element_size)
@ -240,7 +242,7 @@ pub fn (a array) repeat(count int) array {
@[direct_array_access; unsafe]
pub fn (a array) repeat_to_depth(count int, depth int) array {
if count < 0 {
panic('array.repeat: count is negative: ${count}')
panic_n('array.repeat: count is negative:', count)
}
mut size := u64(count) * u64(a.len) * u64(a.element_size)
if size == 0 {
@ -293,7 +295,7 @@ pub fn (a array) repeat_to_depth(count int, depth int) array {
// ```
pub fn (mut a array) insert(i int, val voidptr) {
if i < 0 || i > a.len {
panic('array.insert: index out of range (i == ${i}, a.len == ${a.len})')
panic_n2('array.insert: index out of range (i,a.len):', i, a.len)
}
if a.len == max_int {
panic('array.insert: a.len reached max_int')
@ -313,11 +315,11 @@ pub fn (mut a array) insert(i int, val voidptr) {
@[unsafe]
fn (mut a array) insert_many(i int, val voidptr, size int) {
if i < 0 || i > a.len {
panic('array.insert_many: index out of range (i == ${i}, a.len == ${a.len})')
panic_n2('array.insert_many: index out of range (i,a.len):', i, a.len)
}
new_len := i64(a.len) + i64(size)
if new_len > max_int {
panic('array.insert_many: a.len = ${new_len} will exceed max_int')
panic_n('array.insert_many: max_int will be exceeded by a.len:', new_len)
}
a.ensure_cap(int(new_len))
elem_size := a.element_size
@ -374,8 +376,12 @@ pub fn (mut a array) delete(i int) {
// ```
pub fn (mut a array) delete_many(i int, size int) {
if i < 0 || i64(i) + i64(size) > i64(a.len) {
endidx := if size > 1 { '..${i + size}' } else { '' }
panic('array.delete: index out of range (i == ${i}${endidx}, a.len == ${a.len})')
if size > 1 {
panic_n3('array.delete: index out of range (i,i+size,a.len):', i, i + size,
a.len)
} else {
panic_n2('array.delete: index out of range (i,a.len):', i, a.len)
}
}
if a.flags.all(.noshrink | .noslices) {
unsafe {
@ -465,7 +471,7 @@ fn (a array) get_unsafe(i int) voidptr {
fn (a array) get(i int) voidptr {
$if !no_bounds_checking {
if i < 0 || i >= a.len {
panic('array.get: index out of range (i == ${i}, a.len == ${a.len})')
panic_n2('array.get: index out of range (i,a.len):', i, a.len)
}
}
unsafe {
@ -557,13 +563,15 @@ fn (a array) slice(start int, _end int) array {
end := if _end == max_int { a.len } else { _end } // max_int
$if !no_bounds_checking {
if start > end {
panic('array.slice: invalid slice index (${start} > ${end})')
panic('array.slice: invalid slice index (start>end):' + i64(start).str() + ', ' +
i64(end).str())
}
if end > a.len {
panic('array.slice: slice bounds out of range (${end} >= ${a.len})')
panic('array.slice: slice bounds out of range (' + i64(end).str() + ' >= ' +
i64(a.len).str() + ')')
}
if start < 0 {
panic('array.slice: slice bounds out of range (${start} < 0)')
panic('array.slice: slice bounds out of range (start<0):' + start.str())
}
}
// TODO: integrate reference counting
@ -683,7 +691,7 @@ fn (mut a array) set_unsafe(i int, val voidptr) {
fn (mut a array) set(i int, val voidptr) {
$if !no_bounds_checking {
if i < 0 || i >= a.len {
panic('array.set: index out of range (i == ${i}, a.len == ${a.len})')
panic_n2('array.set: index out of range (i,a.len):', i, a.len)
}
}
unsafe { vmemcpy(&u8(a.data) + u64(a.element_size) * u64(i), val, a.element_size) }
@ -1000,7 +1008,7 @@ pub fn copy(mut dst []u8, src []u8) int {
pub fn (mut a array) grow_cap(amount int) {
new_cap := i64(amount) + i64(a.cap)
if new_cap > max_int {
panic('array.grow_cap: new capacity ${new_cap} will exceed max_int')
panic_n('array.grow_cap: max_int will be exceeded by new cap:', new_cap)
}
a.ensure_cap(int(new_cap))
}
@ -1013,7 +1021,7 @@ pub fn (mut a array) grow_cap(amount int) {
pub fn (mut a array) grow_len(amount int) {
new_len := i64(amount) + i64(a.len)
if new_len > max_int {
panic('array.grow_len: new len ${new_len} will exceed max_int')
panic_n('array.grow_len: max_int will be exceeded by new len:', new_len)
}
a.ensure_cap(int(new_len))
a.len = int(new_len)
@ -1053,13 +1061,13 @@ pub fn (data &u8) vbytes(len int) []u8 {
@[if !no_bounds_checking ?; inline]
fn panic_on_negative_len(len int) {
if len < 0 {
panic('negative .len')
panic_n('negative .len:', len)
}
}
@[if !no_bounds_checking ?; inline]
fn panic_on_negative_cap(cap int) {
if cap < 0 {
panic('negative .cap')
panic_n('negative .cap:', cap)
}
}

View File

@ -103,7 +103,8 @@ fn (mut a array) ensure_cap_noscan(required int) {
return
}
if a.flags.has(.nogrow) {
panic('array.ensure_cap_noscan: array with the flag `.nogrow` cannot grow in size, array required new size: ${required}')
panic_n('array.ensure_cap_noscan: array with the flag `.nogrow` cannot grow in size, array required new size:',
required)
}
mut cap := if a.cap > 0 { i64(a.cap) } else { i64(2) }
for required > cap {
@ -114,7 +115,8 @@ fn (mut a array) ensure_cap_noscan(required int) {
// limit the capacity, since bigger values, will overflow the 32bit integer used to store it
cap = max_int
} else {
panic('array.ensure_cap_noscan: array needs to grow to cap = ${cap}, which is > 2^31')
panic_n('array.ensure_cap_noscan: array needs to grow to cap (which is > 2^31):',
cap)
}
}
new_size := u64(cap) * u64(a.element_size)
@ -136,7 +138,7 @@ fn (mut a array) ensure_cap_noscan(required int) {
@[unsafe]
fn (a array) repeat_to_depth_noscan(count int, depth int) array {
if count < 0 {
panic('array.repeat: count is negative: ${count}')
panic_n('array.repeat: count is negative:', count)
}
mut size := u64(count) * u64(a.len) * u64(a.element_size)
if size == 0 {
@ -170,7 +172,7 @@ fn (a array) repeat_to_depth_noscan(count int, depth int) array {
// insert inserts a value in the array at index `i`
fn (mut a array) insert_noscan(i int, val voidptr) {
if i < 0 || i > a.len {
panic('array.insert_noscan: index out of range (i == ${i}, a.len == ${a.len})')
panic_n2('array.insert_noscan: index out of range (i,a.len):', i, a.len)
}
if a.len == max_int {
panic('array.insert_noscan: a.len reached max_int')
@ -187,11 +189,11 @@ fn (mut a array) insert_noscan(i int, val voidptr) {
@[unsafe]
fn (mut a array) insert_many_noscan(i int, val voidptr, size int) {
if i < 0 || i > a.len {
panic('array.insert_many: index out of range (i == ${i}, a.len == ${a.len})')
panic_n2('array.insert_many: index out of range (i, a.len):', i, a.len)
}
new_len := i64(a.len) + i64(size)
if new_len > max_int {
panic('array.insert_many_noscan: a.len = ${new_len} will exceed max_int')
panic_n('array.insert_many_noscan: max_int will be exceeded by a.len:', new_len)
}
a.ensure_cap_noscan(a.len + size)
elem_size := a.element_size
@ -328,7 +330,7 @@ fn (a array) reverse_noscan() array {
fn (mut a array) grow_cap_noscan(amount int) {
new_cap := i64(amount) + i64(a.cap)
if new_cap > max_int {
panic('array.grow_cap: new capacity ${new_cap} will exceed max_int')
panic_n('array.grow_cap: max_int will be exceeded by new cap:', new_cap)
}
a.ensure_cap_noscan(int(new_cap))
}
@ -338,7 +340,7 @@ fn (mut a array) grow_cap_noscan(amount int) {
fn (mut a array) grow_len_noscan(amount int) {
new_len := i64(amount) + i64(a.len)
if new_len > max_int {
panic('array.grow_len: new len ${new_len} will exceed max_int')
panic_n('array.grow_len: max_int will be exceeded by new len:', new_len)
}
a.ensure_cap_noscan(int(new_len))
a.len = int(new_len)

View File

@ -10,19 +10,24 @@ pub fn print_backtrace() {
$if !no_backtrace ? {
$if freestanding {
println(bare_backtrace())
} $else $if native {
// TODO: native backtrace solution
} $else $if tinyc {
C.tcc_backtrace(c'Backtrace')
} $else $if use_libbacktrace ? {
// NOTE: TCC doesn't have the unwind library
print_libbacktrace(1)
} $else {
$if native {
// TODO: native backtrace solution
} $else $if tinyc {
C.tcc_backtrace(c'Backtrace')
} $else {
// NOTE: TCC doesn't have the unwind library
$if use_libbacktrace ? {
print_libbacktrace(1)
} $else {
print_backtrace_skipping_top_frames(2)
}
}
print_backtrace_skipping_top_frames(2)
}
}
}
fn eprint_space_padding(output string, max_len int) {
padding_len := max_len - output.len
if padding_len > 0 {
for _ in 0 .. padding_len {
eprint(' ')
}
}
}

View File

@ -67,17 +67,14 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool {
return false
}
nr_actual_frames := nr_ptrs - skipframes
mut sframes := []string{}
//////csymbols := backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames)
csymbols := C.backtrace_symbols(voidptr(&buffer[skipframes]), nr_actual_frames)
for i in 0 .. nr_actual_frames {
sframes << unsafe { tos2(&u8(csymbols[i])) }
}
for sframe in sframes {
sframe := unsafe { tos2(&u8(csymbols[i])) }
executable := sframe.all_before('(')
addr := sframe.all_after('[').all_before(']')
beforeaddr := sframe.all_before('[')
cmd := 'addr2line -e ${executable} ${addr}'
cmd := 'addr2line -e ' + executable + ' ' + addr
// taken from os, to avoid depending on the os module inside builtin.v
f := C.popen(&char(cmd.str), c'r')
if f == unsafe { nil } {
@ -92,7 +89,7 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool {
output += tos(bp, vstrlen(bp))
}
}
output = output.trim_space() + ':'
output = output.trim_chars(' \t\n', .trim_both) + ':'
if C.pclose(f) != 0 {
eprintln(sframe)
continue
@ -104,9 +101,14 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool {
// Note: it is shortened here to just d. , just so that it fits, and so
// that the common error file:lineno: line format is enforced.
output = output.replace(' (discriminator', ': (d.')
eprintln('${output:-55s} | ${addr:14s} | ${beforeaddr}')
eprint(output)
eprint_space_padding(output, 55)
eprint(' | ')
eprint(addr)
eprint(' | ')
eprintln(beforeaddr)
}
if sframes.len > 0 {
if nr_actual_frames > 0 {
unsafe { C.free(csymbols) }
}
}

View File

@ -76,6 +76,7 @@ pub fn print_backtrace_skipping_top_frames(skipframes int) bool {
return false
}
@[direct_array_access]
fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool {
$if msvc {
mut offset := u64(0)
@ -116,23 +117,31 @@ fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool {
if C.SymGetLineFromAddr64(handle, frame_addr, &offset, &sline64) == 1 {
file_name := unsafe { tos3(sline64.f_file_name) }
lnumber := sline64.f_line_number
lineinfo = '${file_name}:${lnumber}'
lineinfo = file_name + i64(lnumber).str()
} else {
// addr:
lineinfo = '?? : address = 0x${(&frame_addr):x}'
lineinfo = '?? : address = 0x' + ptr_str(frame_addr)
}
sfunc := unsafe { tos3(fname) }
eprintln('${nframe:-2d}: ${sfunc:-25s} ${lineinfo}')
snframe := i64(nframe).str()
eprint_space_padding(snframe, 2)
eprint(': ')
eprint(sfunc)
eprint_space_padding(sfunc, 25)
eprint(' ')
eprint(lineinfo)
} else {
// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes
cerr := int(C.GetLastError())
eprint('SymFromAddr failure: ')
eprint(i64(cerr).str())
if cerr == 87 {
eprintln('SymFromAddr failure: ${cerr} = The parameter is incorrect)')
eprintln(' = The parameter is incorrect)')
} else if cerr == 487 {
// probably caused because the .pdb isn't in the executable folder
eprintln('SymFromAddr failure: ${cerr} = Attempt to access invalid address (Verify that you have the .pdb file in the right folder.)')
eprintln(' = Attempt to access invalid address (Verify that you have the .pdb file in the right folder.)')
} else {
eprintln('SymFromAddr failure: ${cerr} (see https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes)')
eprintln(' (see https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes)')
}
}
}

View File

@ -106,14 +106,14 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) {
// It ends the program with a panic.
@[noreturn]
pub fn panic_option_not_set(s string) {
panic('option not set (${s})')
panic('option not set (' + s + ')')
}
// panic_result_not_set is called by V, when you use result error propagation in your main function
// It ends the program with a panic.
@[noreturn]
pub fn panic_result_not_set(s string) {
panic('result not set (${s})')
panic('result not set (' + s + ')')
}
// panic prints a nice error message, then exits the process with exit code of 1.
@ -175,6 +175,24 @@ pub fn c_error_number_str(errnum int) string {
return err_msg
}
// panic_n prints an error message, followed by the given number, then exits the process with exit code of 1.
@[noreturn]
pub fn panic_n(s string, number1 i64) {
panic(s + number1.str())
}
// panic_n2 prints an error message, followed by the given numbers, then exits the process with exit code of 1.
@[noreturn]
pub fn panic_n2(s string, number1 i64, number2 i64) {
panic(s + number1.str() + ', ' + number2.str())
}
// panic_n3 prints an error message, followed by the given numbers, then exits the process with exit code of 1.
@[noreturn]
fn panic_n3(s string, number1 i64, number2 i64, number3 i64) {
panic(s + number1.str() + ', ' + number2.str() + ', ' + number2.str())
}
// panic with a C-API error message matching `errnum`
@[noreturn]
pub fn panic_error_number(basestr string, errnum int) {
@ -751,8 +769,8 @@ pub fn gc_memory_use() usize {
fn v_fixed_index(i int, len int) int {
$if !no_bounds_checking {
if i < 0 || i >= len {
s := 'fixed array index out of range (index: ${i}, len: ${len})'
panic(s)
panic('fixed array index out of range (index: ' + i64(i).str() + ', len: ' +
i64(len).str() + ')')
}
}
return i

View File

@ -10,12 +10,6 @@ pub fn isnil(v voidptr) bool {
return v == 0
}
/*
fn on_panic(f fn(int)int) {
// TODO
}
*/
struct VCastTypeIndexName {
tindex int
tname string
@ -37,7 +31,7 @@ fn __as_cast(obj voidptr, obj_type int, expected_type int) voidptr {
expected_name = x.tname.clone()
}
}
panic('as cast: cannot cast `${obj_name}` to `${expected_name}`')
panic('as cast: cannot cast `' + obj_name + '` to `' + expected_name + '`')
}
return obj
}

View File

@ -67,6 +67,9 @@ fn builtin_init() {
$if !no_backtrace ? {
add_unhandled_exception_handler()
}
// On windows, the default buffering is block based (~4096bytes), which interferes badly with non cmd shells
// It is much better to have it off by default instead.
unbuffer_stdout()
}
// TODO: copypaste from os
@ -109,7 +112,7 @@ fn unhandled_exception_handler(e &ExceptionPointers) int {
return 0
}
else {
println('Unhandled Exception 0x${e.exception_record.code:X}')
println('Unhandled Exception 0x' + ptr_str(e.exception_record.code))
print_backtrace_skipping_top_frames(5)
}
}

View File

@ -3,29 +3,18 @@
// that can be found in the LICENSE file.
module builtin
//
// ----- value to string functions -----
//
pub struct VContext {
allocator int
}
// type u8 = byte
type byte = u8
// type i32 = int
// ptr_str returns the address of `ptr` as a `string`.
pub fn ptr_str(ptr voidptr) string {
buf1 := u64(ptr).hex()
return buf1
}
// pub fn nil_str(x voidptr) string {
// return 'nil'
//}
// str returns string equivalent of x
pub fn (x isize) str() string {
return i64(x).str()
@ -118,18 +107,7 @@ fn (nn int) str_l(max int) string {
}
diff := max - index
vmemmove(buf, voidptr(buf + index), diff + 1)
/*
// === manual memory move for bare metal ===
mut c:= 0
for c < diff {
buf[c] = buf[c+index]
c++
}
buf[c] = 0
*/
return tos(buf, diff)
// return tos(memdup(&buf[0] + index, (max - index)), (max - index))
}
}
@ -165,10 +143,6 @@ pub fn (n int) str() string {
return n.str_l(12)
}
// pub fn int_str(n int) string {
// return i64(n).str()
//}
// str returns the value of the `u32` as a `string`.
// Example: assert u32(20000).str() == '20000'
@[direct_array_access; inline]
@ -202,8 +176,6 @@ pub fn (nn u32) str() string {
diff := max - index
vmemmove(buf, voidptr(buf + index), diff + 1)
return tos(buf, diff)
// return tos(memdup(&buf[0] + index, (max - index)), (max - index))
}
}
@ -258,7 +230,6 @@ pub fn (nn i64) str() string {
diff := max - index
vmemmove(buf, voidptr(buf + index), diff + 1)
return tos(buf, diff)
// return tos(memdup(&buf[0] + index, (max - index)), (max - index))
}
}
@ -295,7 +266,6 @@ pub fn (nn u64) str() string {
diff := max - index
vmemmove(buf, voidptr(buf + index), diff + 1)
return tos(buf, diff)
// return tos(memdup(&buf[0] + index, (max - index)), (max - index))
}
}
@ -308,10 +278,6 @@ pub fn (b bool) str() string {
return 'false'
}
//
// ----- value to hex string functions -----
//
// u64_to_hex converts the number `nn` to a (zero padded if necessary) hexadecimal `string`.
@[direct_array_access; inline]
fn u64_to_hex(nn u64, len u8) string {

View File

@ -6,9 +6,19 @@ module builtin
fn (a any) toString()
// panic prints an error message, then exits the process with exit code of 1.
@[noreturn]
pub fn panic(s string) {
eprintln('V panic: ${s}\n${js_stacktrace()}')
eprintln('V panic: ' + s)
eprintln(js_stacktrace())
exit(1)
}
// panic_n prints an error message, followed by the given number, then exits the process with exit code of 1.
@[noreturn]
pub fn panic_n(s string, n i64) {
eprintln('V panic: ' + s)
eprintln(js_stacktrace())
exit(1)
}

View File

@ -158,6 +158,7 @@ fn (mut n mapnode) split_child(child_index int, mut y mapnode) {
n.len++
}
@[direct_array_access]
fn (m SortedMap) get(key string, out voidptr) bool {
mut node := m.root
for {

View File

@ -1115,7 +1115,8 @@ pub fn (s string) substr(start int, _end int) string {
end := if _end == max_int { s.len } else { _end } // max_int
$if !no_bounds_checking {
if start > end || start > s.len || end > s.len || start < 0 || end < 0 {
panic('substr(${start}, ${end}) out of bounds (len=${s.len}) s="${s}"')
panic('substr(' + start.str() + ', ' + end.str() + ') out of bounds (len=' +
s.len.str() + ') s=' + s)
}
}
len := end - start
@ -1153,7 +1154,8 @@ pub fn (s string) substr_unsafe(start int, _end int) string {
pub fn (s string) substr_with_check(start int, _end int) !string {
end := if _end == max_int { s.len } else { _end } // max_int
if start > end || start > s.len || end > s.len || start < 0 || end < 0 {
return error('substr(${start}, ${end}) out of bounds (len=${s.len})')
return error('substr(' + start.str() + ', ' + end.str() + ') out of bounds (len=' +
s.len.str() + ')')
}
len := end - start
if len == s.len {
@ -1962,7 +1964,7 @@ pub fn (s string) str() string {
fn (s string) at(idx int) u8 {
$if !no_bounds_checking {
if idx < 0 || idx >= s.len {
panic('string index out of range: ${idx} / ${s.len}')
panic_n2('string index out of range(idx,s.len):', idx, s.len)
}
}
return unsafe { s.str[idx] }

View File

@ -78,14 +78,15 @@ pub fn utf32_decode_to_buffer(code u32, mut buf &u8) int {
// it is used in vlib/builtin/string.v,
// and also in vlib/v/gen/c/cgen.v
pub fn (_rune string) utf32_code() int {
return int(_rune.bytes().utf8_to_utf32() or {
// error('more than one utf-8 rune found in this string')
rune(0)
})
if res := _rune.bytes().utf8_to_utf32() {
return int(res)
}
return 0
}
// convert array of utf8 bytes to single utf32 value
// will error if more than 4 bytes are submitted
@[direct_array_access]
pub fn (_bytes []u8) utf8_to_utf32() !rune {
if _bytes.len == 0 {
return 0

View File

@ -113,6 +113,6 @@ fn init() {
}
if ret < 0 {
panic('failed to initialize coroutines via photon (ret=${ret})')
panic_n('failed to initialize coroutines via photon ret:', ret)
}
}

View File

@ -72,7 +72,7 @@ pub fn (mut l Log) set_output_path(output_file_path string) {
l.output_target = .file
l.output_file_name = os.join_path(os.real_path(output_file_path), l.output_label)
ofile := os.open_append(l.output_file_name) or {
panic('error while opening log file ${l.output_file_name} for appending')
panic('error while opening log file ' + l.output_file_name + ' for appending')
}
l.ofile = ofile
}
@ -160,7 +160,7 @@ pub fn (mut l Log) fatal(s string) {
l.send_output(s, .fatal)
l.ofile.close()
}
panic('${l.output_label}: ${s}')
panic(l.output_label + ': ' + s)
}
// error logs line `s` via `send_output` if `Log.level` is greater than or equal to the `Level.error` category.

View File

@ -36,7 +36,7 @@ pub fn maxof[T]() T {
}
return int(max_i32)
} $else {
panic('A maximum value of the type `${typeof[T]().name}` is not defined.')
panic('A maximum value of the type `' + typeof[T]().name + '` is not defined.')
}
}
@ -73,6 +73,6 @@ pub fn minof[T]() T {
}
return int(min_i32)
} $else {
panic('A minimum value of the type `${typeof[T]().name}` is not defined.')
panic('A minimum value of the type `' + typeof[T]().name + '` is not defined.')
}
}

View File

@ -70,7 +70,7 @@ pub fn digits(num i64, params DigitParams) []int {
// set base to 10 initially and change only if base is explicitly set.
mut b := params.base
if b < 2 {
panic('digits: Cannot find digits of n with base ${b}')
panic_n('digits: Cannot find digits of n with base:', b)
}
mut n := num
mut sign := 1

View File

@ -254,7 +254,7 @@ pub fn resolve_ipaddrs(addr string, family AddrFamily, typ SocketType) ![]Addr {
addresses << new_addr
}
else {
panic('Unexpected address family ${result.ai_family}')
panic('Unexpected address family ' + result.ai_family.str())
}
}
}

View File

@ -743,7 +743,7 @@ pub fn (h Header) join(other Header) Header {
for v in other.custom_values(k, exact: true) {
combined.add_custom(k, v) or {
// panic because this should never fail
panic('unexpected error: ${err}')
panic('unexpected error: ' + err.str())
}
}
}

View File

@ -93,7 +93,7 @@ fn swap_bytes[T](input T) T {
} $else $if T is i64 {
return i64(swap_bytes_u64(u64(input)))
} $else {
panic('type is not supported: ${typeof[T]()}')
panic('type is not supported: ' + typeof[T]().str())
}
}

View File

@ -606,7 +606,7 @@ pub fn read_file_array[T](path string) []T {
// On some systems C.ftell can return values in the 64-bit range
// that, when cast to `int`, can result in values below 0.
if i64(allocate) < fsize {
panic('${fsize} cast to int results in ${int(fsize)})')
panic_n2('cast to int results in (fsize, int(fsize)):', i64(fsize), i64(int(fsize)))
}
buf := unsafe {
malloc_noscan(allocate)

View File

@ -40,7 +40,7 @@ fn test_open_file() {
file.write_string(hello) or { panic(err) }
file.close()
assert u64(hello.len) == os.file_size(filename)
read_hello := os.read_file(filename) or { panic('error reading file ${filename}') }
read_hello := os.read_file(filename) or { panic('error reading file ' + filename) }
assert hello == read_hello
os.rm(filename) or { panic(err) }
}
@ -80,7 +80,7 @@ fn test_open_file_binary() {
unsafe { file.write_ptr(bytes.data, bytes.len) }
file.close()
assert u64(hello.len) == os.file_size(filename)
read_hello := os.read_bytes(filename) or { panic('error reading file ${filename}') }
read_hello := os.read_bytes(filename) or { panic('error reading file ' + filename) }
assert bytes == read_hello
os.rm(filename) or { panic(err) }
}
@ -160,7 +160,7 @@ fn test_write_and_read_string_to_file() {
hello := 'hello world!'
os.write_file(filename, hello) or { panic(err) }
assert u64(hello.len) == os.file_size(filename)
read_hello := os.read_file(filename) or { panic('error reading file ${filename}') }
read_hello := os.read_file(filename) or { panic('error reading file ' + filename) }
assert hello == read_hello
os.rm(filename) or { panic(err) }
}
@ -322,7 +322,7 @@ fn test_cp() {
old_file_name := 'cp_example.txt'
new_file_name := 'cp_new_example.txt'
os.write_file(old_file_name, 'Test data 1 2 3, V is awesome #$%^[]!~') or { panic(err) }
os.cp(old_file_name, new_file_name) or { panic('${err}') }
os.cp(old_file_name, new_file_name) or { panic(err) }
old_file := os.read_file(old_file_name) or { panic(err) }
new_file := os.read_file(new_file_name) or { panic(err) }
assert old_file == new_file

View File

@ -258,10 +258,10 @@ fn (mut p Process) _is_pending(pkind ChildProcessPipeKind) bool {
// _check_redirection_call - should be called just by stdxxx methods
fn (mut p Process) _check_redirection_call(fn_name string) {
if !p.use_stdio_ctl {
panic('Call p.set_redirect_stdio() before calling p.${fn_name}')
panic('Call p.set_redirect_stdio() before calling p.' + fn_name)
}
if p.status == .not_started {
panic('Call p.${fn_name}() after you have called p.run()')
panic('Call p.' + fn_name + '() after you have called p.run()')
}
}

View File

@ -198,7 +198,7 @@ fn (mut p Process) win_is_alive() bool {
///////////////
fn (mut p Process) win_write_string(idx int, _s string) {
panic('Process.write_string ${idx} is not implemented yet')
panic_n('Process.write_string is not implemented yet, idx:', idx)
}
fn (mut p Process) win_read_string(idx int, _maxbytes int) (string, int) {

View File

@ -8,7 +8,7 @@ const base_digits = '0123456789abcdefghijklmnopqrstuvwxyz'
pub fn format_int(n i64, radix int) string {
unsafe {
if radix < 2 || radix > 36 {
panic('invalid radix: ${radix} . It should be => 2 and <= 36')
panic_n('invalid radix, it should be => 2 and <= 36, actual:', radix)
}
if n == 0 {
return '0'
@ -45,7 +45,7 @@ pub fn format_int(n i64, radix int) string {
pub fn format_uint(n u64, radix int) string {
unsafe {
if radix < 2 || radix > 36 {
panic('invalid radix: ${radix} . It should be => 2 and <= 36')
panic_n('invalid radix, it should be => 2 and <= 36, actual:', radix)
}
if n == 0 {
return '0'

View File

@ -551,7 +551,8 @@ pub fn v_sprintf(str string, pt ...voidptr) string {
}
if p_index != pt.len {
panic('${p_index} % conversion specifiers, but given ${pt.len} args')
panic_n2('% conversion specifiers number mismatch (expected %, given args)', p_index,
pt.len)
}
return res.str()
@ -560,7 +561,8 @@ pub fn v_sprintf(str string, pt ...voidptr) string {
@[inline]
fn v_sprintf_panic(idx int, len int) {
if idx >= len {
panic('${idx + 1} % conversion specifiers, but given only ${len} args')
panic_n2('% conversion specifiers number mismatch (expected %, given args)', idx + 1,
len)
}
}

View File

@ -13,21 +13,48 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
defer {
util.timing_measure('MARKUSED')
}
trace_skip_unused := pref_.compile_values['trace_skip_unused'] == 'true'
trace_skip_unused_all_fns := pref_.compile_values['trace_skip_unused_all_fns'] == 'true'
trace_skip_unused_fn_names := pref_.compile_values['trace_skip_unused_fn_names'] == 'true'
trace_skip_unused_interface_methods := pref_.compile_values['trace_skip_unused_interface_methods'] == 'true'
used_fns := pref_.compile_values['used_fns']
byteptr_idx_str := ast.byteptr_type_idx.str()
charptr_idx_str := ast.charptr_type_idx.str()
string_idx_str := ast.string_type_idx.str()
array_idx_str := ast.array_type_idx.str()
map_idx_str := ast.map_type_idx.str()
ref_map_idx_str := int(ast.map_type.ref()).str()
ref_densearray_idx_str := int(table.find_type('DenseArray').ref()).str()
ref_array_idx_str := int(ast.array_type.ref()).str()
// Functions that must be generated and can't be skipped
mut all_fn_root_names := []string{}
if used_fns != '' {
aused_fns := used_fns.split(',')
all_fns_keys := all_fns.keys()
mut matching := []string{}
for ufn in aused_fns {
if ufn.contains('*') {
matching_fns := all_fns_keys.filter(it.match_glob(ufn))
if matching_fns.len > 0 {
matching << matching_fns
}
} else {
matching << ufn
}
}
all_fn_root_names << matching
for m in matching {
println('> used_fn, found matching symbol: ${m}')
}
}
if pref_.backend == .native {
// Note: this is temporary, until the native backend supports more features!
all_fn_root_names << 'main.main'
} else {
byteptr_idx_str := '${ast.byteptr_type_idx}'
charptr_idx_str := '${ast.charptr_type_idx}'
string_idx_str := '${ast.string_type_idx}'
array_idx_str := '${ast.array_type_idx}'
map_idx_str := '${ast.map_type_idx}'
ref_map_idx_str := '${int(ast.map_type.ref())}'
ref_densearray_idx_str := '${int(table.find_type('DenseArray').ref())}'
ref_array_idx_str := '${int(ast.array_type.ref())}'
mut include_panic_deps := false
mut core_fns := [
'main.main',
@ -41,23 +68,13 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
'println',
'ptr_str',
]
$if debug_used_features ? {
dump(table.used_features)
}
if ast.float_literal_type.idx() in table.used_features.print_types
|| ast.f64_type_idx in table.used_features.print_types
|| ast.f32_type_idx in table.used_features.print_types {
include_panic_deps = true
}
$if windows {
if 'no_backtrace' !in pref_.compile_defines {
include_panic_deps = true
}
} $else {
if 'use_libbacktrace' in pref_.compile_defines {
core_fns << 'print_libbacktrace'
}
if 'use_libbacktrace' in pref_.compile_defines {
core_fns << 'print_libbacktrace'
}
if 'callstack' in pref_.compile_defines {
core_fns << ref_array_idx_str + '.push'
@ -69,7 +86,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
if pref_.autofree {
core_fns << string_idx_str + '.clone_static'
}
if table.used_features.as_cast || table.used_features.auto_str || pref_.is_shared {
if table.used_features.auto_str || pref_.is_shared {
include_panic_deps = true
core_fns << 'isnil'
core_fns << '__new_array'
@ -133,26 +150,19 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
if table.used_features.arr_last {
core_fns << array_idx_str + '.last'
}
if table.used_features.arr_delete {
include_panic_deps = true
}
if table.used_features.arr_insert {
core_fns << ref_array_idx_str + '.insert_many'
}
if pref_.ccompiler_type != .tinyc && 'no_backtrace' !in pref_.compile_defines {
// with backtrace on gcc/clang more code needs be generated
include_panic_deps = true
}
if table.used_features.interpolation {
include_panic_deps = true
}
if table.used_features.dump {
include_panic_deps = true
builderptr_idx := int(table.find_type('strings.Builder').ref())
builderptr_idx := int(table.find_type('strings.Builder').ref()).str()
core_fns << [
'${builderptr_idx}.str',
'${builderptr_idx}.free',
'${builderptr_idx}.write_rune',
builderptr_idx + '.str',
builderptr_idx + '.free',
builderptr_idx + '.write_rune',
]
}
if table.used_features.arr_init || table.used_features.comptime_for {
@ -205,14 +215,13 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
core_fns << 'v.trace_calls.on_call'
}
if 'C.cJSON_Parse' in all_fns {
all_fn_root_names << 'tos5'
all_fn_root_names << 'time.unix' // used by json
core_fns << 'tos5'
core_fns << 'time.unix' // used by json
table.used_features.used_maps++ // json needs new_map etc
include_panic_deps = true
}
all_fn_root_names << core_fns
if include_panic_deps {
all_fn_root_names << [
core_fns << [
'__new_array_with_default',
'__new_array_with_default_noscan',
'str_intp',
@ -225,6 +234,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
charptr_idx_str + '.vstring_literal',
]
}
all_fn_root_names << core_fns
}
if pref_.is_bare {
all_fn_root_names << [
@ -238,10 +248,11 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
}
is_noscan_whitelisted := pref_.gc_mode in [.boehm_full_opt, .boehm_incr_opt]
has_noscan := all_fn_root_names.any(it.contains('noscan')
&& it !in ['vcalloc_noscan', 'malloc_noscan'])
for k, mut mfn in all_fns {
$if trace_skip_unused_all_fns ? {
if trace_skip_unused_all_fns {
println('k: ${k} | mfn: ${mfn.name}')
}
if pref_.translated && mfn.attrs.any(it.name == 'c') {
@ -328,7 +339,6 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
continue
}
if k.starts_with('testsuite_') || k.contains('.testsuite_') {
// eprintln('>>> test suite: $k')
all_fn_root_names << k
continue
}
@ -363,10 +373,10 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
all_fn_root_names << 'main.cb_assertion_ok'
all_fn_root_names << 'main.cb_assertion_failed'
if benched_tests_sym := table.find_sym('main.BenchedTests') {
bts_type := benched_tests_sym.methods[0].params[0].typ
all_fn_root_names << '${bts_type}.testing_step_start'
all_fn_root_names << '${bts_type}.testing_step_end'
all_fn_root_names << '${bts_type}.end_testing'
bts_type := benched_tests_sym.methods[0].params[0].typ.str()
all_fn_root_names << bts_type + '.testing_step_start'
all_fn_root_names << bts_type + '.testing_step_end'
all_fn_root_names << bts_type + '.end_testing'
all_fn_root_names << 'main.start_testing'
}
}
@ -390,16 +400,18 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
interface_types := [ptype, ntype]
for method in interface_info.methods {
for typ in interface_types {
interface_implementation_method_name := '${int(typ.clear_flags())}.${method.name}'
$if trace_skip_unused_interface_methods ? {
interface_implementation_method_name := int(typ.clear_flags()).str() + '.' +
method.name
if trace_skip_unused_interface_methods {
eprintln('>> isym.name: ${isym.name} | interface_implementation_method_name: ${interface_implementation_method_name}')
}
all_fn_root_names << interface_implementation_method_name
}
}
for embed_method in table.get_embed_methods(table.sym(itype)) {
interface_implementation_method_name := '${int(embed_method.params[0].typ.clear_flags())}.${embed_method.name}'
$if trace_skip_unused_interface_methods ? {
interface_implementation_method_name :=
int(embed_method.params[0].typ.clear_flags()).str() + '.' + embed_method.name
if trace_skip_unused_interface_methods {
eprintln('>> isym.name: ${isym.name} | interface_implementation_method_name: ${interface_implementation_method_name} (embeded)')
}
all_fn_root_names << interface_implementation_method_name
@ -420,17 +432,21 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
}
}
for orm_type in orm_connection_implementations {
typ := int(orm_type)
all_fn_root_names << '${typ}.select'
all_fn_root_names << '${typ}.insert'
all_fn_root_names << '${typ}.update'
all_fn_root_names << '${typ}.delete'
all_fn_root_names << '${typ}.create'
all_fn_root_names << '${typ}.drop'
all_fn_root_names << '${typ}.last_id'
typ := int(orm_type).str()
all_fn_root_names << typ + '.select'
all_fn_root_names << typ + '.insert'
all_fn_root_names << typ + '.update'
all_fn_root_names << typ + '.delete'
all_fn_root_names << typ + '.create'
all_fn_root_names << typ + '.drop'
all_fn_root_names << typ + '.last_id'
}
}
if 'debug_used_features' in pref_.compile_defines {
dump(table.used_features)
}
mut walker := Walker.new(
table: table
files: ast_files
@ -483,7 +499,7 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
}
}
$if trace_skip_unused_fn_names ? {
if trace_skip_unused_fn_names {
for key, _ in walker.used_fns {
println('> used fn key: ${key}')
}
@ -504,11 +520,18 @@ pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&a
}
}
if table.used_features.range_index {
walker.fn_by_name(string_idx_str + '.substr')
}
if walker.as_cast_type_names.len > 0 {
walker.fn_by_name('new_array_from_c_array')
}
table.used_features.used_fns = walker.used_fns.move()
table.used_features.used_consts = walker.used_consts.move()
table.used_features.used_globals = walker.used_globals.move()
$if trace_skip_unused ? {
if trace_skip_unused {
eprintln('>> t.used_fns: ${table.used_features.used_fns.keys()}')
eprintln('>> t.used_consts: ${table.used_features.used_consts.keys()}')
eprintln('>> t.used_globals: ${table.used_features.used_globals.keys()}')
@ -554,9 +577,9 @@ fn all_fn_const_and_global(ast_files []&ast.File) (map[string]ast.FnDecl, map[st
fn mark_all_methods_used(mut table ast.Table, mut all_fn_root_names []string, typ ast.Type) {
sym := table.sym(typ)
styp := '${int(typ)}'
styp := int(typ).str()
for method in sym.methods {
all_fn_root_names << '${styp}.${method.name}'
all_fn_root_names << styp + '.' + method.name
}
}
@ -572,7 +595,7 @@ fn handle_vweb(mut table ast.Table, mut all_fn_root_names []string, result_name
mark_all_methods_used(mut table, mut all_fn_root_names, typ_vweb_context)
for vgt in table.used_features.used_veb_types {
sym_app := table.sym(vgt)
pvgt := '${int(vgt.set_nr_muls(1))}'
pvgt := int(vgt.set_nr_muls(1)).str()
for m in sym_app.methods {
mut skip := true
if m.name == 'before_request' {
@ -586,7 +609,7 @@ fn handle_vweb(mut table ast.Table, mut all_fn_root_names []string, result_name
continue
}
// eprintln('vgt: $vgt | pvgt: $pvgt | sym_app.name: $sym_app.name | m.name: $m.name')
all_fn_root_names << '${pvgt}.${m.name}'
all_fn_root_names << pvgt + '.' + m.name
}
}
}

View File

@ -22,6 +22,8 @@ mut:
all_fns map[string]ast.FnDecl
all_consts map[string]ast.ConstField
all_globals map[string]ast.GlobalField
//
as_cast_type_names map[string]string
}
pub fn Walker.new(params Walker) &Walker {
@ -367,6 +369,10 @@ fn (mut w Walker) expr(node_ ast.Expr) {
w.features.used_maps++
} else if sym.kind == .array {
w.features.used_arrays++
} else if sym.kind == .string {
if node.index is ast.RangeExpr {
w.features.range_index = true
}
}
}
ast.InfixExpr {
@ -507,6 +513,7 @@ fn (mut w Walker) expr(node_ ast.Expr) {
///
ast.AsCast {
w.expr(node.expr)
w.as_cast(node)
}
ast.AtExpr {}
ast.BoolLiteral {}
@ -726,3 +733,37 @@ pub fn (mut w Walker) or_block(node ast.OrExpr) {
w.stmts(node.stmts)
}
}
pub fn (mut w Walker) as_cast(node ast.AsCast) {
if node.typ == 0 {
return
}
if node.typ.has_flag(.generic) {
w.as_cast_type_names['some_generic_type'] = 'some_generic_name'
return
}
if node.expr_type == 0 {
return
}
if node.expr_type.has_flag(.generic) {
w.as_cast_type_names['some_generic_type'] = 'some_generic_name'
return
}
mut expr_type_sym := w.table.sym(node.expr_type)
if mut expr_type_sym.info is ast.SumType {
w.fill_as_cast_type_names(expr_type_sym.info.variants)
} else if mut expr_type_sym.info is ast.Interface && node.expr_type != node.typ {
w.fill_as_cast_type_names(expr_type_sym.info.types)
}
}
fn (mut w Walker) fill_as_cast_type_names(types []ast.Type) {
for variant in types {
idx := u32(variant).str()
if idx in w.as_cast_type_names {
continue
}
variant_sym := w.table.sym(variant)
w.as_cast_type_names[idx] = variant_sym.name
}
}

View File

@ -188,7 +188,7 @@ pub fn (mut am AssetManager) add(asset_type string, file string) bool {
} else if asset_type == 'js' {
am.js << asset
} else {
panic('${unknown_asset_type_error} (${asset_type}).')
panic(unknown_asset_type_error + ' ' + asset_type + ' .')
}
return true
}
@ -205,7 +205,7 @@ fn (am AssetManager) exists(asset_type string, file string) bool {
fn (am AssetManager) get_assets(asset_type string) []Asset {
if asset_type != 'css' && asset_type != 'js' {
panic('${unknown_asset_type_error} (${asset_type}).')
panic(unknown_asset_type_error + ' ' + asset_type + ' .')
}
assets := if asset_type == 'css' { am.css } else { am.js }
return assets

View File

@ -1124,6 +1124,6 @@ pub fn string_buffer_to_generic_number[T](result &T, data []u8) {
}
*result = T(enumeration)
} $else {
panic('unsupported type ${typeof[T]().name}')
panic('unsupported type ' + typeof[T]().name)
}
}

View File

@ -39,7 +39,7 @@ pub fn strict_check[T](json_data string) StructCheckResult {
field_name := field.name
last_key := arrays.find_last(key_struct, fn [field_name] (k KeyStruct) bool {
return k.key == field_name
}) or { panic('${field.name} not found') }
}) or { panic('field not found: ' + field.name) }
// TODO: get path here from `last_key.key`
if last_key.value_type == .map {