v4 / vlib / v3 / tests / callback_userdata_fn_field_codegen_test.v
444 lines · 389 sloc · 11.85 KB · 288feee4702b35beadb4037d02b96b5c8674156b
Raw
1import os
2import strings
3
4const callback_vexe = @VEXE
5const callback_tests_dir = os.dir(@FILE)
6const callback_v3_dir = os.dir(callback_tests_dir)
7const callback_vlib_dir = os.dir(callback_v3_dir)
8const callback_v3_src = os.join_path(callback_v3_dir, 'v3.v')
9
10fn callback_build_v3() string {
11 return callback_build_v3_with_flags('serial', '')
12}
13
14fn callback_build_v3_parallel() string {
15 return callback_build_v3_with_flags('parallel', '-d parallel')
16}
17
18fn callback_build_v3_with_flags(name string, flags string) string {
19 v3_bin := os.join_path(os.temp_dir(), 'v3_callback_userdata_test_${name}_${os.getpid()}')
20 os.rm(v3_bin) or {}
21 build :=
22 os.execute('${callback_vexe} ${flags} -gc none -path "${callback_vlib_dir}|@vlib|@vmodules" -o ${v3_bin} ${callback_v3_src}')
23 assert build.exit_code == 0, build.output
24 return v3_bin
25}
26
27fn callback_write_source(name string, source string) string {
28 path := os.join_path(os.temp_dir(), 'v3_callback_userdata_${name}_${os.getpid()}.v')
29 os.write_file(path, source) or { panic(err) }
30 return path
31}
32
33fn callback_compile(v3_bin string, source string, name string) os.Result {
34 out := os.join_path(os.temp_dir(), 'v3_callback_userdata_${name}_${os.getpid()}')
35 return os.execute('${v3_bin} ${source} -b c -o ${out}')
36}
37
38fn callback_write_project_file(root string, rel string, source string) string {
39 path := os.join_path(root, rel)
40 os.mkdir_all(os.dir(path)) or { panic(err) }
41 os.write_file(path, source) or { panic(err) }
42 return path
43}
44
45fn callback_adapter_names(generated string, prefix string) []string {
46 mut names := []string{}
47 for line in generated.split_into_lines() {
48 if line.starts_with('static void ${prefix}') {
49 name := line.all_after('static void ').all_before('(')
50 if name !in names {
51 names << name
52 }
53 }
54 }
55 return names
56}
57
58fn test_callback_userdata_fn_field_adapters() {
59 v3_bin := callback_build_v3()
60 good_src := callback_write_source('good', 'module main
61
62struct Event {
63 code int
64}
65
66struct App {
67mut:
68 frames int
69 events int
70 directs int
71}
72
73struct Config {
74 frame fn (voidptr)
75 event fn (&Event, voidptr)
76 native_event fn (&Event, voidptr)
77 direct fn (voidptr)
78 user_data voidptr
79}
80
81type FrameAlias = fn (voidptr)
82type EventAlias = fn (&Event, voidptr)
83
84struct AliasConfig {
85 alias_frame FrameAlias
86 alias_event EventAlias
87 user_data voidptr
88}
89
90struct StructAliasTarget {
91 alias2_frame FrameAlias
92 alias2_event EventAlias
93 user_data voidptr
94}
95
96type StructAlias = StructAliasTarget
97
98@[params]
99struct Params {
100 frame fn (voidptr)
101 event fn (&Event, voidptr)
102 native_event fn (&Event, voidptr)
103 direct fn (voidptr)
104 user_data voidptr
105}
106
107fn run_config(mut app App, cfg Config) int {
108 cfg.frame(cfg.user_data)
109 e := Event{code: 3}
110 cfg.event(&e, cfg.user_data)
111 cfg.native_event(&e, cfg.user_data)
112 cfg.direct(cfg.user_data)
113 return app.frames + app.events + app.directs
114}
115
116fn run_params(mut app App, params Params) int {
117 params.frame(params.user_data)
118 e := Event{code: 4}
119 params.event(&e, params.user_data)
120 params.native_event(&e, params.user_data)
121 params.direct(params.user_data)
122 return app.frames + app.events + app.directs
123}
124
125fn use_cb(cb fn (voidptr), data voidptr) {
126 cb(data)
127}
128
129fn frame(mut app App) {
130 app.frames++
131}
132
133fn on_event(e &Event, mut app App) {
134 app.events += e.code
135}
136
137fn erased_event(e voidptr, data voidptr) {
138 event := unsafe { &Event(e) }
139 mut app := unsafe { &App(data) }
140 app.events += event.code * 10
141}
142
143fn direct(ptr voidptr) {
144 mut app := unsafe { &App(ptr) }
145 app.directs++
146}
147
148fn main() {
149 mut a := App{}
150 cfg := Config{
151 frame: frame
152 event: on_event
153 native_event: erased_event
154 direct: direct
155 user_data: voidptr(&a)
156 }
157 first := run_config(mut a, cfg)
158 mut b := App{}
159 second := run_params(mut b, frame: frame, event: on_event, native_event: erased_event, direct: direct, user_data: voidptr(&b))
160 mut c := App{}
161 use_cb(frame, voidptr(&c))
162 alias_cfg := AliasConfig{alias_frame: frame, alias_event: on_event, user_data: voidptr(&c)}
163 alias_target := StructAlias{alias2_frame: frame, alias2_event: on_event, user_data: voidptr(&c)}
164 if alias_cfg.user_data == voidptr(0) || alias_target.user_data == voidptr(0) {
165 println(int_str(-1))
166 }
167 println(int_str(first + second + c.frames))
168}
169')
170 good_out := os.join_path(os.temp_dir(), 'v3_callback_userdata_good_${os.getpid()}')
171 good_compile := os.execute('${v3_bin} ${good_src} -b c -o ${good_out}')
172 assert good_compile.exit_code == 0, good_compile.output
173 run := os.execute(good_out)
174 assert run.exit_code == 0, run.output
175 assert run.output.trim_space() == '82'
176 generated := os.read_file(good_out + '.c') or { panic(err) }
177 assert generated.contains('callback_adapter'), generated
178 assert generated.contains('frame_callback_adapter'), generated
179 assert generated.contains('on_event_callback_adapter'), generated
180 assert generated.contains('erased_event_callback_adapter'), generated
181 assert generated.contains('frame((App*)arg0);'), generated
182 assert generated.contains('on_event(arg0, (App*)arg1);'), generated
183 assert generated.contains('erased_event((void*)arg0, arg1);'), generated
184 assert generated.contains('use_cb(frame_callback_adapter_'), generated
185 assert generated.contains('.alias_frame = frame_callback_adapter_'), generated
186 assert generated.contains('.alias_event = on_event_callback_adapter_'), generated
187 assert generated.contains('.alias2_frame = frame_callback_adapter_'), generated
188 assert generated.contains('.alias2_event = on_event_callback_adapter_'), generated
189 assert generated.contains('.direct = direct'), generated
190 assert !generated.contains('.frame = frame,'), generated
191 assert !generated.contains('.event = on_event,'), generated
192 assert !generated.contains('.native_event = erased_event,'), generated
193 assert !generated.contains('.alias_frame = frame,'), generated
194 assert !generated.contains('.alias_event = on_event,'), generated
195 assert !generated.contains('.alias2_frame = frame,'), generated
196 assert !generated.contains('.alias2_event = on_event,'), generated
197 assert !generated.contains('.frame = (_fn_ptr'), generated
198 assert !generated.contains('.event = (_fn_ptr'), generated
199 assert !generated.contains('.native_event = (_fn_ptr'), generated
200 assert !generated.contains('use_cb((_fn_ptr'), generated
201 assert !generated.contains('use_cb(frame,'), generated
202 assert !generated.contains('direct_callback_adapter'), generated
203
204 wrong_arity := callback_write_source('wrong_arity', 'module main
205struct Config {
206 frame fn (voidptr)
207}
208fn wrong(a voidptr, b int) {}
209fn main() {
210 _ := Config{frame: wrong}
211}
212')
213 arity_compile := callback_compile(v3_bin, wrong_arity, 'wrong_arity')
214 assert arity_compile.exit_code != 0, arity_compile.output
215
216 wrong_return := callback_write_source('wrong_return', 'module main
217struct Config {
218 frame fn (voidptr)
219}
220fn wrong(a voidptr) int {
221 return 1
222}
223fn main() {
224 _ := Config{frame: wrong}
225}
226')
227 return_compile := callback_compile(v3_bin, wrong_return, 'wrong_return')
228 assert return_compile.exit_code != 0, return_compile.output
229
230 wrong_return_pointer := callback_write_source('wrong_return_pointer', 'module main
231struct Event {}
232struct Config {
233 cb fn () &Event
234}
235fn ret_voidptr() voidptr {
236 return voidptr(0)
237}
238fn main() {
239 _ := Config{cb: ret_voidptr}
240}
241')
242 return_pointer_compile := callback_compile(v3_bin, wrong_return_pointer, 'wrong_return_pointer')
243 assert return_pointer_compile.exit_code != 0, return_pointer_compile.output
244
245 wrong_param := callback_write_source('wrong_param', 'module main
246struct Event {}
247struct Other {}
248struct App {}
249struct Config {
250 event fn (&Event, voidptr)
251}
252fn wrong(e &Other, mut app App) {}
253fn main() {
254 _ := Config{event: wrong}
255}
256')
257 param_compile := callback_compile(v3_bin, wrong_param, 'wrong_param')
258 assert param_compile.exit_code != 0, param_compile.output
259
260 wrong_primitive_param := callback_write_source('wrong_primitive_param', 'module main
261struct Config {
262 cb fn (i64)
263}
264fn takes_int(x int) {}
265fn main() {
266 _ := Config{cb: takes_int}
267}
268')
269 primitive_param_compile := callback_compile(v3_bin, wrong_primitive_param,
270 'wrong_primitive_param')
271 assert primitive_param_compile.exit_code != 0, primitive_param_compile.output
272
273 homonym_root := os.join_path(os.temp_dir(), 'v3_callback_userdata_homonym_${os.getpid()}')
274 os.rmdir_all(homonym_root) or {}
275 callback_write_project_file(homonym_root, 'left/left.v', 'module left
276
277pub struct App {
278pub mut:
279 hits int
280}
281
282pub fn hit(mut app App) {
283 app.hits += 10
284}
285')
286 callback_write_project_file(homonym_root, 'right/right.v', 'module right
287
288pub struct App {
289pub mut:
290 hits int
291}
292
293pub fn hit(mut app App) {
294 app.hits += 100
295}
296')
297 homonym_main := callback_write_project_file(homonym_root, 'main.v', 'module main
298
299import left
300import right
301
302struct Config {
303 cb fn (voidptr)
304 user_data voidptr
305}
306
307fn run(cfg Config) {
308 cfg.cb(cfg.user_data)
309}
310
311fn main() {
312 _ := left.App{}
313 mut app := right.App{}
314 cfg := Config{
315 cb: right.hit
316 user_data: voidptr(&app)
317 }
318 run(cfg)
319 println(int_str(app.hits))
320}
321')
322 homonym_out := os.join_path(os.temp_dir(), 'v3_callback_userdata_homonym_out_${os.getpid()}')
323 homonym_compile := os.execute('${v3_bin} ${homonym_main} -b c -o ${homonym_out}')
324 assert homonym_compile.exit_code == 0, homonym_compile.output
325 homonym_run := os.execute(homonym_out)
326 assert homonym_run.exit_code == 0, homonym_run.output
327 assert homonym_run.output.trim_space() == '100'
328 homonym_c := os.read_file(homonym_out + '.c') or { panic(err) }
329 assert homonym_c.contains('right__hit_callback_adapter'), homonym_c
330 assert homonym_c.contains('right__hit((right__App*)arg0);'), homonym_c
331 assert !homonym_c.contains('left__hit_callback_adapter'), homonym_c
332
333 c_fn_src := callback_write_source('c_fn_value', 'module main
334
335fn C.abs(int) int
336
337struct App {
338mut:
339 hits int
340}
341
342fn abs(mut app App) int {
343 app.hits = 99
344 return app.hits
345}
346
347fn call_c(cb fn (int) int) int {
348 return cb(-3)
349}
350
351fn main() {
352 _ := App{}
353 rc := call_c(C.abs)
354 println(int_str(rc))
355}
356')
357 c_fn_out := os.join_path(os.temp_dir(), 'v3_callback_userdata_c_fn_${os.getpid()}')
358 c_fn_compile := os.execute('${v3_bin} ${c_fn_src} -b c -o ${c_fn_out}')
359 assert c_fn_compile.exit_code == 0, c_fn_compile.output
360 c_fn_run := os.execute(c_fn_out)
361 assert c_fn_run.exit_code == 0, c_fn_run.output
362 assert c_fn_run.output.trim_space() == '3'
363 c_fn_c := os.read_file(c_fn_out + '.c') or { panic(err) }
364 assert c_fn_c.contains('call_c(abs)'), c_fn_c
365 assert !c_fn_c.contains('call_c((_fn_ptr'), c_fn_c
366 assert !c_fn_c.contains('int abs(App*'), c_fn_c
367}
368
369fn test_callback_parallel_wrapper_names_are_stable() {
370 v3_bin := callback_build_v3_parallel()
371 mut src := strings.new_builder(128 * 1024)
372 src.write_string('module main
373
374struct EventA {
375 code int
376}
377
378struct EventB {
379 code int
380}
381
382struct App {
383mut:
384 total int
385}
386
387struct ConfigA {
388 cb fn (&EventA, voidptr)
389 user_data voidptr
390}
391
392struct ConfigB {
393 cb fn (&EventB, voidptr)
394 user_data voidptr
395}
396
397fn erased(e voidptr, data voidptr) {
398 _ := e
399 mut app := unsafe { &App(data) }
400 app.total++
401}
402
403')
404 for i in 0 .. 1100 {
405 if i % 2 == 0 {
406 src.write_string('fn use_${i}(mut app App) {
407 e := EventA{code: ${i}}
408 cfg := ConfigA{cb: erased, user_data: voidptr(app)}
409 cfg.cb(&e, cfg.user_data)
410}
411
412')
413 } else {
414 src.write_string('fn use_${i}(mut app App) {
415 e := EventB{code: ${i}}
416 cfg := ConfigB{cb: erased, user_data: voidptr(app)}
417 cfg.cb(&e, cfg.user_data)
418}
419
420')
421 }
422 }
423 src.write_string('fn main() {
424 mut app := App{}
425')
426 for i in 0 .. 1100 {
427 src.write_string(' use_${i}(mut app)
428')
429 }
430 src.write_string(' println(int_str(app.total))
431}
432')
433 parallel_src := callback_write_source('parallel', src.str())
434 parallel_out := os.join_path(os.temp_dir(), 'v3_callback_userdata_parallel_${os.getpid()}')
435 parallel_compile := os.execute('${v3_bin} ${parallel_src} -b c -o ${parallel_out}')
436 assert parallel_compile.exit_code == 0, parallel_compile.output
437 parallel_run := os.execute(parallel_out)
438 assert parallel_run.exit_code == 0, parallel_run.output
439 assert parallel_run.output.trim_space() == '1100'
440 parallel_c := os.read_file(parallel_out + '.c') or { panic(err) }
441 names := callback_adapter_names(parallel_c, 'erased_callback_adapter_')
442 assert names.len == 2, parallel_c
443 assert names[0] != names[1]
444}
445