Skip to content

Commit 1d9c583

Browse files
authored
perf,cgen: add integer overflow check (#25442)
1 parent 60b0ee9 commit 1d9c583

140 files changed

Lines changed: 1894 additions & 19 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎vlib/builtin/overflow/overflow.v‎

Lines changed: 446 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// vtest build: !self_sandboxed_packaging?
2+
import os
3+
import time
4+
import term
5+
import v.util.diff
6+
import v.util.vtest
7+
8+
const vexe = @VEXE
9+
10+
const vroot = os.real_path(@VMODROOT)
11+
12+
const testdata_folder = os.join_path(vroot, 'vlib', 'builtin', 'overflow', 'testdata')
13+
14+
fn mm(s string) string {
15+
return term.colorize(term.magenta, s)
16+
}
17+
18+
fn mj(input ...string) string {
19+
return mm(input.filter(it.len > 0).join(' '))
20+
}
21+
22+
fn test_out_files() {
23+
println(term.colorize(term.green, '> testing whether .out files match:'))
24+
os.chdir(vroot) or {}
25+
output_path := os.join_path(os.vtmp_dir(), 'overflow_outs')
26+
os.mkdir_all(output_path)!
27+
defer {
28+
os.rmdir_all(output_path) or {}
29+
}
30+
files := os.ls(testdata_folder) or { [] }
31+
tests := files.filter(it.ends_with('.out'))
32+
if tests.len == 0 {
33+
eprintln('no `.out` tests found in ${testdata_folder}')
34+
return
35+
}
36+
paths := vtest.filter_vtest_only(tests, basepath: testdata_folder).sorted()
37+
mut total_errors := 0
38+
for out_path in paths {
39+
basename, path, relpath, out_relpath := target2paths(out_path, '.out')
40+
pexe := os.join_path(output_path, '${basename}.exe')
41+
//
42+
file_options := '-g -check-overflow'
43+
alloptions := '-o ${os.quoted_path(pexe)} ${file_options}'
44+
label := mj('v', file_options, 'run', relpath) + ' == ${mm(out_relpath)} '
45+
//
46+
compile_cmd := '${os.quoted_path(vexe)} ${alloptions} ${os.quoted_path(path)}'
47+
sw_compile := time.new_stopwatch()
48+
compilation := os.execute(compile_cmd)
49+
compile_ms := sw_compile.elapsed().milliseconds()
50+
ensure_compilation_succeeded(compilation, compile_cmd)
51+
//
52+
sw_run := time.new_stopwatch()
53+
res := os.execute(os.quoted_path(pexe))
54+
run_ms := sw_run.elapsed().milliseconds()
55+
//
56+
if res.exit_code < 0 {
57+
println('nope')
58+
panic(res.output)
59+
}
60+
mut found := res.output.trim_right('\r\n').replace('\r\n', '\n')
61+
mut expected := os.read_file(out_path)!
62+
expected = expected.trim_right('\r\n').replace('\r\n', '\n')
63+
if expected.contains('================ V panic ================') {
64+
// panic include backtraces and absolute file paths, so can't do char by char comparison
65+
n_found := normalize_panic_message(found, vroot)
66+
n_expected := normalize_panic_message(expected, vroot)
67+
if found.contains('================ V panic ================') {
68+
if n_found.starts_with(n_expected) {
69+
println('${term.green('OK (panic)')} C:${compile_ms:6}ms, R:${run_ms:2}ms ${label}')
70+
continue
71+
} else {
72+
// Both have panics, but there was a difference...
73+
// Pass the normalized strings for further reporting.
74+
// There is no point in comparing the backtraces too.
75+
found = n_found
76+
expected = n_expected
77+
}
78+
}
79+
}
80+
if expected != found {
81+
println('${term.red('FAIL')} C:${compile_ms:6}ms, R:${run_ms:2}ms ${label}')
82+
if diff_ := diff.compare_text(expected, found) {
83+
println(term.header('difference:', '-'))
84+
println(diff_)
85+
} else {
86+
println(term.header('expected:', '-'))
87+
println(expected)
88+
println(term.header('found:', '-'))
89+
println(found)
90+
}
91+
println(term.h_divider('-'))
92+
total_errors++
93+
} else {
94+
println('${term.green('OK ')} C:${compile_ms:6}ms, R:${run_ms:2}ms ${label}')
95+
}
96+
}
97+
assert total_errors == 0
98+
}
99+
100+
fn normalize_panic_message(message string, vroot string) string {
101+
mut msg := message.all_before('=========================================')
102+
// change windows to nix path
103+
s := vroot.replace(os.path_separator, '/')
104+
msg = msg.replace(s + '/', '')
105+
msg = msg.trim_space()
106+
return msg
107+
}
108+
109+
fn vroot_relative(opath string) string {
110+
nvroot := vroot.replace(os.path_separator, '/') + '/'
111+
npath := opath.replace(os.path_separator, '/')
112+
return npath.replace(nvroot, '')
113+
}
114+
115+
fn ensure_compilation_succeeded(compilation os.Result, cmd string) {
116+
if compilation.exit_code < 0 {
117+
eprintln('> cmd exit_code < 0, cmd: ${cmd}')
118+
panic(compilation.output)
119+
}
120+
if compilation.exit_code != 0 {
121+
eprintln('> cmd exit_code != 0, cmd: ${cmd}')
122+
panic('compilation failed: ${compilation.output}')
123+
}
124+
}
125+
126+
fn target2paths(target_path string, postfix string) (string, string, string, string) {
127+
basename := os.file_name(target_path).replace(postfix, '')
128+
target_dir := os.dir(target_path)
129+
path := os.join_path(target_dir, '${basename}.vv')
130+
relpath := vroot_relative(path)
131+
target_relpath := vroot_relative(target_path)
132+
return basename, path, relpath, target_relpath
133+
}
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
module main
2+
3+
import os
4+
import strings
5+
6+
struct OverflowStruct {
7+
name string
8+
max [2]string // add by 1 every time, should overflow
9+
min [2]string // sub by 1 every time, should overflow
10+
mid [2]string // mult by 10 every time, should overflow
11+
}
12+
13+
// TODO: add tests for `rune`,`int`,`isize`,`usize`.
14+
const overflows = [
15+
OverflowStruct{
16+
name: 'u8'
17+
max: ['254', '255']!
18+
min: ['1', '0']!
19+
mid: ['25', '250']!
20+
},
21+
OverflowStruct{
22+
name: 'u16'
23+
max: ['65534', '65535']!
24+
min: ['1', '0']!
25+
mid: ['6553', '65530']!
26+
},
27+
OverflowStruct{
28+
name: 'u32'
29+
max: ['4294967294', '4294967295']!
30+
min: ['1', '0']!
31+
mid: ['429496729', '4294967290']!
32+
},
33+
OverflowStruct{
34+
name: 'u64'
35+
max: ['18446744073709551614', '18446744073709551615']!
36+
min: ['1', '0']!
37+
mid: ['1844674407370955161', '18446744073709551610']!
38+
},
39+
OverflowStruct{
40+
name: 'i8'
41+
max: ['126', '127']!
42+
min: ['-127', '-128']!
43+
mid: ['12', '120']!
44+
},
45+
OverflowStruct{
46+
name: 'i16'
47+
max: ['32766', '32767']!
48+
min: ['-32767', '-32768']!
49+
mid: ['3276', '32760']!
50+
},
51+
OverflowStruct{
52+
name: 'i32'
53+
max: ['2147483646', '2147483647']!
54+
min: ['-2147483647', '-2147483648']!
55+
mid: ['214748364', '2147483640']!
56+
},
57+
OverflowStruct{
58+
name: 'i64'
59+
max: ['9223372036854775806', '9223372036854775807']!
60+
min: ['-9223372036854775807', '-9223372036854775808']!
61+
mid: ['922337203685477580', '9223372036854775800']!
62+
},
63+
]
64+
65+
const ops = ['add', 'add_assign', 'inc', 'sub', 'sub_assign', 'dec', 'mul', 'mul_assign']
66+
67+
fn main() {
68+
mut sb := strings.new_builder(1024)
69+
for o in overflows {
70+
for op in ops {
71+
match op {
72+
'add' {
73+
// vv file
74+
sb.writeln('mut x := ${o.name}(${o.max[0]})')
75+
sb.writeln('println(x)')
76+
sb.writeln('x = x + 1')
77+
sb.writeln('println(x)')
78+
sb.writeln('x = x + 1')
79+
sb.writeln('println(x)')
80+
os.write_file('panic_on_${o.name}_${op}_overflow.vv', sb.str())!
81+
82+
// out file
83+
sb.writeln('${o.max[0]}')
84+
sb.writeln('${o.max[1]}')
85+
sb.writeln('================ V panic ================')
86+
sb.writeln(' module: builtin.overflow')
87+
sb.writeln(' function: add_${o.name}()')
88+
sb.writeln(' message: attempt to add with overflow(${o.name}(${o.max[1]}) + ${o.name}(1))')
89+
sb.writeln(' file: vlib/builtin/overflow/overflow.v\n')
90+
os.write_file('panic_on_${o.name}_${op}_overflow.out', sb.str())!
91+
}
92+
'add_assign' {
93+
// vv file
94+
sb.writeln('mut x := ${o.name}(${o.max[0]})')
95+
sb.writeln('println(x)')
96+
sb.writeln('x += 1')
97+
sb.writeln('println(x)')
98+
sb.writeln('x += 1')
99+
sb.writeln('println(x)')
100+
os.write_file('panic_on_${o.name}_${op}_overflow.vv', sb.str())!
101+
102+
// out file
103+
sb.writeln('${o.max[0]}')
104+
sb.writeln('${o.max[1]}')
105+
sb.writeln('================ V panic ================')
106+
sb.writeln(' module: builtin.overflow')
107+
sb.writeln(' function: add_${o.name}()')
108+
sb.writeln(' message: attempt to add with overflow(${o.name}(${o.max[1]}) + ${o.name}(1))')
109+
sb.writeln(' file: vlib/builtin/overflow/overflow.v\n')
110+
os.write_file('panic_on_${o.name}_${op}_overflow.out', sb.str())!
111+
}
112+
'inc' {
113+
// vv file
114+
sb.writeln('mut x := ${o.name}(${o.max[0]})')
115+
sb.writeln('println(x)')
116+
sb.writeln('x ++')
117+
sb.writeln('println(x)')
118+
sb.writeln('x ++')
119+
sb.writeln('println(x)')
120+
os.write_file('panic_on_${o.name}_${op}_overflow.vv', sb.str())!
121+
122+
// out file
123+
sb.writeln('${o.max[0]}')
124+
sb.writeln('${o.max[1]}')
125+
sb.writeln('================ V panic ================')
126+
sb.writeln(' module: builtin.overflow')
127+
sb.writeln(' function: add_${o.name}()')
128+
sb.writeln(' message: attempt to add with overflow(${o.name}(${o.max[1]}) + ${o.name}(1))')
129+
sb.writeln(' file: vlib/builtin/overflow/overflow.v\n')
130+
os.write_file('panic_on_${o.name}_${op}_overflow.out', sb.str())!
131+
}
132+
'sub' {
133+
// vv file
134+
sb.writeln('mut x := ${o.name}(${o.min[0]})')
135+
sb.writeln('println(x)')
136+
sb.writeln('x = x - 1')
137+
sb.writeln('println(x)')
138+
sb.writeln('x = x - 1')
139+
sb.writeln('println(x)')
140+
os.write_file('panic_on_${o.name}_${op}_overflow.vv', sb.str())!
141+
142+
// out file
143+
sb.writeln('${o.min[0]}')
144+
sb.writeln('${o.min[1]}')
145+
sb.writeln('================ V panic ================')
146+
sb.writeln(' module: builtin.overflow')
147+
sb.writeln(' function: sub_${o.name}()')
148+
sb.writeln(' message: attempt to sub with overflow(${o.name}(${o.min[1]}) - ${o.name}(1))')
149+
sb.writeln(' file: vlib/builtin/overflow/overflow.v\n')
150+
os.write_file('panic_on_${o.name}_${op}_overflow.out', sb.str())!
151+
}
152+
'sub_assign' {
153+
// vv file
154+
sb.writeln('mut x := ${o.name}(${o.min[0]})')
155+
sb.writeln('println(x)')
156+
sb.writeln('x -= 1')
157+
sb.writeln('println(x)')
158+
sb.writeln('x -= 1')
159+
sb.writeln('println(x)')
160+
os.write_file('panic_on_${o.name}_${op}_overflow.vv', sb.str())!
161+
162+
// out file
163+
sb.writeln('${o.min[0]}')
164+
sb.writeln('${o.min[1]}')
165+
sb.writeln('================ V panic ================')
166+
sb.writeln(' module: builtin.overflow')
167+
sb.writeln(' function: sub_${o.name}()')
168+
sb.writeln(' message: attempt to sub with overflow(${o.name}(${o.min[1]}) - ${o.name}(1))')
169+
sb.writeln(' file: vlib/builtin/overflow/overflow.v\n')
170+
os.write_file('panic_on_${o.name}_${op}_overflow.out', sb.str())!
171+
}
172+
'dec' {
173+
// vv file
174+
sb.writeln('mut x := ${o.name}(${o.min[0]})')
175+
sb.writeln('println(x)')
176+
sb.writeln('x --')
177+
sb.writeln('println(x)')
178+
sb.writeln('x --')
179+
sb.writeln('println(x)')
180+
os.write_file('panic_on_${o.name}_${op}_overflow.vv', sb.str())!
181+
182+
// out file
183+
sb.writeln('${o.min[0]}')
184+
sb.writeln('${o.min[1]}')
185+
sb.writeln('================ V panic ================')
186+
sb.writeln(' module: builtin.overflow')
187+
sb.writeln(' function: sub_${o.name}()')
188+
sb.writeln(' message: attempt to sub with overflow(${o.name}(${o.min[1]}) - ${o.name}(1))')
189+
sb.writeln(' file: vlib/builtin/overflow/overflow.v\n')
190+
os.write_file('panic_on_${o.name}_${op}_overflow.out', sb.str())!
191+
}
192+
'mul' {
193+
// vv file
194+
sb.writeln('mut x := ${o.name}(${o.mid[0]})')
195+
sb.writeln('println(x)')
196+
sb.writeln('x = x * 10')
197+
sb.writeln('println(x)')
198+
sb.writeln('x = x * 10')
199+
sb.writeln('println(x)')
200+
os.write_file('panic_on_${o.name}_${op}_overflow.vv', sb.str())!
201+
202+
// out file
203+
sb.writeln('${o.mid[0]}')
204+
sb.writeln('${o.mid[1]}')
205+
sb.writeln('================ V panic ================')
206+
sb.writeln(' module: builtin.overflow')
207+
sb.writeln(' function: mul_${o.name}()')
208+
sb.writeln(' message: attempt to mul with overflow(${o.name}(${o.mid[1]}) * ${o.name}(10))')
209+
sb.writeln(' file: vlib/builtin/overflow/overflow.v\n')
210+
os.write_file('panic_on_${o.name}_${op}_overflow.out', sb.str())!
211+
}
212+
'mul_assign' {
213+
// vv file
214+
sb.writeln('mut x := ${o.name}(${o.mid[0]})')
215+
sb.writeln('println(x)')
216+
sb.writeln('x *= 10')
217+
sb.writeln('println(x)')
218+
sb.writeln('x *= 10')
219+
sb.writeln('println(x)')
220+
os.write_file('panic_on_${o.name}_${op}_overflow.vv', sb.str())!
221+
222+
// out file
223+
sb.writeln('${o.mid[0]}')
224+
sb.writeln('${o.mid[1]}')
225+
sb.writeln('================ V panic ================')
226+
sb.writeln(' module: builtin.overflow')
227+
sb.writeln(' function: mul_${o.name}()')
228+
sb.writeln(' message: attempt to mul with overflow(${o.name}(${o.mid[1]}) * ${o.name}(10))')
229+
sb.writeln(' file: vlib/builtin/overflow/overflow.v\n')
230+
os.write_file('panic_on_${o.name}_${op}_overflow.out', sb.str())!
231+
}
232+
else {}
233+
}
234+
}
235+
}
236+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
32766
2+
32767
3+
================ V panic ================
4+
module: builtin.overflow
5+
function: add_i16()
6+
message: attempt to add with overflow(i16(32767) + i16(1))
7+
file: vlib/builtin/overflow/overflow.v
8+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
mut x := i16(32766)
2+
println(x)
3+
x += 1
4+
println(x)
5+
x += 1
6+
println(x)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
32766
2+
32767
3+
================ V panic ================
4+
module: builtin.overflow
5+
function: add_i16()
6+
message: attempt to add with overflow(i16(32767) + i16(1))
7+
file: vlib/builtin/overflow/overflow.v
8+

0 commit comments

Comments
 (0)