v4 / vlib / v3 / tests / c_callback_const_qualifier_codegen_test.v
344 lines · 302 sloc · 9.91 KB · 288feee4702b35beadb4037d02b96b5c8674156b
Raw
1import os
2import strings
3
4const const_cb_vexe = @VEXE
5const const_cb_tests_dir = os.dir(@FILE)
6const const_cb_v3_dir = os.dir(const_cb_tests_dir)
7const const_cb_vlib_dir = os.dir(const_cb_v3_dir)
8const const_cb_v3_src = os.join_path(const_cb_v3_dir, 'v3.v')
9
10fn const_cb_build_v3() string {
11 return const_cb_build_v3_with_flags('serial', '')
12}
13
14fn const_cb_build_v3_parallel() string {
15 return const_cb_build_v3_with_flags('parallel', '-d parallel')
16}
17
18fn const_cb_build_v3_with_flags(name string, flags string) string {
19 v3_bin := os.join_path(os.temp_dir(), 'v3_const_callback_test_${name}_${os.getpid()}')
20 os.rm(v3_bin) or {}
21 build :=
22 os.execute('${const_cb_vexe} ${flags} -gc none -path "${const_cb_vlib_dir}|@vlib|@vmodules" -o ${v3_bin} ${const_cb_v3_src}')
23 assert build.exit_code == 0, build.output
24 return v3_bin
25}
26
27fn const_cb_write_project(source string) string {
28 root := os.join_path(os.temp_dir(), 'v3_const_callback_${os.getpid()}')
29 os.rmdir_all(root) or {}
30 os.mkdir_all(root) or { panic(err) }
31 header := '#ifndef V3_CONST_CALLBACK_NATIVE_H
32#define V3_CONST_CALLBACK_NATIVE_H
33typedef struct native_event {
34 int value;
35} native_event;
36typedef struct native_desc {
37 void (*cb)(const struct native_event*, void*);
38 void (*plain)(struct native_event*, void*);
39 void* user_data;
40} native_desc;
41typedef struct alias_desc {
42 void (*cb)(const struct native_event*, void*);
43 void* user_data;
44} alias_desc;
45typedef struct late_alias_desc {
46 void (*cb)(const struct native_event*, void*);
47 void* user_data;
48} late_alias_desc;
49#endif
50'
51 os.write_file(os.join_path(root, 'native_callback.h'), header) or { panic(err) }
52 path := os.join_path(root, 'main.v')
53 os.write_file(path, source) or { panic(err) }
54 return path
55}
56
57fn test_c_callback_const_qualifier_codegen() {
58 v3_bin := const_cb_build_v3()
59 src := const_cb_write_project('module main
60
61#include "@DIR/native_callback.h"
62
63@[typedef]
64struct C.native_event {
65 value int
66}
67
68type Event = C.native_event
69type ConstNativeCb = fn (const_event &Event, user_data voidptr)
70
71@[typedef]
72struct C.native_desc {
73mut:
74 cb fn (const_event &C.native_event, voidptr) = unsafe { nil }
75 plain fn (&C.native_event, voidptr) = unsafe { nil }
76 user_data voidptr
77}
78
79@[typedef]
80struct C.alias_desc {
81mut:
82 cb ConstNativeCb = unsafe { nil }
83 user_data voidptr
84}
85
86struct App {
87mut:
88 hits int
89}
90
91fn erased_event(e voidptr, data voidptr) {
92 event := unsafe { &C.native_event(e) }
93 mut app := unsafe { &App(data) }
94 app.hits += event.value
95}
96
97fn plain_event(e &C.native_event, data voidptr) {
98 mut app := unsafe { &App(data) }
99 app.hits += e.value * 10
100}
101
102fn concrete_event(e &C.native_event, data voidptr) {
103 mut app := unsafe { &App(data) }
104 app.hits += e.value * 100
105}
106
107fn main() {
108 mut app := App{}
109 mut desc := C.native_desc{
110 cb: erased_event
111 plain: plain_event
112 user_data: voidptr(&app)
113 }
114 event := C.native_event{value: 3}
115 desc.cb = erased_event
116 desc.cb(&event, desc.user_data)
117 desc.plain(&event, desc.user_data)
118 mut concrete := C.native_desc{
119 user_data: voidptr(&app)
120 }
121 concrete.cb = concrete_event
122 concrete.cb(&event, concrete.user_data)
123 mut alias_desc := C.alias_desc{
124 user_data: voidptr(&app)
125 }
126 alias_desc.cb = concrete_event
127 alias_desc.cb(&event, alias_desc.user_data)
128 println(int_str(app.hits))
129}
130')
131 out := os.join_path(os.temp_dir(), 'v3_const_callback_out_${os.getpid()}')
132 compile := os.execute('${v3_bin} ${src} -b c -o ${out}')
133 assert compile.exit_code == 0, compile.output
134 run := os.execute(out)
135 assert run.exit_code == 0, run.output
136 assert run.output.trim_space() == '633'
137 generated := os.read_file(out + '.c') or { panic(err) }
138 assert generated.contains('typedef void (*'), generated
139 assert generated.contains('(const struct native_event*, void*)'), generated
140 assert generated.contains('(struct native_event*, void*)'), generated
141 assert generated.contains('erased_event_callback_adapter_'), generated
142 assert generated.contains('concrete_event_callback_adapter_'), generated
143 assert generated.contains('const struct native_event* arg0, void* arg1'), generated
144 assert generated.contains('erased_event((void*)arg0, arg1);'), generated
145 assert generated.contains('concrete_event((struct native_event*)arg0, arg1);'), generated
146 assert generated.contains('.cb = erased_event_callback_adapter_'), generated
147 assert generated.contains('desc.cb = erased_event_callback_adapter_'), generated
148 assert generated.contains('concrete.cb = concrete_event_callback_adapter_'), generated
149 assert generated.contains('alias_desc.cb = concrete_event_callback_adapter_'), generated
150 assert generated.contains('.plain = plain_event'), generated
151 assert !generated.contains('.cb = (_fn_ptr'), generated
152 assert !generated.contains('desc.cb = (_fn_ptr'), generated
153 assert !generated.contains('concrete.cb = concrete_event;'), generated
154 assert !generated.contains('alias_desc.cb = concrete_event;'), generated
155 assert !generated.contains('.plain = plain_event_callback_adapter_'), generated
156}
157
158fn test_generic_heap_positional_c_callback_codegen() {
159 v3_bin := const_cb_build_v3()
160 src := const_cb_write_project('module main
161
162#include "@DIR/native_callback.h"
163
164@[typedef]
165struct C.native_event {
166 value int
167}
168
169@[typedef]
170struct C.native_desc {
171mut:
172 cb fn (const_event &C.native_event, voidptr) = unsafe { nil }
173 plain fn (&C.native_event, voidptr) = unsafe { nil }
174 user_data voidptr
175}
176
177struct App {
178mut:
179 hits int
180}
181
182fn concrete_event(e &C.native_event, data voidptr) {
183 mut app := unsafe { &App(data) }
184 app.hits += e.value
185}
186
187fn plain_event(e &C.native_event, data voidptr) {
188 mut app := unsafe { &App(data) }
189 app.hits += e.value * 10
190}
191
192fn make_desc[T](data T) &C.native_desc {
193 return &C.native_desc{concrete_event, plain_event, voidptr(data)}
194}
195
196fn main() {
197 mut app := App{}
198 event := C.native_event{value: 5}
199 desc := make_desc(&app)
200 desc.cb(&event, desc.user_data)
201 desc.plain(&event, desc.user_data)
202 println(int_str(app.hits))
203}
204')
205 out := os.join_path(os.temp_dir(), 'v3_const_callback_heap_positional_out_${os.getpid()}')
206 compile := os.execute('${v3_bin} ${src} -b c -o ${out}')
207 assert compile.exit_code == 0, compile.output
208 run := os.execute(out)
209 assert run.exit_code == 0, run.output
210 assert run.output.trim_space() == '55'
211 generated := os.read_file(out + '.c') or { panic(err) }
212 assert generated.contains('concrete_event_callback_adapter_'), generated
213 assert generated.contains('.cb = concrete_event_callback_adapter_'), generated
214 assert !generated.contains('.cb = concrete_event,'), generated
215}
216
217fn test_c_callback_c_alias_declared_after_field_codegen() {
218 v3_bin := const_cb_build_v3()
219 src := const_cb_write_project('module main
220
221#include "@DIR/native_callback.h"
222
223@[typedef]
224struct C.late_alias_desc {
225mut:
226 cb fn (&Event, voidptr) = unsafe { nil }
227 user_data voidptr
228}
229
230@[typedef]
231struct C.native_event {
232 value int
233}
234
235type Event = C.native_event
236
237struct App {
238mut:
239 hits int
240}
241
242fn late_alias_event(e &C.native_event, data voidptr) {
243 mut app := unsafe { &App(data) }
244 app.hits += e.value
245}
246
247fn main() {
248 mut app := App{}
249 event := C.native_event{value: 11}
250 mut desc := C.late_alias_desc{
251 cb: late_alias_event
252 user_data: voidptr(&app)
253 }
254 desc.cb(&event, desc.user_data)
255 desc.cb = late_alias_event
256 desc.cb(&event, desc.user_data)
257 println(int_str(app.hits))
258}
259')
260 out := os.join_path(os.temp_dir(), 'v3_const_callback_late_alias_out_${os.getpid()}')
261 compile := os.execute('${v3_bin} ${src} -b c -o ${out}')
262 assert compile.exit_code == 0, compile.output
263 run := os.execute(out)
264 assert run.exit_code == 0, run.output
265 assert run.output.trim_space() == '22'
266 generated := os.read_file(out + '.c') or { panic(err) }
267 assert generated.contains('(const struct native_event*, void*)'), generated
268 assert generated.contains('late_alias_event_callback_adapter_'), generated
269 assert generated.contains('const struct native_event* arg0, void* arg1'), generated
270 assert generated.contains('late_alias_event((struct native_event*)arg0, arg1);'), generated
271 assert generated.contains('.cb = late_alias_event_callback_adapter_'), generated
272 assert generated.contains('desc.cb = late_alias_event_callback_adapter_'), generated
273 assert !generated.contains('main__Event*'), generated
274 assert !generated.contains('.cb = late_alias_event,'), generated
275 assert !generated.contains('desc.cb = late_alias_event;'), generated
276}
277
278fn test_c_callback_const_qualifier_parallel_worker_codegen() {
279 v3_bin := const_cb_build_v3_parallel()
280 mut src := strings.new_builder(96 * 1024)
281 src.write_string('module main
282
283#include "@DIR/native_callback.h"
284
285@[typedef]
286struct C.native_event {
287 value int
288}
289
290type Event = C.native_event
291type ConstNativeCb = fn (const_event &Event, user_data voidptr)
292
293@[typedef]
294struct C.alias_desc {
295mut:
296 cb ConstNativeCb = unsafe { nil }
297 user_data voidptr
298}
299
300struct App {
301mut:
302 hits int
303}
304
305fn concrete_event(e &C.native_event, data voidptr) {
306 mut app := unsafe { &App(data) }
307 app.hits += e.value
308}
309
310')
311 for i in 0 .. 1100 {
312 src.write_string('fn filler_${i}() int { return ${i} }\n')
313 }
314 src.write_string('
315fn run_parallel(mut app App) {
316 mut desc := C.alias_desc{
317 user_data: voidptr(app)
318 }
319 desc.cb = concrete_event
320 event := C.native_event{value: 7}
321 desc.cb(&event, desc.user_data)
322}
323
324fn main() {
325 mut app := App{}
326 run_parallel(mut app)
327 println(int_str(app.hits))
328}
329')
330 path := const_cb_write_project(src.str())
331 out := os.join_path(os.temp_dir(), 'v3_const_callback_parallel_out_${os.getpid()}')
332 compile := os.execute('VJOBS=2 ${v3_bin} ${path} -b c -o ${out}')
333 assert compile.exit_code == 0, compile.output
334 run := os.execute(out)
335 assert run.exit_code == 0, run.output
336 assert run.output.trim_space() == '7'
337 generated := os.read_file(out + '.c') or { panic(err) }
338 assert generated.contains('concrete_event_callback_adapter_'), generated
339 assert generated.contains('const struct native_event* arg0, void* arg1'), generated
340 assert generated.contains('concrete_event((struct native_event*)arg0, arg1);'), generated
341 assert generated.contains('desc.cb = concrete_event_callback_adapter_'), generated
342 assert !generated.contains('const gg__Event'), generated
343 assert !generated.contains('desc.cb = concrete_event;'), generated
344}
345