v / vlib / v2 / builder / module_storage_cache_test.v
403 lines · 359 sloc · 12.85 KB · 20eada7233525573d6471d2cc70fa501213e430a
Raw
1module builder
2
3import os
4import v2.ast
5import v2.gen.v as gen_v
6import v2.parser
7import v2.pref
8import v2.token
9
10fn module_storage_cache_tmp_dir(label string) string {
11 return os.join_path(os.temp_dir(), 'v2_module_storage_cache_${label}_${os.getpid()}')
12}
13
14fn module_storage_cached_header_source_for_test() string {
15 tmp_dir := module_storage_cache_tmp_dir('source')
16 os.rmdir_all(tmp_dir) or {}
17 os.mkdir_all(os.join_path(tmp_dir, 'state')) or { panic(err) }
18 defer {
19 os.rmdir_all(tmp_dir) or {}
20 }
21 source_file := os.join_path(tmp_dir, 'state', 'state.v')
22 os.write_file(source_file, 'module state
23
24pub __global mut total = 0
25__global mut hidden = 0
26') or {
27 panic(err)
28 }
29 mut prefs := pref.new_preferences()
30 mut b := new_builder(&prefs)
31 source_files := b.parse_source_files_for_headers([source_file])
32 header_ast := b.build_module_header_ast(source_files, 'state') or {
33 panic('missing state header')
34 }
35 mut gen := gen_v.new_gen(b.pref)
36 gen.gen(header_ast)
37 return sanitize_header_source(gen.output_string(), map[string]string{})
38}
39
40fn module_mut_cached_header_source_for_test() string {
41 tmp_dir := module_storage_cache_tmp_dir('module_mut_source')
42 os.rmdir_all(tmp_dir) or {}
43 os.mkdir_all(os.join_path(tmp_dir, 'state')) or { panic(err) }
44 defer {
45 os.rmdir_all(tmp_dir) or {}
46 }
47 source_file := os.join_path(tmp_dir, 'state', 'state.v')
48 os.write_file(source_file, 'module state
49
50pub struct Counter {
51pub module_mut:
52 value int
53pub mut:
54 open int
55}
56') or {
57 panic(err)
58 }
59 mut prefs := pref.new_preferences()
60 mut b := new_builder(&prefs)
61 source_files := b.parse_source_files_for_headers([source_file])
62 header_ast := b.build_module_header_ast(source_files, 'state') or {
63 panic('missing state header')
64 }
65 mut gen := gen_v.new_gen(b.pref)
66 gen.gen(header_ast)
67 return sanitize_header_source(gen.output_string(), map[string]string{})
68}
69
70fn module_mut_interface_cached_header_source_for_test() string {
71 return module_mut_interface_cached_header_source_for_test_with_source_decls(false)
72}
73
74fn module_mut_interface_merged_cached_header_source_for_test() string {
75 return module_mut_interface_cached_header_source_for_test_with_source_decls(true)
76}
77
78fn module_mut_interface_cached_header_source_for_test_with_source_decls(include_source_decls bool) string {
79 tmp_dir := module_storage_cache_tmp_dir('module_mut_interface_source')
80 os.rmdir_all(tmp_dir) or {}
81 os.mkdir_all(os.join_path(tmp_dir, 'state')) or { panic(err) }
82 defer {
83 os.rmdir_all(tmp_dir) or {}
84 }
85 source_file := os.join_path(tmp_dir, 'state', 'state.v')
86 os.write_file(source_file, 'module state
87
88pub type Handler = fn (int) bool
89
90pub interface Mutator {
91 handler Handler
92 direct fn (int) bool
93 handle(int) bool
94mut:
95 bump()
96 reset()
97}
98
99pub struct Counter {
100pub module_mut:
101 iface Mutator
102}
103') or {
104 panic(err)
105 }
106 mut prefs := pref.new_preferences()
107 mut b := new_builder(&prefs)
108 source_files := b.parse_source_files_for_headers([source_file])
109 header_ast := b.build_module_header_ast(source_files, 'state') or {
110 panic('missing state header')
111 }
112 mut gen := gen_v.new_gen(b.pref)
113 gen.gen(header_ast)
114 mut header_source := sanitize_header_source(gen.output_string(), map[string]string{})
115 if include_source_decls {
116 source_fn_decls := b.source_fn_decls_for_files([source_file])
117 header_source = merge_missing_source_fn_decls(header_source, source_fn_decls)
118 }
119 return header_source
120}
121
122fn module_storage_cached_header_global(header_source string, name string) ast.FieldDecl {
123 tmp_dir := module_storage_cache_tmp_dir('parse')
124 os.rmdir_all(tmp_dir) or {}
125 os.mkdir_all(tmp_dir) or { panic(err) }
126 defer {
127 os.rmdir_all(tmp_dir) or {}
128 }
129 header_path := os.join_path(tmp_dir, 'state.vh')
130 os.write_file(header_path, header_source) or { panic(err) }
131 mut prefs := pref.new_preferences()
132 mut file_set := token.FileSet.new()
133 mut par := parser.Parser.new(&prefs)
134 files := par.parse_files([header_path], mut file_set)
135 for stmt in files[0].stmts {
136 if stmt is ast.GlobalDecl {
137 for field in stmt.fields {
138 if field.name == name {
139 return field
140 }
141 }
142 }
143 }
144 panic('missing cached header field ${name}')
145}
146
147fn module_storage_cached_header_struct(header_source string, name string) ast.StructDecl {
148 tmp_dir := module_storage_cache_tmp_dir('parse_struct')
149 os.rmdir_all(tmp_dir) or {}
150 os.mkdir_all(tmp_dir) or { panic(err) }
151 defer {
152 os.rmdir_all(tmp_dir) or {}
153 }
154 header_path := os.join_path(tmp_dir, 'state.vh')
155 os.write_file(header_path, header_source) or { panic(err) }
156 mut prefs := pref.new_preferences()
157 mut file_set := token.FileSet.new()
158 mut par := parser.Parser.new(&prefs)
159 files := par.parse_files([header_path], mut file_set)
160 for stmt in files[0].stmts {
161 if stmt is ast.StructDecl {
162 if stmt.name == name {
163 return stmt
164 }
165 }
166 }
167 panic('missing cached header struct ${name}')
168}
169
170fn module_storage_cached_header_interface(header_source string, name string) ast.InterfaceDecl {
171 tmp_dir := module_storage_cache_tmp_dir('parse_interface')
172 os.rmdir_all(tmp_dir) or {}
173 os.mkdir_all(tmp_dir) or { panic(err) }
174 defer {
175 os.rmdir_all(tmp_dir) or {}
176 }
177 header_path := os.join_path(tmp_dir, 'state.vh')
178 os.write_file(header_path, header_source) or { panic(err) }
179 mut prefs := pref.new_preferences()
180 mut file_set := token.FileSet.new()
181 mut par := parser.Parser.new(&prefs)
182 files := par.parse_files([header_path], mut file_set)
183 for stmt in files[0].stmts {
184 if stmt is ast.InterfaceDecl {
185 if stmt.name == name {
186 return stmt
187 }
188 }
189 }
190 panic('missing cached header interface ${name}')
191}
192
193fn module_storage_run_cached_header_check(label string, header_source string, main_source string) (int, string) {
194 tmp_dir := module_storage_cache_tmp_dir(label)
195 os.rmdir_all(tmp_dir) or {}
196 os.mkdir_all(os.join_path(tmp_dir, 'state')) or { panic(err) }
197 defer {
198 os.rmdir_all(tmp_dir) or {}
199 }
200 os.write_file(os.join_path(tmp_dir, 'state', 'state.v'), header_source) or { panic(err) }
201 main_path := os.join_path(tmp_dir, 'main.v')
202 os.write_file(main_path, main_source) or { panic(err) }
203 out_path := os.join_path(tmp_dir, 'out.txt')
204 cmd := '${os.quoted_path(@VEXE)} -v2 -backend v -o ${os.quoted_path(out_path)} ${os.quoted_path(main_path)} 2>&1'
205 res := os.execute(cmd)
206 return res.exit_code, res.output
207}
208
209fn test_module_storage_cached_header_preserves_flags() {
210 header_source := module_storage_cached_header_source_for_test()
211 assert header_source.contains('pub __global (')
212 assert header_source.contains('mut total int')
213 assert header_source.contains('__global (')
214 assert header_source.contains('mut hidden int')
215 public_field := module_storage_cached_header_global(header_source, 'total')
216 assert public_field.is_public
217 assert public_field.is_mut
218 private_field := module_storage_cached_header_global(header_source, 'hidden')
219 assert !private_field.is_public
220 assert private_field.is_mut
221}
222
223fn test_module_storage_cached_header_preserves_module_mut_struct_fields() {
224 header_source := module_mut_cached_header_source_for_test()
225 assert header_source.contains('pub module_mut:')
226 assert !header_source.contains('@[module_mut]')
227 counter := module_storage_cached_header_struct(header_source, 'Counter')
228 assert counter.fields[0].name == 'value'
229 assert counter.fields[0].is_public
230 assert counter.fields[0].is_mut
231 assert counter.fields[0].is_module_mut
232 assert counter.fields[1].name == 'open'
233 assert counter.fields[1].is_public
234 assert counter.fields[1].is_mut
235 assert !counter.fields[1].is_module_mut
236}
237
238fn test_module_storage_cached_header_preserves_mut_interface_methods_for_module_mut_field() {
239 header_source := module_mut_interface_cached_header_source_for_test()
240 assert header_source.contains('pub interface Mutator {')
241 assert header_source.contains('handler Handler')
242 assert header_source.contains('direct fn(int) bool')
243 assert header_source.contains('handle(int) bool')
244 assert header_source.contains('mut:')
245 assert header_source.contains('bump()')
246 assert !header_source.contains('direct(int) bool')
247 assert !header_source.contains('bump fn(')
248 mutator := module_storage_cached_header_interface(header_source, 'Mutator')
249 assert mutator.fields[0].name == 'handler'
250 assert !mutator.fields[0].is_interface_method
251 assert mutator.fields[1].name == 'direct'
252 assert !mutator.fields[1].is_interface_method
253 assert mutator.fields[2].name == 'handle'
254 assert mutator.fields[2].is_interface_method
255 assert mutator.fields[3].name == 'bump'
256 assert mutator.fields[3].is_mut
257 assert mutator.fields[3].is_interface_method
258 assert mutator.fields[4].name == 'reset'
259 assert mutator.fields[4].is_mut
260 assert mutator.fields[4].is_interface_method
261
262 module_mut_code, module_mut_output := module_storage_run_cached_header_check('module_mut_interface_method',
263 header_source, 'module main
264
265import state
266
267fn main() {
268 mut c := state.Counter{}
269 c.iface.bump()
270}
271')
272 assert module_mut_code != 0, 'cached module_mut interface method mutation should fail'
273 assert module_mut_output.contains('cannot mutate module-mutable field `state.Counter.iface` outside module `state`'), module_mut_output
274}
275
276fn test_module_storage_cached_header_does_not_synthesize_interface_fn_field_decls() {
277 header_source := module_mut_interface_merged_cached_header_source_for_test()
278 assert header_source.contains('pub interface Mutator {')
279 assert header_source.contains('direct fn(int) bool')
280 assert header_source.contains('handle(int) bool')
281 assert header_source.contains('pub fn (it Mutator) handle(int) bool')
282 assert header_source.contains('pub fn (mut it Mutator) bump()')
283 assert header_source.contains('pub fn (mut it Mutator) reset()')
284 assert !header_source.contains('pub fn (it Mutator) direct fn')
285 assert !header_source.contains('fn (it Mutator) direct fn')
286 assert !header_source.contains('pub fn (it Mutator) bump()')
287
288 module_mut_code, module_mut_output := module_storage_run_cached_header_check('module_mut_interface_merged_method',
289 header_source, 'module main
290
291import state
292
293fn main() {
294 mut c := state.Counter{}
295 c.iface.bump()
296}
297')
298 assert module_mut_code != 0, 'merged cached module_mut interface method mutation should fail'
299 assert module_mut_output.contains('cannot mutate module-mutable field `state.Counter.iface` outside module `state`'), module_mut_output
300}
301
302fn test_module_storage_cached_header_visibility_matches_source_module() {
303 header_source := module_storage_cached_header_source_for_test()
304 public_code, public_output := module_storage_run_cached_header_check('public', header_source, 'module main
305
306import state
307
308fn main() {
309 state.total += 1
310}
311')
312 assert public_code == 0, public_output
313
314 private_code, private_output := module_storage_run_cached_header_check('private',
315 header_source, 'module main
316
317import state
318
319fn main() {
320 state.hidden += 1
321}
322')
323 assert private_code != 0, 'cached private module storage should fail'
324 assert private_output.contains('module global `state.hidden` is private'), private_output
325}
326
327fn test_module_storage_cached_header_module_mut_visibility_matches_source_module() {
328 header_source := module_mut_cached_header_source_for_test()
329 public_code, public_output := module_storage_run_cached_header_check('module_mut_public',
330 header_source, 'module main
331
332import state
333
334fn main() {
335 mut c := state.Counter{}
336 println(c.value)
337 c.open++
338}
339')
340 assert public_code == 0, public_output
341
342 module_mut_code, module_mut_output := module_storage_run_cached_header_check('module_mut_private',
343 header_source, 'module main
344
345import state
346
347fn main() {
348 mut c := state.Counter{}
349 c.value++
350}
351')
352 assert module_mut_code != 0, 'cached module_mut field mutation should fail'
353 assert module_mut_output.contains('cannot mutate module-mutable field `state.Counter.value` outside module `state`'), module_mut_output
354}
355
356fn test_module_storage_c_extern_import_cache_uses_raw_symbol() {
357 tmp_dir := module_storage_cache_tmp_dir('c_extern_import')
358 os.rmdir_all(tmp_dir) or {}
359 os.mkdir_all(os.join_path(tmp_dir, 'ext')) or { panic(err) }
360 defer {
361 os.rmdir_all(tmp_dir) or {}
362 }
363 mut prefs := pref.new_preferences()
364 mut cache_builder := new_builder(&prefs)
365 cache_dir := cache_builder.core_cache_dir()
366 os.rmdir_all(cache_dir) or {}
367 c_path := os.join_path(tmp_dir, 'raw_status.c')
368 os.write_file(c_path, 'int raw_status = 7;\n') or { panic(err) }
369 os.write_file(os.join_path(tmp_dir, 'ext', 'ext.v'), 'module ext
370
371@[c_extern]
372pub __global raw_status int
373
374pub fn read_status() int {
375 return raw_status
376}
377') or {
378 panic(err)
379 }
380 main_path := os.join_path(tmp_dir, 'main.v')
381 os.write_file(main_path, 'module main
382
383import ext
384
385#flag ${c_path}
386
387fn main() {
388 _ = ext.raw_status
389 _ = ext.read_status()
390}
391') or {
392 panic(err)
393 }
394 out_path := os.join_path(tmp_dir, 'out')
395 cmd := '${os.quoted_path(@VEXE)} -v -v2 -keepc -o ${os.quoted_path(out_path)} ${os.quoted_path(main_path)} 2>&1'
396 res := os.execute(cmd)
397 assert res.exit_code == 0, res.output
398 ext_header := os.read_file(cache_builder.core_header_path('ext')) or { panic(err) }
399 assert ext_header.contains('@[c_extern]'), ext_header
400 imports_c := os.read_file(os.join_path(cache_dir, 'imports.c')) or { panic(err) }
401 assert imports_c.contains('return raw_status;'), imports_c
402 assert !imports_c.contains('ext__raw_status'), imports_c
403}
404