| 1 | module cleanc |
| 2 | |
| 3 | import os |
| 4 | import v2.ast |
| 5 | import v2.parser |
| 6 | import v2.pref as vpref |
| 7 | import v2.token |
| 8 | import v2.transformer |
| 9 | import v2.types |
| 10 | |
| 11 | fn new_target_test_gen(target_os string, user_defines []string) &Gen { |
| 12 | return new_target_test_gen_with_freestanding(target_os, user_defines, false) |
| 13 | } |
| 14 | |
| 15 | fn new_target_test_gen_with_freestanding(target_os string, user_defines []string, freestanding bool) &Gen { |
| 16 | return new_target_test_gen_with_options(target_os, user_defines, freestanding, false) |
| 17 | } |
| 18 | |
| 19 | fn new_target_test_gen_with_options(target_os string, user_defines []string, freestanding bool, prealloc bool) &Gen { |
| 20 | return new_target_test_gen_with_options_and_hooks(target_os, user_defines, freestanding, |
| 21 | prealloc, []) |
| 22 | } |
| 23 | |
| 24 | fn new_target_test_gen_with_options_and_hooks(target_os string, user_defines []string, freestanding bool, prealloc bool, freestanding_hooks []string) &Gen { |
| 25 | return new_target_test_gen_with_options_hooks_and_skip_builtin(target_os, user_defines, |
| 26 | freestanding, prealloc, freestanding_hooks, false) |
| 27 | } |
| 28 | |
| 29 | fn new_target_test_gen_with_options_hooks_and_skip_builtin(target_os string, user_defines []string, freestanding bool, prealloc bool, freestanding_hooks []string, skip_builtin bool) &Gen { |
| 30 | prefs := &vpref.Preferences{ |
| 31 | backend: .cleanc |
| 32 | target_os: target_os |
| 33 | freestanding: freestanding |
| 34 | prealloc: prealloc |
| 35 | skip_builtin: skip_builtin |
| 36 | user_defines: user_defines |
| 37 | explicit_user_defines: user_defines.clone() |
| 38 | freestanding_hooks: freestanding_hooks |
| 39 | no_parallel: true |
| 40 | } |
| 41 | env := types.Environment.new() |
| 42 | return Gen.new_with_env_and_pref([]ast.File{}, env, prefs) |
| 43 | } |
| 44 | |
| 45 | fn freestanding_hook_defines(hooks []string) []string { |
| 46 | mut defines := ['freestanding', 'freestanding_hooks'] |
| 47 | for hook in hooks { |
| 48 | defines << 'freestanding_${hook}' |
| 49 | defines << 'freestanding_hooks_${hook}' |
| 50 | } |
| 51 | return defines |
| 52 | } |
| 53 | |
| 54 | fn c_directive_output_for_target(cond string, target_os string, user_defines []string) string { |
| 55 | return c_directive_output_for_target_with_explicit(cond, target_os, user_defines, |
| 56 | user_defines.clone(), false) |
| 57 | } |
| 58 | |
| 59 | fn c_directive_output_for_target_with_explicit(cond string, target_os string, user_defines []string, explicit_user_defines []string, freestanding bool) string { |
| 60 | prefs := &vpref.Preferences{ |
| 61 | backend: .cleanc |
| 62 | target_os: target_os |
| 63 | freestanding: freestanding |
| 64 | user_defines: user_defines |
| 65 | explicit_user_defines: explicit_user_defines |
| 66 | no_parallel: true |
| 67 | } |
| 68 | env := types.Environment.new() |
| 69 | mut g := Gen.new_with_env_and_pref([]ast.File{}, env, prefs) |
| 70 | mut seen := map[string]bool{} |
| 71 | g.emit_directive(ast.Directive{ |
| 72 | name: 'include' |
| 73 | value: '<target_marker.h>' |
| 74 | ct_cond: cond |
| 75 | }, 'target_test.v', true, mut seen) |
| 76 | return g.sb.str() |
| 77 | } |
| 78 | |
| 79 | fn c_directive_output_for_freestanding_target(cond string, target_os string) string { |
| 80 | return c_directive_output_for_target_with_explicit(cond, target_os, [ |
| 81 | 'freestanding', |
| 82 | ], [], true) |
| 83 | } |
| 84 | |
| 85 | fn preamble_for_target(target_os string, user_defines []string) string { |
| 86 | mut g := new_target_test_gen(target_os, user_defines) |
| 87 | g.set_emit_modules(['main']) |
| 88 | g.write_preamble() |
| 89 | return g.sb.str() |
| 90 | } |
| 91 | |
| 92 | fn preamble_for_freestanding_field(target_os string) string { |
| 93 | mut g := new_target_test_gen_with_freestanding(target_os, [], true) |
| 94 | g.set_emit_modules(['main']) |
| 95 | g.write_preamble() |
| 96 | return g.sb.str() |
| 97 | } |
| 98 | |
| 99 | fn full_preamble_for_target(target_os string, user_defines []string) string { |
| 100 | mut g := new_target_test_gen(target_os, user_defines) |
| 101 | g.write_preamble() |
| 102 | return g.sb.str() |
| 103 | } |
| 104 | |
| 105 | fn full_preamble_for_freestanding_field(target_os string) string { |
| 106 | mut g := new_target_test_gen_with_freestanding(target_os, [], true) |
| 107 | g.write_preamble() |
| 108 | return g.sb.str() |
| 109 | } |
| 110 | |
| 111 | fn full_preamble_for_options(target_os string, user_defines []string, freestanding bool, prealloc bool) string { |
| 112 | mut g := new_target_test_gen_with_options(target_os, user_defines, freestanding, prealloc) |
| 113 | g.write_preamble() |
| 114 | return g.sb.str() |
| 115 | } |
| 116 | |
| 117 | fn full_preamble_for_freestanding_hooks(hooks []string) string { |
| 118 | mut g := new_target_test_gen_with_options_and_hooks('linux', freestanding_hook_defines(hooks), |
| 119 | true, false, hooks) |
| 120 | g.write_preamble() |
| 121 | return g.sb.str() |
| 122 | } |
| 123 | |
| 124 | fn runtime_fallbacks_for_called_functions(names []string) string { |
| 125 | mut g := new_target_test_gen('linux', []) |
| 126 | g.add_called_fn_names(names) |
| 127 | g.emit_missing_runtime_fallbacks() |
| 128 | return g.sb.str() |
| 129 | } |
| 130 | |
| 131 | fn runtime_fallbacks_for_called_functions_for_target(target_os string, names []string) string { |
| 132 | mut g := new_target_test_gen(target_os, []) |
| 133 | g.add_called_fn_names(names) |
| 134 | g.emit_missing_runtime_fallbacks() |
| 135 | return g.sb.str() |
| 136 | } |
| 137 | |
| 138 | fn runtime_fallbacks_for_called_functions_with_options(names []string, user_defines []string, freestanding bool) string { |
| 139 | mut g := new_target_test_gen_with_freestanding('linux', user_defines, freestanding) |
| 140 | g.add_called_fn_names(names) |
| 141 | g.emit_missing_runtime_fallbacks() |
| 142 | return g.sb.str() |
| 143 | } |
| 144 | |
| 145 | fn runtime_fallbacks_for_called_functions_with_hooks(names []string, hooks []string) string { |
| 146 | mut g := new_target_test_gen_with_options_and_hooks('linux', freestanding_hook_defines(hooks), |
| 147 | true, false, hooks) |
| 148 | g.add_called_fn_names(names) |
| 149 | g.emit_missing_runtime_fallbacks() |
| 150 | return g.sb.str() |
| 151 | } |
| 152 | |
| 153 | fn runtime_fallbacks_for_existing_c_source(csrc string) string { |
| 154 | mut g := new_target_test_gen('linux', []) |
| 155 | g.sb.write_string(csrc) |
| 156 | g.emit_missing_runtime_fallbacks() |
| 157 | return g.sb.str() |
| 158 | } |
| 159 | |
| 160 | fn runtime_fallbacks_for_existing_c_source_with_options(csrc string, user_defines []string, freestanding bool) string { |
| 161 | mut g := new_target_test_gen_with_freestanding('linux', user_defines, freestanding) |
| 162 | g.sb.write_string(csrc) |
| 163 | g.emit_missing_runtime_fallbacks() |
| 164 | return g.sb.str() |
| 165 | } |
| 166 | |
| 167 | fn runtime_fallbacks_for_existing_c_source_with_hooks(csrc string, hooks []string) string { |
| 168 | mut g := new_target_test_gen_with_options_and_hooks('linux', freestanding_hook_defines(hooks), |
| 169 | true, false, hooks) |
| 170 | g.sb.write_string(csrc) |
| 171 | g.emit_missing_runtime_fallbacks() |
| 172 | return g.sb.str() |
| 173 | } |
| 174 | |
| 175 | fn runtime_fallbacks_for_existing_c_source_with_hooks_skip_builtin(csrc string, hooks []string) string { |
| 176 | mut g := new_target_test_gen_with_options_hooks_and_skip_builtin('linux', |
| 177 | freestanding_hook_defines(hooks), true, false, hooks, true) |
| 178 | g.sb.write_string(csrc) |
| 179 | g.emit_missing_runtime_fallbacks() |
| 180 | return g.sb.str() |
| 181 | } |
| 182 | |
| 183 | fn soa_companion_for_options(user_defines []string, freestanding bool) string { |
| 184 | mut g := new_target_test_gen_with_freestanding('linux', user_defines, freestanding) |
| 185 | g.gen_soa_companion('Point', types.Struct{ |
| 186 | name: 'Point' |
| 187 | fields: [ |
| 188 | types.Field{ |
| 189 | name: 'x' |
| 190 | typ: types.Type(types.Primitive{ |
| 191 | props: .integer |
| 192 | size: 32 |
| 193 | }) |
| 194 | }, |
| 195 | ] |
| 196 | }) |
| 197 | return g.sb.str() |
| 198 | } |
| 199 | |
| 200 | fn soa_companion_for_hooks(hooks []string) string { |
| 201 | mut g := new_target_test_gen_with_options_and_hooks('linux', freestanding_hook_defines(hooks), |
| 202 | true, false, hooks) |
| 203 | g.gen_soa_companion('Point', types.Struct{ |
| 204 | name: 'Point' |
| 205 | fields: [ |
| 206 | types.Field{ |
| 207 | name: 'x' |
| 208 | typ: types.Type(types.Primitive{ |
| 209 | props: .integer |
| 210 | size: 32 |
| 211 | }) |
| 212 | }, |
| 213 | ] |
| 214 | }) |
| 215 | return g.sb.str() |
| 216 | } |
| 217 | |
| 218 | fn fixed_array_voidptr_str_write_for_options(user_defines []string, freestanding bool) string { |
| 219 | mut g := new_target_test_gen_with_freestanding('linux', user_defines, freestanding) |
| 220 | g.emit_fixed_array_str_write('items', 'voidptr', 2) |
| 221 | return g.sb.str() |
| 222 | } |
| 223 | |
| 224 | fn fixed_array_voidptr_str_write_for_hooks(hooks []string) string { |
| 225 | mut g := new_target_test_gen_with_options_and_hooks('linux', freestanding_hook_defines(hooks), |
| 226 | true, false, hooks) |
| 227 | g.emit_fixed_array_str_write('items', 'voidptr', 2) |
| 228 | return g.sb.str() |
| 229 | } |
| 230 | |
| 231 | fn unresolved_generic_stub_for_options(user_defines []string, freestanding bool) string { |
| 232 | mut g := new_target_test_gen_with_freestanding('linux', user_defines, freestanding) |
| 233 | g.emit_unresolved_generic_stub('missing_generic_T', 'int') |
| 234 | return g.sb.str() |
| 235 | } |
| 236 | |
| 237 | fn unresolved_generic_stub_for_hooks(hooks []string) string { |
| 238 | mut g := new_target_test_gen_with_options_and_hooks('linux', freestanding_hook_defines(hooks), |
| 239 | true, false, hooks) |
| 240 | g.emit_unresolved_generic_stub('missing_generic_T', 'int') |
| 241 | return g.sb.str() |
| 242 | } |
| 243 | |
| 244 | fn generated_c_for_target_program(name string, source string) string { |
| 245 | return generated_c_for_target_program_with_options(name, source, 'linux', false, false) |
| 246 | } |
| 247 | |
| 248 | fn generated_c_for_target_program_with_options(name string, source string, target_os string, freestanding bool, skip_builtin bool) string { |
| 249 | return generated_c_for_target_program_with_defines(name, source, target_os, [], freestanding, |
| 250 | skip_builtin) |
| 251 | } |
| 252 | |
| 253 | fn generated_c_for_target_program_with_defines(name string, source string, target_os string, user_defines []string, freestanding bool, skip_builtin bool) string { |
| 254 | return generated_c_for_target_program_with_defines_and_hooks(name, source, target_os, |
| 255 | user_defines, freestanding, skip_builtin, []) |
| 256 | } |
| 257 | |
| 258 | fn generated_c_for_trace_calls_import_with_defines(name string, user_defines []string) string { |
| 259 | source := 'module main |
| 260 | |
| 261 | import v.trace_calls |
| 262 | |
| 263 | pub struct C.FILE {} |
| 264 | |
| 265 | pub struct C.timespec { |
| 266 | pub mut: |
| 267 | tv_sec i64 |
| 268 | tv_nsec i64 |
| 269 | } |
| 270 | |
| 271 | __global C.stderr &C.FILE |
| 272 | |
| 273 | pub const C.CLOCK_MONOTONIC int |
| 274 | |
| 275 | fn C.fprintf(fstream &C.FILE, const_format &char, opt ...voidptr) i32 |
| 276 | fn C.fflush(fstream &C.FILE) i32 |
| 277 | fn C.clock_gettime(i32, &C.timespec) i32 |
| 278 | fn C.pthread_self() usize |
| 279 | |
| 280 | fn main() { |
| 281 | trace_calls.on_call("probe") |
| 282 | } |
| 283 | ' |
| 284 | trace_calls_file := os.join_path(os.getwd(), 'vlib', 'v', 'trace_calls', 'tracing_calls.c.v') |
| 285 | return generated_c_for_target_program_with_extra_files(name, source, 'linux', user_defines, |
| 286 | false, false, [trace_calls_file]) |
| 287 | } |
| 288 | |
| 289 | fn generated_c_for_target_program_with_extra_files(name string, source string, target_os string, user_defines []string, freestanding bool, skip_builtin bool, extra_files []string) string { |
| 290 | tmp_file := os.join_path(os.temp_dir(), 'v2_cleanc_target_codegen_${name}_${os.getpid()}.v') |
| 291 | os.write_file(tmp_file, source) or { panic(err) } |
| 292 | defer { |
| 293 | os.rm(tmp_file) or {} |
| 294 | } |
| 295 | prefs := &vpref.Preferences{ |
| 296 | backend: .cleanc |
| 297 | target_os: target_os |
| 298 | freestanding: freestanding |
| 299 | skip_builtin: skip_builtin |
| 300 | user_defines: user_defines |
| 301 | explicit_user_defines: user_defines.clone() |
| 302 | no_parallel: true |
| 303 | } |
| 304 | mut paths := [tmp_file] |
| 305 | paths << extra_files |
| 306 | mut file_set := token.FileSet.new() |
| 307 | mut par := parser.Parser.new(prefs) |
| 308 | files := par.parse_files(paths, mut file_set) |
| 309 | env := types.Environment.new() |
| 310 | mut checker := types.Checker.new(prefs, file_set, env) |
| 311 | checker.check_files(files) |
| 312 | mut trans := transformer.Transformer.new_with_pref(env, prefs) |
| 313 | trans.set_file_set(file_set) |
| 314 | transformed_files := trans.transform_files(files) |
| 315 | mut gen := Gen.new_with_env_and_pref(transformed_files, env, prefs) |
| 316 | return gen.gen() |
| 317 | } |
| 318 | |
| 319 | fn generated_c_for_target_program_with_hooks(name string, source string, hooks []string) string { |
| 320 | return generated_c_for_target_program_with_defines_and_hooks(name, source, 'linux', |
| 321 | freestanding_hook_defines(hooks), true, true, hooks) |
| 322 | } |
| 323 | |
| 324 | fn generated_c_for_target_program_with_defines_and_hooks(name string, source string, target_os string, user_defines []string, freestanding bool, skip_builtin bool, freestanding_hooks []string) string { |
| 325 | tmp_file := os.join_path(os.temp_dir(), 'v2_cleanc_target_codegen_${name}_${os.getpid()}.v') |
| 326 | os.write_file(tmp_file, source) or { panic(err) } |
| 327 | defer { |
| 328 | os.rm(tmp_file) or {} |
| 329 | } |
| 330 | prefs := &vpref.Preferences{ |
| 331 | backend: .cleanc |
| 332 | target_os: target_os |
| 333 | freestanding: freestanding |
| 334 | skip_builtin: skip_builtin |
| 335 | user_defines: user_defines |
| 336 | explicit_user_defines: user_defines.clone() |
| 337 | freestanding_hooks: freestanding_hooks |
| 338 | no_parallel: true |
| 339 | } |
| 340 | mut file_set := token.FileSet.new() |
| 341 | mut par := parser.Parser.new(prefs) |
| 342 | files := par.parse_files([tmp_file], mut file_set) |
| 343 | env := types.Environment.new() |
| 344 | mut checker := types.Checker.new(prefs, file_set, env) |
| 345 | checker.check_files(files) |
| 346 | mut trans := transformer.Transformer.new_with_pref(env, prefs) |
| 347 | trans.set_file_set(file_set) |
| 348 | transformed_files := trans.transform_files(files) |
| 349 | mut gen := Gen.new_with_env_and_pref(transformed_files, env, prefs) |
| 350 | return gen.gen() |
| 351 | } |
| 352 | |
| 353 | fn assert_no_hosted_runtime_fallbacks(csrc string) { |
| 354 | for marker in [ |
| 355 | '__attribute__((weak)) u64 __at_least_one', |
| 356 | '__attribute__((weak)) Array_string arguments()', |
| 357 | '__attribute__((weak)) void _write_buf_to_fd', |
| 358 | '__attribute__((weak)) void _writeln_to_fd', |
| 359 | '__attribute__((weak)) void eprint', |
| 360 | '__attribute__((weak)) void flush_stdout', |
| 361 | '__attribute__((weak)) void flush_stderr', |
| 362 | '__attribute__((weak)) bool Array_int_contains', |
| 363 | '__attribute__((weak)) bool Array_string_contains', |
| 364 | '__attribute__((weak)) int Array_string_index', |
| 365 | '__attribute__((weak)) string Array_int_str', |
| 366 | '__attribute__((weak)) void DenseArray__zeros_to_end', |
| 367 | '__attribute__((weak)) int bits__leading_zeros_64', |
| 368 | '__attribute__((weak)) int bits__trailing_zeros_32', |
| 369 | '__attribute__((weak)) int bits__trailing_zeros_64', |
| 370 | '__attribute__((weak)) u32 bits__rotate_left_32', |
| 371 | '__attribute__((weak)) u8* malloc_noscan', |
| 372 | '__attribute__((weak)) void* memdup', |
| 373 | '__attribute__((weak)) f64 f64_abs', |
| 374 | '__attribute__((weak)) string f64__str', |
| 375 | '__attribute__((weak)) string f64__strg', |
| 376 | '__attribute__((weak)) string f32__str', |
| 377 | '__attribute__((weak)) string f32__strg', |
| 378 | ] { |
| 379 | assert !csrc.contains(marker), marker |
| 380 | } |
| 381 | } |
| 382 | |
| 383 | fn assert_no_hosted_output_runtime(csrc string) { |
| 384 | assert !csrc.contains('isize written = write(fd, ptr, remaining_bytes)') |
| 385 | assert !csrc.contains('fflush(') |
| 386 | assert !csrc.contains('fflush(stdout)') |
| 387 | assert !csrc.contains('fflush(stderr)') |
| 388 | } |
| 389 | |
| 390 | fn assert_no_hosted_alloc_runtime(csrc string) { |
| 391 | assert !csrc.contains('return malloc(n);') |
| 392 | assert !csrc.contains(' = malloc(') |
| 393 | assert !csrc.contains('\tfree(') |
| 394 | assert !csrc.contains('realloc_data(') |
| 395 | assert !csrc.contains(' = realloc(') |
| 396 | } |
| 397 | |
| 398 | fn assert_no_raw_heap_runtime(csrc string) { |
| 399 | for marker in ['*)malloc(', ' = malloc(', '\tmalloc(', 'return malloc(', 'calloc(', '*)realloc(', |
| 400 | ' = realloc(', '\trealloc(', '\tfree(', ' free('] { |
| 401 | assert !csrc.contains(marker), marker |
| 402 | } |
| 403 | } |
| 404 | |
| 405 | fn assert_freestanding_alloc_refusal_without_hosted_heap(csrc string) { |
| 406 | assert csrc.contains('_Static_assert(0, "${freestanding_missing_alloc_hook_message}")') |
| 407 | assert_no_raw_heap_runtime(csrc) |
| 408 | } |
| 409 | |
| 410 | fn assert_freestanding_format_refusal_without_hosted_formatting(csrc string) { |
| 411 | assert csrc.contains('_Static_assert(0, "${freestanding_missing_format_hook_message}")') |
| 412 | assert_no_hosted_formatting_or_abort_runtime(csrc) |
| 413 | } |
| 414 | |
| 415 | fn assert_freestanding_heap_runtime_refusal(csrc string, helper string) { |
| 416 | assert csrc.contains('_Static_assert(0, "${freestanding_missing_heap_runtime_message}: ${helper}")') |
| 417 | } |
| 418 | |
| 419 | fn assert_freestanding_output_refusal_without_hosted_io(csrc string) { |
| 420 | assert csrc.contains('_Static_assert(0, "${freestanding_missing_output_hook_message}")') |
| 421 | assert_no_hosted_output_runtime(csrc) |
| 422 | } |
| 423 | |
| 424 | fn assert_freestanding_panic_refusal_without_hosted_exit(csrc string) { |
| 425 | assert csrc.contains('_Static_assert(0, "${freestanding_missing_panic_hook_message}")') |
| 426 | assert !csrc.contains('v_platform_panic') |
| 427 | assert !csrc.contains('exit(') |
| 428 | } |
| 429 | |
| 430 | fn assert_no_hosted_formatting_or_abort_runtime(csrc string) { |
| 431 | for marker in ['snprintf(', 'sprintf(', 'printf(', 'fprintf(', 'fputs(', 'stderr', 'abort('] { |
| 432 | assert !csrc.contains(marker), marker |
| 433 | } |
| 434 | } |
| 435 | |
| 436 | fn assert_no_os_runtime_headers(csrc string) { |
| 437 | for marker in [ |
| 438 | '#include <stdio.h>', |
| 439 | '#include <stdlib.h>', |
| 440 | '#include <unistd.h>', |
| 441 | '#include <windows.h>', |
| 442 | '#include <dirent.h>', |
| 443 | '#include <pthread.h>', |
| 444 | '#include <mach/mach.h>', |
| 445 | '#include <termios.h>', |
| 446 | '#include <sys/wait.h>', |
| 447 | '#include <sys/ioctl.h>', |
| 448 | 'extern char** environ;', |
| 449 | 'pthread_rwlock_t', |
| 450 | 'SRWLOCK', |
| 451 | ] { |
| 452 | assert !csrc.contains(marker), marker |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | fn assert_c_marker_before(csrc string, before string, after string) { |
| 457 | before_idx := csrc.index(before) or { panic('missing marker: ${before}') } |
| 458 | after_idx := csrc.index(after) or { panic('missing marker: ${after}') } |
| 459 | assert before_idx < after_idx |
| 460 | } |
| 461 | |
| 462 | fn assert_no_gettid_compat(csrc string) { |
| 463 | assert !csrc.contains('_GNU_SOURCE') |
| 464 | assert !csrc.contains('SYS_gettid') |
| 465 | assert !csrc.contains('v_cleanc_gettid') |
| 466 | assert !csrc.contains('#define gettid') |
| 467 | } |
| 468 | |
| 469 | fn test_eval_comptime_flag_uses_target_os_preference() { |
| 470 | windows_gen := new_target_test_gen('windows', []) |
| 471 | assert windows_gen.eval_comptime_flag('windows') |
| 472 | assert !windows_gen.eval_comptime_flag('linux') |
| 473 | assert !windows_gen.eval_comptime_flag('macos') |
| 474 | |
| 475 | linux_gen := new_target_test_gen('linux', []) |
| 476 | assert linux_gen.eval_comptime_flag('linux') |
| 477 | assert !linux_gen.eval_comptime_flag('windows') |
| 478 | |
| 479 | macos_gen := new_target_test_gen('darwin', []) |
| 480 | assert macos_gen.eval_comptime_flag('macos') |
| 481 | assert macos_gen.eval_comptime_flag('darwin') |
| 482 | assert macos_gen.eval_comptime_flag('mac') |
| 483 | assert macos_gen.eval_comptime_flag('bsd') |
| 484 | |
| 485 | freebsd_gen := new_target_test_gen('freebsd', []) |
| 486 | assert freebsd_gen.eval_comptime_flag('bsd') |
| 487 | } |
| 488 | |
| 489 | fn test_eval_comptime_flag_uses_target_os_preference_for_extended_targets() { |
| 490 | for target in [ |
| 491 | 'freebsd', |
| 492 | 'openbsd', |
| 493 | 'netbsd', |
| 494 | 'dragonfly', |
| 495 | 'android', |
| 496 | 'termux', |
| 497 | 'ios', |
| 498 | 'solaris', |
| 499 | 'qnx', |
| 500 | 'serenity', |
| 501 | 'plan9', |
| 502 | 'vinix', |
| 503 | ] { |
| 504 | gen := new_target_test_gen(target, []) |
| 505 | assert gen.eval_comptime_flag(target) |
| 506 | assert !gen.eval_comptime_flag('linux') |
| 507 | } |
| 508 | |
| 509 | cross_gen := new_target_test_gen('cross', []) |
| 510 | assert cross_gen.eval_comptime_flag('cross') |
| 511 | assert !cross_gen.eval_comptime_flag('linux') |
| 512 | |
| 513 | freestanding_gen := new_target_test_gen_with_freestanding('linux', [], true) |
| 514 | assert freestanding_gen.eval_comptime_flag('freestanding') |
| 515 | } |
| 516 | |
| 517 | fn test_freestanding_none_comptime_selects_no_concrete_os() { |
| 518 | gen := new_target_test_gen_with_freestanding('none', [], true) |
| 519 | assert gen.eval_comptime_flag('freestanding') |
| 520 | assert gen.eval_comptime_flag('none') |
| 521 | assert !gen.eval_comptime_flag('linux') |
| 522 | assert !gen.eval_comptime_flag('windows') |
| 523 | assert !gen.eval_comptime_flag('macos') |
| 524 | assert !gen.eval_comptime_flag('darwin') |
| 525 | assert !gen.eval_comptime_flag('cross') |
| 526 | |
| 527 | hosted_with_define := new_target_test_gen('linux', ['none']) |
| 528 | assert !hosted_with_define.eval_comptime_flag('none') |
| 529 | } |
| 530 | |
| 531 | fn test_c_directives_use_target_os_preference() { |
| 532 | assert c_directive_output_for_target('windows', 'windows', []).contains('#include <target_marker.h>') |
| 533 | assert !c_directive_output_for_target('linux', 'windows', []).contains('#include <target_marker.h>') |
| 534 | assert c_directive_output_for_target('linux', 'linux', []).contains('#include <target_marker.h>') |
| 535 | assert !c_directive_output_for_target('windows', 'linux', []).contains('#include <target_marker.h>') |
| 536 | assert c_directive_output_for_target('mac', 'macos', []).contains('#include <target_marker.h>') |
| 537 | assert c_directive_output_for_target('darwin', 'macos', []).contains('#include <target_marker.h>') |
| 538 | assert c_directive_output_for_target('bsd', 'freebsd', []).contains('#include <target_marker.h>') |
| 539 | } |
| 540 | |
| 541 | fn test_c_directives_use_extended_target_os_preference() { |
| 542 | for target in [ |
| 543 | 'freebsd', |
| 544 | 'openbsd', |
| 545 | 'netbsd', |
| 546 | 'dragonfly', |
| 547 | 'android', |
| 548 | 'termux', |
| 549 | 'ios', |
| 550 | 'solaris', |
| 551 | 'qnx', |
| 552 | 'serenity', |
| 553 | 'plan9', |
| 554 | 'vinix', |
| 555 | ] { |
| 556 | assert c_directive_output_for_target(target, target, []).contains('#include <target_marker.h>') |
| 557 | assert !c_directive_output_for_target('linux', target, []).contains('#include <target_marker.h>') |
| 558 | } |
| 559 | } |
| 560 | |
| 561 | struct CrossGuardCase { |
| 562 | cond string |
| 563 | guard string |
| 564 | } |
| 565 | |
| 566 | fn test_c_directives_keep_cross_portable() { |
| 567 | assert c_directive_output_for_target('', 'cross', []).contains('#include <target_marker.h>') |
| 568 | cross_src := c_directive_output_for_target('cross', 'cross', []) |
| 569 | assert cross_src.contains('#include <target_marker.h>') |
| 570 | assert !cross_src.contains('#if') |
| 571 | |
| 572 | linux_src := c_directive_output_for_target('linux', 'cross', []) |
| 573 | assert linux_src.contains('#if defined(__linux__)') |
| 574 | assert linux_src.contains('#include <target_marker.h>') |
| 575 | assert linux_src.contains('#endif') |
| 576 | |
| 577 | windows_src := c_directive_output_for_target('windows', 'cross', []) |
| 578 | assert windows_src.contains('#if defined(_WIN32)') |
| 579 | assert windows_src.contains('#include <target_marker.h>') |
| 580 | assert windows_src.contains('#endif') |
| 581 | |
| 582 | macos_src := c_directive_output_for_target('macos', 'cross', []) |
| 583 | assert macos_src.contains('#if ${apple_macos_cross_guard}') |
| 584 | assert macos_src.contains('#include <target_marker.h>') |
| 585 | assert macos_src.contains('#endif') |
| 586 | } |
| 587 | |
| 588 | fn test_c_directives_keep_cross_extended_targets_portable() { |
| 589 | for case in [ |
| 590 | CrossGuardCase{'mac', apple_macos_cross_guard}, |
| 591 | CrossGuardCase{'darwin', apple_macos_cross_guard}, |
| 592 | CrossGuardCase{'freebsd', 'defined(__FreeBSD__)'}, |
| 593 | CrossGuardCase{'openbsd', 'defined(__OpenBSD__)'}, |
| 594 | CrossGuardCase{'netbsd', 'defined(__NetBSD__)'}, |
| 595 | CrossGuardCase{'dragonfly', 'defined(__DragonFly__)'}, |
| 596 | CrossGuardCase{'android', 'defined(__ANDROID__)'}, |
| 597 | CrossGuardCase{'termux', 'defined(__TERMUX__)'}, |
| 598 | CrossGuardCase{'ios', apple_ios_cross_guard}, |
| 599 | CrossGuardCase{'solaris', 'defined(__sun)'}, |
| 600 | CrossGuardCase{'qnx', 'defined(__QNX__)'}, |
| 601 | CrossGuardCase{'serenity', 'defined(__serenity__)'}, |
| 602 | CrossGuardCase{'plan9', 'defined(__plan9__)'}, |
| 603 | CrossGuardCase{'vinix', 'defined(__vinix__)'}, |
| 604 | ] { |
| 605 | src := c_directive_output_for_target(case.cond, 'cross', []) |
| 606 | assert src.contains('#if ${case.guard}') |
| 607 | assert src.contains('#include <target_marker.h>') |
| 608 | assert src.contains('#endif') |
| 609 | } |
| 610 | |
| 611 | bsd_src := c_directive_output_for_target('bsd', 'cross', []) |
| 612 | assert bsd_src.contains(apple_macos_cross_guard) |
| 613 | assert bsd_src.contains('defined(__FreeBSD__)') |
| 614 | assert bsd_src.contains('defined(__OpenBSD__)') |
| 615 | assert bsd_src.contains('defined(__NetBSD__)') |
| 616 | assert bsd_src.contains('defined(__DragonFly__)') |
| 617 | assert !bsd_src.contains('defined(__APPLE__) ||') |
| 618 | assert bsd_src.contains('#include <target_marker.h>') |
| 619 | } |
| 620 | |
| 621 | fn test_c_directives_keep_cross_complex_os_conditions_portable() { |
| 622 | not_linux_src := c_directive_output_for_target('!linux', 'cross', []) |
| 623 | assert not_linux_src.contains('#if !(defined(__linux__))') |
| 624 | assert not_linux_src.contains('#include <target_marker.h>') |
| 625 | |
| 626 | and_src := c_directive_output_for_target('linux && !windows', 'cross', []) |
| 627 | assert and_src.contains('#if (defined(__linux__)) && (!(defined(_WIN32)))') |
| 628 | assert and_src.contains('#include <target_marker.h>') |
| 629 | |
| 630 | cross_and_src := c_directive_output_for_target('cross && linux', 'cross', []) |
| 631 | assert cross_and_src.contains('#if defined(__linux__)') |
| 632 | assert cross_and_src.contains('#include <target_marker.h>') |
| 633 | assert !cross_and_src.contains('cross') |
| 634 | |
| 635 | feature_and_src := c_directive_output_for_target('feature && linux', 'cross', [ |
| 636 | 'feature', |
| 637 | ]) |
| 638 | assert feature_and_src.contains('#if defined(__linux__)') |
| 639 | assert feature_and_src.contains('#include <target_marker.h>') |
| 640 | assert !feature_and_src.contains('feature') |
| 641 | |
| 642 | not_feature_and_src := c_directive_output_for_target('!feature && linux', 'cross', [ |
| 643 | 'feature', |
| 644 | ]) |
| 645 | assert !not_feature_and_src.contains('#include <target_marker.h>') |
| 646 | |
| 647 | missing_feature_and_src := c_directive_output_for_target('feature && linux', 'cross', []) |
| 648 | assert !missing_feature_and_src.contains('#include <target_marker.h>') |
| 649 | |
| 650 | or_src := c_directive_output_for_target('linux || windows', 'cross', []) |
| 651 | assert or_src.contains('#if (defined(__linux__)) || (defined(_WIN32))') |
| 652 | assert or_src.contains('#include <target_marker.h>') |
| 653 | |
| 654 | not_and_src := c_directive_output_for_target('!windows && linux', 'cross', []) |
| 655 | assert not_and_src.contains('#if (!(defined(_WIN32))) && (defined(__linux__))') |
| 656 | assert not_and_src.contains('#include <target_marker.h>') |
| 657 | |
| 658 | or_and_src := c_directive_output_for_target('linux || windows && macos', 'cross', []) |
| 659 | assert or_and_src.contains('#if (defined(__linux__)) || ((defined(_WIN32)) && (${apple_macos_cross_guard}))') |
| 660 | assert or_and_src.contains('#include <target_marker.h>') |
| 661 | |
| 662 | not_group_src := c_directive_output_for_target('!(linux || windows) && macos', 'cross', []) |
| 663 | assert not_group_src.contains('#if (!((defined(__linux__)) || (defined(_WIN32)))) && (${apple_macos_cross_guard})') |
| 664 | assert not_group_src.contains('#include <target_marker.h>') |
| 665 | |
| 666 | assert c_directive_output_for_target('linux || windows && macos', 'linux', []).contains('#include <target_marker.h>') |
| 667 | assert !c_directive_output_for_target('linux || windows && macos', 'macos', []).contains('#include <target_marker.h>') |
| 668 | assert c_directive_output_for_target('!(linux || windows) && macos', 'macos', []).contains('#include <target_marker.h>') |
| 669 | assert !c_directive_output_for_target('!(linux || windows) && macos', 'windows', []).contains('#include <target_marker.h>') |
| 670 | } |
| 671 | |
| 672 | fn test_c_directives_keep_termux_distinct_from_android() { |
| 673 | assert c_directive_output_for_target('termux', 'termux', []).contains('#include <target_marker.h>') |
| 674 | assert c_directive_output_for_target('linux || termux', 'termux', []).contains('#include <target_marker.h>') |
| 675 | assert !c_directive_output_for_target('android', 'termux', []).contains('#include <target_marker.h>') |
| 676 | assert !c_directive_output_for_target('android && !termux', 'termux', []).contains('#include <target_marker.h>') |
| 677 | assert c_directive_output_for_target('android && !termux', 'android', []).contains('#include <target_marker.h>') |
| 678 | } |
| 679 | |
| 680 | fn test_cross_optional_termux_flag_named_like_os_is_not_os_guarded() { |
| 681 | assert !c_directive_output_for_target('termux ?', 'cross', []).contains('#include <target_marker.h>') |
| 682 | optional_directive_src := c_directive_output_for_target('termux ?', 'cross', [ |
| 683 | 'termux', |
| 684 | ]) |
| 685 | assert optional_directive_src.contains('#include <target_marker.h>') |
| 686 | assert !optional_directive_src.contains('defined(__TERMUX__)') |
| 687 | } |
| 688 | |
| 689 | fn test_optional_target_mode_directives_ignore_synthesized_defines() { |
| 690 | assert !c_directive_output_for_target_with_explicit('cross ?', 'cross', ['cross'], [], false).contains('#include <target_marker.h>') |
| 691 | assert c_directive_output_for_target_with_explicit('cross ?', 'cross', ['cross'], [ |
| 692 | 'cross', |
| 693 | ], false).contains('#include <target_marker.h>') |
| 694 | |
| 695 | assert !c_directive_output_for_freestanding_target('freestanding ?', 'linux').contains('#include <target_marker.h>') |
| 696 | assert c_directive_output_for_target_with_explicit('freestanding ?', 'linux', [ |
| 697 | 'freestanding', |
| 698 | ], ['freestanding'], true).contains('#include <target_marker.h>') |
| 699 | |
| 700 | assert !c_directive_output_for_target_with_explicit('none ?', 'none', [ |
| 701 | 'freestanding', |
| 702 | ], [], true).contains('#include <target_marker.h>') |
| 703 | assert c_directive_output_for_target_with_explicit('none ?', 'none', [ |
| 704 | 'freestanding', |
| 705 | ], ['none'], true).contains('#include <target_marker.h>') |
| 706 | } |
| 707 | |
| 708 | fn test_comptime_if_directives_support_infix_os_conditions() { |
| 709 | source := 'module main |
| 710 | |
| 711 | \$if linux || windows { |
| 712 | #include <active_or_marker.h> |
| 713 | } |
| 714 | |
| 715 | \$if linux && !windows { |
| 716 | #include <active_and_marker.h> |
| 717 | } |
| 718 | |
| 719 | \$if macos { |
| 720 | #include <active_macos_marker.h> |
| 721 | } |
| 722 | |
| 723 | \$if cross && linux { |
| 724 | #include <cross_linux_marker.h> |
| 725 | } |
| 726 | |
| 727 | fn main() {} |
| 728 | ' |
| 729 | linux_src := generated_c_for_target_program_with_options('comptime_if_infix_linux', source, |
| 730 | 'linux', false, false) |
| 731 | assert linux_src.contains('#include <active_or_marker.h>') |
| 732 | assert linux_src.contains('#include <active_and_marker.h>') |
| 733 | assert !linux_src.contains('#include <active_macos_marker.h>') |
| 734 | assert !linux_src.contains('#include <cross_linux_marker.h>') |
| 735 | |
| 736 | macos_src := generated_c_for_target_program_with_options('comptime_if_infix_macos', source, |
| 737 | 'macos', false, false) |
| 738 | assert !macos_src.contains('#include <active_or_marker.h>') |
| 739 | assert !macos_src.contains('#include <active_and_marker.h>') |
| 740 | assert macos_src.contains('#include <active_macos_marker.h>') |
| 741 | assert !macos_src.contains('#include <cross_linux_marker.h>') |
| 742 | |
| 743 | windows_src := generated_c_for_target_program_with_options('comptime_if_infix_windows', source, |
| 744 | 'windows', false, false) |
| 745 | assert windows_src.contains('#include <active_or_marker.h>') |
| 746 | assert !windows_src.contains('#include <active_and_marker.h>') |
| 747 | assert !windows_src.contains('#include <active_macos_marker.h>') |
| 748 | assert !windows_src.contains('#include <cross_linux_marker.h>') |
| 749 | |
| 750 | cross_src := generated_c_for_target_program_with_options('comptime_if_infix_cross', source, |
| 751 | 'cross', false, false) |
| 752 | assert cross_src.contains('defined(__linux__)') |
| 753 | assert cross_src.contains('defined(_WIN32)') |
| 754 | assert cross_src.contains('#include <active_or_marker.h>') |
| 755 | assert cross_src.contains('!(defined(_WIN32))') |
| 756 | assert cross_src.contains('#include <active_and_marker.h>') |
| 757 | assert cross_src.contains(apple_macos_cross_guard) |
| 758 | assert cross_src.contains('#include <active_macos_marker.h>') |
| 759 | assert cross_src.contains('#if defined(__linux__)\n#include <cross_linux_marker.h>\n#endif') |
| 760 | } |
| 761 | |
| 762 | fn test_comptime_if_pkgconfig_directives_select_available_branch() { |
| 763 | if !vpref.comptime_pkgconfig_value('sqlite3') { |
| 764 | return |
| 765 | } |
| 766 | source := 'module main |
| 767 | |
| 768 | \$if \$pkgconfig("sqlite3") { |
| 769 | #include "sqlite3.h" |
| 770 | } \$else \$if darwin { |
| 771 | #include <sqlite_darwin_fallback_marker.h> |
| 772 | } |
| 773 | |
| 774 | fn main() {} |
| 775 | ' |
| 776 | macos_src := generated_c_for_target_program_with_options('pkgconfig_sqlite3_directive_macos', |
| 777 | source, 'macos', false, false) |
| 778 | assert macos_src.contains('#include "sqlite3.h"') |
| 779 | assert !macos_src.contains('#include <sqlite_darwin_fallback_marker.h>') |
| 780 | } |
| 781 | |
| 782 | fn test_cross_comptime_if_else_directives_do_not_emit_selected_else_unguarded() { |
| 783 | source := 'module main |
| 784 | |
| 785 | \$if linux { |
| 786 | #include <linux_marker.h> |
| 787 | } \$else { |
| 788 | #include <fallback_marker.h> |
| 789 | } |
| 790 | |
| 791 | fn main() {} |
| 792 | ' |
| 793 | cross_src := generated_c_for_target_program_with_options('comptime_if_else_cross', source, |
| 794 | 'cross', false, false) |
| 795 | assert cross_src.contains('#if defined(__linux__)\n#include <linux_marker.h>\n#endif') |
| 796 | assert cross_src.contains('#if !(defined(__linux__))\n#include <fallback_marker.h>\n#endif') |
| 797 | assert cross_src.count('#include <fallback_marker.h>') == 1 |
| 798 | assert cross_src.count('#include <linux_marker.h>') == 1 |
| 799 | } |
| 800 | |
| 801 | fn test_cross_comptime_directive_scope_preserves_nested_preprocessor_blocks() { |
| 802 | source := 'module main |
| 803 | |
| 804 | \$if linux { |
| 805 | #ifdef HAVE_FOO |
| 806 | #include <foo.h> |
| 807 | #endif |
| 808 | } |
| 809 | |
| 810 | fn main() {} |
| 811 | ' |
| 812 | cross_src := generated_c_for_target_program_with_options('nested_preprocessor_cross', source, |
| 813 | 'cross', false, false) |
| 814 | assert cross_src.contains('#if defined(__linux__)\n#ifdef HAVE_FOO\n#include <foo.h>\n#endif\n#endif') |
| 815 | assert !cross_src.contains('#if defined(__linux__)\n#ifdef HAVE_FOO\n#endif\n#if defined(__linux__)') |
| 816 | assert cross_src.count('#include <foo.h>') == 1 |
| 817 | } |
| 818 | |
| 819 | fn test_cross_user_define_comptime_directives_reduce_to_os_guards() { |
| 820 | source := 'module main |
| 821 | |
| 822 | \$if feature && linux { |
| 823 | #include <feature_linux.h> |
| 824 | } |
| 825 | |
| 826 | \$if !feature && linux { |
| 827 | #include <disabled_feature_linux.h> |
| 828 | } |
| 829 | |
| 830 | \$if missing_feature && linux { |
| 831 | #include <missing_feature_linux.h> |
| 832 | } |
| 833 | |
| 834 | fn main() {} |
| 835 | ' |
| 836 | feature_src := generated_c_for_target_program_with_defines('feature_directive_cross', source, |
| 837 | 'cross', ['feature'], false, false) |
| 838 | assert feature_src.contains('#if defined(__linux__)\n#include <feature_linux.h>\n#endif') |
| 839 | assert feature_src.count('#include <feature_linux.h>') == 1 |
| 840 | assert !feature_src.contains('#include <disabled_feature_linux.h>') |
| 841 | assert !feature_src.contains('#include <missing_feature_linux.h>') |
| 842 | assert !feature_src.contains('feature && linux') |
| 843 | } |
| 844 | |
| 845 | fn test_cross_optional_user_flag_named_like_os_is_not_os_guarded() { |
| 846 | assert !c_directive_output_for_target('linux ?', 'cross', []).contains('#include <target_marker.h>') |
| 847 | optional_directive_src := c_directive_output_for_target('linux ?', 'cross', [ |
| 848 | 'linux', |
| 849 | ]) |
| 850 | assert optional_directive_src.contains('#include <target_marker.h>') |
| 851 | assert !optional_directive_src.contains('defined(__linux__)') |
| 852 | |
| 853 | source := 'module main |
| 854 | |
| 855 | \$if linux ? { |
| 856 | #include <optional_linux_marker.h> |
| 857 | } |
| 858 | |
| 859 | fn main() {} |
| 860 | ' |
| 861 | missing_src := generated_c_for_target_program_with_options('optional_linux_missing_cross', |
| 862 | source, 'cross', false, false) |
| 863 | assert !missing_src.contains('#include <optional_linux_marker.h>') |
| 864 | |
| 865 | defined_src := generated_c_for_target_program_with_defines('optional_linux_defined_cross', |
| 866 | source, 'cross', ['linux'], false, false) |
| 867 | assert defined_src.contains('#include <optional_linux_marker.h>') |
| 868 | assert !defined_src.contains('#if defined(__linux__)\n#include <optional_linux_marker.h>') |
| 869 | } |
| 870 | |
| 871 | fn test_optional_user_flag_named_like_os_is_not_target_os_flag() { |
| 872 | source := 'module main |
| 873 | |
| 874 | \$if linux { |
| 875 | #include <linux_marker.h> |
| 876 | } |
| 877 | |
| 878 | \$if linux ? { |
| 879 | #include <optional_linux_marker.h> |
| 880 | } |
| 881 | |
| 882 | \$if linux ? || windows { |
| 883 | #include <optional_linux_or_windows_marker.h> |
| 884 | } |
| 885 | |
| 886 | fn main() {} |
| 887 | ' |
| 888 | linux_src := generated_c_for_target_program_with_options('optional_linux_plain_linux', source, |
| 889 | 'linux', false, false) |
| 890 | assert linux_src.contains('#include <linux_marker.h>') |
| 891 | assert !linux_src.contains('#include <optional_linux_marker.h>') |
| 892 | assert !linux_src.contains('#include <optional_linux_or_windows_marker.h>') |
| 893 | |
| 894 | linux_defined_src := generated_c_for_target_program_with_defines('optional_linux_defined_linux', |
| 895 | source, 'linux', ['linux'], false, false) |
| 896 | assert linux_defined_src.contains('#include <linux_marker.h>') |
| 897 | assert linux_defined_src.contains('#include <optional_linux_marker.h>') |
| 898 | assert linux_defined_src.contains('#include <optional_linux_or_windows_marker.h>') |
| 899 | |
| 900 | windows_src := generated_c_for_target_program_with_options('optional_linux_windows', source, |
| 901 | 'windows', false, false) |
| 902 | assert !windows_src.contains('#include <linux_marker.h>') |
| 903 | assert !windows_src.contains('#include <optional_linux_marker.h>') |
| 904 | assert windows_src.contains('#include <optional_linux_or_windows_marker.h>') |
| 905 | } |
| 906 | |
| 907 | fn test_c_directives_keep_freestanding_user_os_directives_for_concrete_target() { |
| 908 | assert c_directive_output_for_target('', 'linux', ['freestanding']).contains('#include <target_marker.h>') |
| 909 | assert c_directive_output_for_target('freestanding', 'linux', ['freestanding']).contains('#include <target_marker.h>') |
| 910 | assert c_directive_output_for_target('linux', 'linux', ['freestanding']).contains('#include <target_marker.h>') |
| 911 | assert !c_directive_output_for_target('windows', 'linux', ['freestanding']).contains('#include <target_marker.h>') |
| 912 | assert c_directive_output_for_target('windows', 'windows', ['freestanding']).contains('#include <target_marker.h>') |
| 913 | assert c_directive_output_for_freestanding_target('freestanding', 'linux').contains('#include <target_marker.h>') |
| 914 | } |
| 915 | |
| 916 | fn test_freestanding_none_c_directives_select_only_freestanding() { |
| 917 | assert c_directive_output_for_freestanding_target('freestanding', 'none').contains('#include <target_marker.h>') |
| 918 | for cond in ['linux', 'windows', 'macos', 'darwin', 'cross'] { |
| 919 | src := c_directive_output_for_freestanding_target(cond, 'none') |
| 920 | assert !src.contains('#include <target_marker.h>'), cond |
| 921 | } |
| 922 | } |
| 923 | |
| 924 | fn test_freestanding_none_comptime_if_directives_select_only_freestanding() { |
| 925 | source := 'module main |
| 926 | |
| 927 | \$if freestanding { |
| 928 | #include <freestanding_none_marker.h> |
| 929 | } |
| 930 | |
| 931 | \$if linux { |
| 932 | #include <linux_none_marker.h> |
| 933 | } |
| 934 | |
| 935 | \$if windows { |
| 936 | #include <windows_none_marker.h> |
| 937 | } |
| 938 | |
| 939 | \$if macos { |
| 940 | #include <macos_none_marker.h> |
| 941 | } |
| 942 | |
| 943 | \$if cross { |
| 944 | #include <cross_none_marker.h> |
| 945 | } |
| 946 | |
| 947 | fn main() {} |
| 948 | ' |
| 949 | src := generated_c_for_target_program_with_options('freestanding_none_comptime_if', source, |
| 950 | 'none', true, true) |
| 951 | assert src.contains('#include <freestanding_none_marker.h>') |
| 952 | assert !src.contains('#include <linux_none_marker.h>') |
| 953 | assert !src.contains('#include <windows_none_marker.h>') |
| 954 | assert !src.contains('#include <macos_none_marker.h>') |
| 955 | assert !src.contains('#include <cross_none_marker.h>') |
| 956 | assert_no_os_runtime_headers(src) |
| 957 | } |
| 958 | |
| 959 | fn test_preamble_specializes_apple_includes_by_target() { |
| 960 | linux_src := preamble_for_target('linux', []) |
| 961 | assert !linux_src.contains('__APPLE__') |
| 962 | assert !linux_src.contains('#include <mach/mach.h>') |
| 963 | |
| 964 | windows_src := preamble_for_target('windows', []) |
| 965 | assert !windows_src.contains('__APPLE__') |
| 966 | assert !windows_src.contains('#include <mach/mach.h>') |
| 967 | |
| 968 | macos_src := preamble_for_target('macos', []) |
| 969 | assert macos_src.contains('#include <mach/mach.h>') |
| 970 | assert !macos_src.contains('#ifdef __APPLE__') |
| 971 | |
| 972 | cross_src := preamble_for_target('cross', []) |
| 973 | assert cross_src.contains('#if ${apple_macos_cross_guard}') |
| 974 | assert !cross_src.contains('#ifdef __APPLE__') |
| 975 | assert cross_src.contains('#include <mach/mach.h>') |
| 976 | assert cross_src.contains('#if defined(_WIN32)\n#include <windows.h>\n#else\n#include <dirent.h>\n#include <pthread.h>\n#endif') |
| 977 | assert cross_src.contains('#if defined(_WIN32)\ntypedef struct sync__RwMutex { SRWLOCK mutex; u32 inited; } sync__RwMutex;') |
| 978 | assert cross_src.contains('#else\ntypedef struct sync__RwMutex { pthread_rwlock_t mutex; u32 inited; } sync__RwMutex;') |
| 979 | full_cross_src := full_preamble_for_target('cross', []) |
| 980 | assert full_cross_src.contains('#if defined(_WIN32)\n#include <windows.h>\n#else\n#include <unistd.h>') |
| 981 | assert full_cross_src.contains('#include <pthread.h>\n#include <sys/time.h>') |
| 982 | assert full_cross_src.contains('extern char** environ;\n#endif') |
| 983 | } |
| 984 | |
| 985 | fn test_linux_preamble_declares_v_owned_gettid_helper_before_c_calls() { |
| 986 | src := full_preamble_for_target('linux', []) |
| 987 | assert src.contains(linux_gettid_feature_define) |
| 988 | assert src.contains('#include <unistd.h>') |
| 989 | assert src.contains(linux_gettid_helper) |
| 990 | assert !src.contains('#define gettid') |
| 991 | assert_c_marker_before(src, '#define _GNU_SOURCE', '#include <stdio.h>') |
| 992 | assert_c_marker_before(src, '#include <unistd.h>', '#include <sys/syscall.h>') |
| 993 | assert_c_marker_before(src, '#include <sys/syscall.h>', |
| 994 | 'static inline uint32_t v_cleanc_gettid(void)') |
| 995 | } |
| 996 | |
| 997 | fn test_linux_minimal_preamble_declares_v_owned_gettid_helper() { |
| 998 | src := preamble_for_target('linux', []) |
| 999 | assert src.contains(linux_gettid_feature_define) |
| 1000 | assert src.contains('#include <unistd.h>') |
| 1001 | assert src.contains(linux_gettid_helper) |
| 1002 | assert !src.contains('#define gettid') |
| 1003 | assert_c_marker_before(src, '#define _GNU_SOURCE', '#include <stdio.h>') |
| 1004 | assert_c_marker_before(src, '#include <unistd.h>', '#include <sys/syscall.h>') |
| 1005 | assert_c_marker_before(src, '#include <sys/syscall.h>', |
| 1006 | 'static inline uint32_t v_cleanc_gettid(void)') |
| 1007 | } |
| 1008 | |
| 1009 | fn test_gettid_linux_compat_does_not_leak_to_non_linux_targets() { |
| 1010 | for target in ['windows', 'macos', 'cross', 'freebsd', 'android', 'termux'] { |
| 1011 | minimal_src := preamble_for_target(target, []) |
| 1012 | full_src := full_preamble_for_target(target, []) |
| 1013 | assert_no_gettid_compat(minimal_src) |
| 1014 | assert_no_gettid_compat(full_src) |
| 1015 | } |
| 1016 | |
| 1017 | freestanding_src := full_preamble_for_options('linux', [], true, false) |
| 1018 | assert_no_gettid_compat(freestanding_src) |
| 1019 | assert_no_os_runtime_headers(freestanding_src) |
| 1020 | } |
| 1021 | |
| 1022 | fn test_gettid_linux_compat_respects_no_gettid_and_musl_defines() { |
| 1023 | for define in ['no_gettid', 'musl'] { |
| 1024 | assert_no_gettid_compat(preamble_for_target('linux', [define])) |
| 1025 | assert_no_gettid_compat(full_preamble_for_target('linux', [define])) |
| 1026 | } |
| 1027 | } |
| 1028 | |
| 1029 | fn test_generated_linux_gettid_call_has_compat_preamble() { |
| 1030 | source := 'module main |
| 1031 | |
| 1032 | fn C.gettid() u32 |
| 1033 | |
| 1034 | fn thread_id() u32 { |
| 1035 | return C.gettid() |
| 1036 | } |
| 1037 | |
| 1038 | fn main() { |
| 1039 | _ = thread_id() |
| 1040 | } |
| 1041 | ' |
| 1042 | src := generated_c_for_target_program_with_options('linux_gettid_call', source, 'linux', false, |
| 1043 | false) |
| 1044 | assert src.contains(linux_gettid_feature_define) |
| 1045 | assert src.contains(linux_gettid_helper) |
| 1046 | assert !src.contains('#define gettid') |
| 1047 | assert src.contains('return v_cleanc_gettid();') |
| 1048 | assert !src.contains('return gettid();') |
| 1049 | assert_c_marker_before(src, '#define _GNU_SOURCE', '#include <stdio.h>') |
| 1050 | assert_c_marker_before(src, 'static inline uint32_t v_cleanc_gettid(void)', |
| 1051 | 'return v_cleanc_gettid();') |
| 1052 | } |
| 1053 | |
| 1054 | fn test_generated_musl_optional_gettid_branch_is_not_selected() { |
| 1055 | source := 'module main |
| 1056 | |
| 1057 | fn C.gettid() u32 |
| 1058 | |
| 1059 | fn thread_id() u32 { |
| 1060 | $if linux && !musl ? { |
| 1061 | return C.gettid() |
| 1062 | } $else { |
| 1063 | return 0 |
| 1064 | } |
| 1065 | } |
| 1066 | |
| 1067 | fn main() { |
| 1068 | _ = thread_id() |
| 1069 | } |
| 1070 | ' |
| 1071 | src := generated_c_for_target_program_with_defines('linux_musl_gettid_branch', source, 'linux', [ |
| 1072 | 'musl', |
| 1073 | ], false, false) |
| 1074 | assert_no_gettid_compat(src) |
| 1075 | assert !src.contains('return gettid();') |
| 1076 | assert src.contains('return 0;') |
| 1077 | } |
| 1078 | |
| 1079 | fn test_generated_no_gettid_optional_branch_does_not_emit_compat() { |
| 1080 | source := 'module main |
| 1081 | |
| 1082 | fn C.gettid() u32 |
| 1083 | |
| 1084 | fn thread_id() u32 { |
| 1085 | $if no_gettid ? { |
| 1086 | return 0 |
| 1087 | } $else $if linux && !musl ? { |
| 1088 | return C.gettid() |
| 1089 | } $else { |
| 1090 | return 1 |
| 1091 | } |
| 1092 | } |
| 1093 | |
| 1094 | fn main() { |
| 1095 | _ = thread_id() |
| 1096 | } |
| 1097 | ' |
| 1098 | src := generated_c_for_target_program_with_defines('linux_no_gettid_branch', source, 'linux', [ |
| 1099 | 'no_gettid', |
| 1100 | ], false, false) |
| 1101 | assert_no_gettid_compat(src) |
| 1102 | assert !src.contains('return gettid();') |
| 1103 | assert src.contains('return 0;') |
| 1104 | } |
| 1105 | |
| 1106 | fn test_trace_calls_import_respects_no_gettid_and_musl() { |
| 1107 | for define in ['no_gettid', 'musl'] { |
| 1108 | src := generated_c_for_trace_calls_import_with_defines('trace_calls_${define}_import', [ |
| 1109 | define, |
| 1110 | ]) |
| 1111 | assert_no_gettid_compat(src) |
| 1112 | assert !src.contains('gettid(') |
| 1113 | assert src.contains('pthread_self()') |
| 1114 | } |
| 1115 | } |
| 1116 | |
| 1117 | fn test_label_followed_by_result_or_temp_emits_null_statement() { |
| 1118 | src := generated_c_for_target_program('label_before_result_or_temp', ' |
| 1119 | fn maybe_label_value() !string { |
| 1120 | return "ok" |
| 1121 | } |
| 1122 | |
| 1123 | fn main() { |
| 1124 | start_no_time: |
| 1125 | value := maybe_label_value() or { return } |
| 1126 | _ = value |
| 1127 | } |
| 1128 | ') |
| 1129 | assert src.contains('start_no_time:;') |
| 1130 | assert src.contains('_result_string _or_t') |
| 1131 | assert !src.contains('start_no_time:\n\t_result_string _or_t') |
| 1132 | } |
| 1133 | |
| 1134 | fn test_generated_sort_comparator_casts_named_fn_to_fnsortcb() { |
| 1135 | src := generated_c_for_target_program('sort_generated_comparator_fnsortcb_cast', ' |
| 1136 | struct RepIndex { |
| 1137 | idx int |
| 1138 | } |
| 1139 | |
| 1140 | fn main() { |
| 1141 | mut idxs := [RepIndex{idx: 2}, RepIndex{idx: 1}] |
| 1142 | idxs.sort(a.idx < b.idx) |
| 1143 | } |
| 1144 | ') |
| 1145 | assert src.contains('int __sort_cmp_RepIndex_by_idx_asc(RepIndex* a, RepIndex* b);') |
| 1146 | assert src.contains('array__sort_with_compare(idxs, (FnSortCB)__sort_cmp_RepIndex_by_idx_asc);') |
| 1147 | assert !src.contains('array__sort_with_compare(idxs, __sort_cmp_RepIndex_by_idx_asc);') |
| 1148 | } |
| 1149 | |
| 1150 | fn test_sort_with_compare_named_comparator_casts_to_fnsortcb() { |
| 1151 | src := generated_c_for_target_program('sort_named_comparator_fnsortcb_cast', ' |
| 1152 | type FnSortCB = fn (voidptr, voidptr) int |
| 1153 | |
| 1154 | struct Item { |
| 1155 | value int |
| 1156 | } |
| 1157 | |
| 1158 | fn array__sort_with_compare(mut items []Item, callback FnSortCB) |
| 1159 | |
| 1160 | fn compare_items(a &Item, b &Item) int { |
| 1161 | return a.value - b.value |
| 1162 | } |
| 1163 | |
| 1164 | fn main() { |
| 1165 | mut items := [Item{value: 2}, Item{value: 1}] |
| 1166 | array__sort_with_compare(mut items, compare_items) |
| 1167 | } |
| 1168 | ') |
| 1169 | assert src.contains('array__sort_with_compare(&items, (FnSortCB)compare_items);') |
| 1170 | assert !src.contains('array__sort_with_compare(&items, compare_items);') |
| 1171 | } |
| 1172 | |
| 1173 | fn test_sort_with_compare_same_name_non_fnsortcb_callback_is_not_cast_to_fnsortcb() { |
| 1174 | src := generated_c_for_target_program('sort_same_name_non_fnsortcb_callback_no_cast', ' |
| 1175 | struct Item { |
| 1176 | value int |
| 1177 | } |
| 1178 | |
| 1179 | type ItemCallback = fn (&Item, &Item) int |
| 1180 | |
| 1181 | fn array__sort_with_compare(mut items []Item, callback ItemCallback) |
| 1182 | |
| 1183 | fn compare_items(a &Item, b &Item) int { |
| 1184 | return a.value - b.value |
| 1185 | } |
| 1186 | |
| 1187 | fn main() { |
| 1188 | mut items := [Item{value: 2}, Item{value: 1}] |
| 1189 | array__sort_with_compare(mut items, compare_items) |
| 1190 | } |
| 1191 | ') |
| 1192 | assert src.contains('array__sort_with_compare(&items, compare_items);') |
| 1193 | assert !src.contains('(FnSortCB)compare_items') |
| 1194 | } |
| 1195 | |
| 1196 | fn test_sort_with_compare_module_qualified_comparator_casts_to_fnsortcb() { |
| 1197 | other_file := os.join_path(os.temp_dir(), |
| 1198 | 'v2_cleanc_target_codegen_sort_other_${os.getpid()}.v') |
| 1199 | os.write_file(other_file, ' |
| 1200 | module other |
| 1201 | |
| 1202 | pub struct Item { |
| 1203 | pub: |
| 1204 | value int |
| 1205 | } |
| 1206 | |
| 1207 | pub fn compare_items(a &Item, b &Item) int { |
| 1208 | return a.value - b.value |
| 1209 | } |
| 1210 | ') or { |
| 1211 | panic(err) |
| 1212 | } |
| 1213 | defer { |
| 1214 | os.rm(other_file) or {} |
| 1215 | } |
| 1216 | src := generated_c_for_target_program_with_extra_files('sort_module_qualified_comparator_fnsortcb_cast', ' |
| 1217 | module main |
| 1218 | |
| 1219 | import other |
| 1220 | |
| 1221 | type FnSortCB = fn (voidptr, voidptr) int |
| 1222 | |
| 1223 | fn array__sort_with_compare(mut items []other.Item, callback FnSortCB) |
| 1224 | |
| 1225 | fn main() { |
| 1226 | mut items := [other.Item{value: 2}, other.Item{value: 1}] |
| 1227 | array__sort_with_compare(mut items, other.compare_items) |
| 1228 | } |
| 1229 | ', |
| 1230 | 'linux', [], false, false, [other_file]) |
| 1231 | assert src.contains('array__sort_with_compare(&items, (FnSortCB)other__compare_items);') |
| 1232 | assert !src.contains('array__sort_with_compare(&items, other__compare_items);') |
| 1233 | } |
| 1234 | |
| 1235 | fn test_sort_with_compare_import_alias_comparator_casts_to_real_module_fnsortcb() { |
| 1236 | other_file := os.join_path(os.temp_dir(), |
| 1237 | 'v2_cleanc_target_codegen_sort_other_alias_${os.getpid()}.v') |
| 1238 | os.write_file(other_file, ' |
| 1239 | module other |
| 1240 | |
| 1241 | pub struct Item { |
| 1242 | pub: |
| 1243 | value int |
| 1244 | } |
| 1245 | |
| 1246 | pub fn compare_items(a &Item, b &Item) int { |
| 1247 | return a.value - b.value |
| 1248 | } |
| 1249 | ') or { |
| 1250 | panic(err) |
| 1251 | } |
| 1252 | defer { |
| 1253 | os.rm(other_file) or {} |
| 1254 | } |
| 1255 | src := generated_c_for_target_program_with_extra_files('sort_import_alias_comparator_fnsortcb_cast', ' |
| 1256 | module main |
| 1257 | |
| 1258 | import other as oth |
| 1259 | |
| 1260 | type FnSortCB = fn (voidptr, voidptr) int |
| 1261 | |
| 1262 | fn array__sort_with_compare(mut items []oth.Item, callback FnSortCB) |
| 1263 | |
| 1264 | fn main() { |
| 1265 | mut items := [oth.Item{value: 2}, oth.Item{value: 1}] |
| 1266 | array__sort_with_compare(mut items, oth.compare_items) |
| 1267 | } |
| 1268 | ', |
| 1269 | 'linux', [], false, false, [other_file]) |
| 1270 | assert src.contains('array__sort_with_compare(&items, (FnSortCB)other__compare_items);') |
| 1271 | assert !src.contains('array__sort_with_compare(&items, other__compare_items);') |
| 1272 | assert !src.contains('(FnSortCB)oth__compare_items') |
| 1273 | } |
| 1274 | |
| 1275 | fn test_sort_with_compare_module_global_callback_casts_to_fnsortcb() { |
| 1276 | other_file := os.join_path(os.temp_dir(), |
| 1277 | 'v2_cleanc_target_codegen_sort_other_global_callback_${os.getpid()}.v') |
| 1278 | os.write_file(other_file, ' |
| 1279 | module other |
| 1280 | |
| 1281 | pub struct Item { |
| 1282 | pub: |
| 1283 | value int |
| 1284 | } |
| 1285 | |
| 1286 | pub type ItemCallback = fn (&Item, &Item) int |
| 1287 | |
| 1288 | pub __global callback ItemCallback |
| 1289 | ') or { |
| 1290 | panic(err) |
| 1291 | } |
| 1292 | defer { |
| 1293 | os.rm(other_file) or {} |
| 1294 | } |
| 1295 | src := generated_c_for_target_program_with_extra_files('sort_module_global_callback_fnsortcb_cast', ' |
| 1296 | module main |
| 1297 | |
| 1298 | import other |
| 1299 | |
| 1300 | type FnSortCB = fn (voidptr, voidptr) int |
| 1301 | |
| 1302 | fn array__sort_with_compare(mut items []other.Item, callback FnSortCB) |
| 1303 | |
| 1304 | fn main() { |
| 1305 | mut items := [other.Item{value: 2}, other.Item{value: 1}] |
| 1306 | array__sort_with_compare(mut items, other.callback) |
| 1307 | } |
| 1308 | ', |
| 1309 | 'linux', [], false, false, [other_file]) |
| 1310 | assert src.contains('array__sort_with_compare(&items, (FnSortCB)other__callback);') |
| 1311 | assert !src.contains('array__sort_with_compare(&items, other__callback);') |
| 1312 | } |
| 1313 | |
| 1314 | fn test_sort_with_compare_import_alias_global_callback_casts_to_real_module_fnsortcb() { |
| 1315 | other_file := os.join_path(os.temp_dir(), |
| 1316 | 'v2_cleanc_target_codegen_sort_other_alias_global_callback_${os.getpid()}.v') |
| 1317 | os.write_file(other_file, ' |
| 1318 | module other |
| 1319 | |
| 1320 | pub struct Item { |
| 1321 | pub: |
| 1322 | value int |
| 1323 | } |
| 1324 | |
| 1325 | pub type ItemCallback = fn (&Item, &Item) int |
| 1326 | |
| 1327 | pub __global callback ItemCallback |
| 1328 | ') or { |
| 1329 | panic(err) |
| 1330 | } |
| 1331 | defer { |
| 1332 | os.rm(other_file) or {} |
| 1333 | } |
| 1334 | src := generated_c_for_target_program_with_extra_files('sort_import_alias_global_callback_fnsortcb_cast', ' |
| 1335 | module main |
| 1336 | |
| 1337 | import other as oth |
| 1338 | |
| 1339 | type FnSortCB = fn (voidptr, voidptr) int |
| 1340 | |
| 1341 | fn array__sort_with_compare(mut items []oth.Item, callback FnSortCB) |
| 1342 | |
| 1343 | fn main() { |
| 1344 | mut items := [oth.Item{value: 2}, oth.Item{value: 1}] |
| 1345 | array__sort_with_compare(mut items, oth.callback) |
| 1346 | } |
| 1347 | ', |
| 1348 | 'linux', [], false, false, [other_file]) |
| 1349 | assert src.contains('array__sort_with_compare(&items, (FnSortCB)other__callback);') |
| 1350 | assert !src.contains('array__sort_with_compare(&items, other__callback);') |
| 1351 | assert !src.contains('(FnSortCB)oth__callback') |
| 1352 | } |
| 1353 | |
| 1354 | fn test_sort_with_compare_module_const_callback_casts_to_fnsortcb() { |
| 1355 | other_file := os.join_path(os.temp_dir(), |
| 1356 | 'v2_cleanc_target_codegen_sort_other_const_callback_${os.getpid()}.v') |
| 1357 | os.write_file(other_file, ' |
| 1358 | module other |
| 1359 | |
| 1360 | pub struct Item { |
| 1361 | pub: |
| 1362 | value int |
| 1363 | } |
| 1364 | |
| 1365 | pub type ItemCallback = fn (&Item, &Item) int |
| 1366 | |
| 1367 | pub fn compare_items(a &Item, b &Item) int { |
| 1368 | return a.value - b.value |
| 1369 | } |
| 1370 | |
| 1371 | pub const callback = ItemCallback(compare_items) |
| 1372 | ') or { |
| 1373 | panic(err) |
| 1374 | } |
| 1375 | defer { |
| 1376 | os.rm(other_file) or {} |
| 1377 | } |
| 1378 | src := generated_c_for_target_program_with_extra_files('sort_module_const_callback_fnsortcb_cast', ' |
| 1379 | module main |
| 1380 | |
| 1381 | import other |
| 1382 | |
| 1383 | type FnSortCB = fn (voidptr, voidptr) int |
| 1384 | |
| 1385 | fn array__sort_with_compare(mut items []other.Item, callback FnSortCB) |
| 1386 | |
| 1387 | fn main() { |
| 1388 | mut items := [other.Item{value: 2}, other.Item{value: 1}] |
| 1389 | array__sort_with_compare(mut items, other.callback) |
| 1390 | } |
| 1391 | ', |
| 1392 | 'linux', [], false, false, [other_file]) |
| 1393 | assert src.contains('array__sort_with_compare(&items, (FnSortCB)other__callback);') |
| 1394 | assert !src.contains('array__sort_with_compare(&items, other__callback);') |
| 1395 | } |
| 1396 | |
| 1397 | fn test_sort_with_compare_module_global_non_fn_value_is_not_cast_to_fnsortcb() { |
| 1398 | other_file := os.join_path(os.temp_dir(), |
| 1399 | 'v2_cleanc_target_codegen_sort_other_global_non_fn_${os.getpid()}.v') |
| 1400 | os.write_file(other_file, ' |
| 1401 | module other |
| 1402 | |
| 1403 | pub struct Item { |
| 1404 | pub: |
| 1405 | value int |
| 1406 | } |
| 1407 | |
| 1408 | pub __global callback int |
| 1409 | ') or { |
| 1410 | panic(err) |
| 1411 | } |
| 1412 | defer { |
| 1413 | os.rm(other_file) or {} |
| 1414 | } |
| 1415 | src := generated_c_for_target_program_with_extra_files('sort_module_global_non_fn_no_fnsortcb_cast', ' |
| 1416 | module main |
| 1417 | |
| 1418 | import other |
| 1419 | |
| 1420 | type FnSortCB = fn (voidptr, voidptr) int |
| 1421 | |
| 1422 | fn array__sort_with_compare(mut items []other.Item, callback FnSortCB) |
| 1423 | |
| 1424 | fn main() { |
| 1425 | mut items := [other.Item{value: 2}, other.Item{value: 1}] |
| 1426 | array__sort_with_compare(mut items, other.callback) |
| 1427 | } |
| 1428 | ', |
| 1429 | 'linux', [], false, false, [other_file]) |
| 1430 | assert src.contains('array__sort_with_compare(&items, other__callback);') |
| 1431 | assert !src.contains('(FnSortCB)other__callback') |
| 1432 | } |
| 1433 | |
| 1434 | fn test_sort_with_compare_local_selector_shadowing_module_is_not_cast_to_fnsortcb() { |
| 1435 | other_file := os.join_path(os.temp_dir(), |
| 1436 | 'v2_cleanc_target_codegen_sort_other_shadowed_module_${os.getpid()}.v') |
| 1437 | os.write_file(other_file, ' |
| 1438 | module other |
| 1439 | |
| 1440 | pub struct Item { |
| 1441 | pub: |
| 1442 | value int |
| 1443 | } |
| 1444 | |
| 1445 | pub type ItemCallback = fn (&Item, &Item) int |
| 1446 | |
| 1447 | pub __global callback ItemCallback |
| 1448 | ') or { |
| 1449 | panic(err) |
| 1450 | } |
| 1451 | defer { |
| 1452 | os.rm(other_file) or {} |
| 1453 | } |
| 1454 | src := generated_c_for_target_program_with_extra_files('sort_local_selector_shadowed_module_no_fnsortcb_cast', ' |
| 1455 | module main |
| 1456 | |
| 1457 | import other |
| 1458 | |
| 1459 | type FnSortCB = fn (voidptr, voidptr) int |
| 1460 | |
| 1461 | type ItemCallback = fn (&other.Item, &other.Item) int |
| 1462 | |
| 1463 | struct Holder { |
| 1464 | callback ItemCallback |
| 1465 | } |
| 1466 | |
| 1467 | fn array__sort_with_compare(mut items []other.Item, callback FnSortCB) |
| 1468 | |
| 1469 | fn compare_items(a &other.Item, b &other.Item) int { |
| 1470 | return a.value - b.value |
| 1471 | } |
| 1472 | |
| 1473 | fn main() { |
| 1474 | mut items := [other.Item{value: 2}, other.Item{value: 1}] |
| 1475 | other := Holder{ |
| 1476 | callback: compare_items |
| 1477 | } |
| 1478 | array__sort_with_compare(mut items, other.callback) |
| 1479 | } |
| 1480 | ', |
| 1481 | 'linux', [], false, false, [other_file]) |
| 1482 | assert src.contains('array__sort_with_compare(&items, other.callback);') |
| 1483 | assert !src.contains('array__sort_with_compare(&items, (FnSortCB)other.callback);') |
| 1484 | assert !src.contains('(FnSortCB)other__callback') |
| 1485 | } |
| 1486 | |
| 1487 | fn test_sort_with_compare_selector_field_callback_is_not_cast_to_fnsortcb() { |
| 1488 | src := generated_c_for_target_program('sort_selector_field_callback_no_fnsortcb_cast', ' |
| 1489 | type FnSortCB = fn (voidptr, voidptr) int |
| 1490 | |
| 1491 | struct Item { |
| 1492 | value int |
| 1493 | } |
| 1494 | |
| 1495 | type ItemCallback = fn (&Item, &Item) int |
| 1496 | |
| 1497 | struct Holder { |
| 1498 | callback ItemCallback |
| 1499 | } |
| 1500 | |
| 1501 | fn array__sort_with_compare(mut items []Item, callback FnSortCB) |
| 1502 | |
| 1503 | fn compare_items(a &Item, b &Item) int { |
| 1504 | return a.value - b.value |
| 1505 | } |
| 1506 | |
| 1507 | fn main() { |
| 1508 | mut items := [Item{value: 2}, Item{value: 1}] |
| 1509 | holder := Holder{ |
| 1510 | callback: compare_items |
| 1511 | } |
| 1512 | array__sort_with_compare(mut items, holder.callback) |
| 1513 | } |
| 1514 | ') |
| 1515 | assert src.contains('array__sort_with_compare(&items, holder.callback);') |
| 1516 | assert !src.contains('array__sort_with_compare(&items, (FnSortCB)holder.callback);') |
| 1517 | assert !src.contains('(FnSortCB)holder__callback') |
| 1518 | } |
| 1519 | |
| 1520 | fn test_sorted_with_compare_named_comparator_casts_to_fnsortcb() { |
| 1521 | src := generated_c_for_target_program('sorted_named_comparator_fnsortcb_cast', ' |
| 1522 | type FnSortCB = fn (voidptr, voidptr) int |
| 1523 | |
| 1524 | struct Item { |
| 1525 | value int |
| 1526 | } |
| 1527 | |
| 1528 | fn array__sorted_with_compare(items []Item, callback FnSortCB) []Item |
| 1529 | |
| 1530 | fn compare_items(a &Item, b &Item) int { |
| 1531 | return a.value - b.value |
| 1532 | } |
| 1533 | |
| 1534 | fn main() { |
| 1535 | items := [Item{value: 2}, Item{value: 1}] |
| 1536 | sorted := array__sorted_with_compare(items, compare_items) |
| 1537 | _ = sorted |
| 1538 | } |
| 1539 | ') |
| 1540 | assert src.contains('array__sorted_with_compare(items, (FnSortCB)compare_items);') |
| 1541 | assert !src.contains('array__sorted_with_compare(items, compare_items);') |
| 1542 | } |
| 1543 | |
| 1544 | fn test_string_sort_helpers_cast_named_comparators_to_fnsortcb() { |
| 1545 | src := generated_c_for_target_program('string_sort_helpers_fnsortcb_cast', ' |
| 1546 | type FnSortCB = fn (voidptr, voidptr) int |
| 1547 | |
| 1548 | fn array__sort_with_compare(mut words []string, callback FnSortCB) |
| 1549 | |
| 1550 | fn compare_lower_strings(a &string, b &string) int { |
| 1551 | return 0 |
| 1552 | } |
| 1553 | |
| 1554 | fn compare_strings_by_len(a &string, b &string) int { |
| 1555 | return 0 |
| 1556 | } |
| 1557 | |
| 1558 | fn (mut words []string) sort_ignore_case() { |
| 1559 | array__sort_with_compare(mut words, compare_lower_strings) |
| 1560 | } |
| 1561 | |
| 1562 | fn (mut words []string) sort_by_len() { |
| 1563 | array__sort_with_compare(mut words, compare_strings_by_len) |
| 1564 | } |
| 1565 | |
| 1566 | fn main() { |
| 1567 | mut words := ["Beta", "alpha", "z"] |
| 1568 | words.sort_ignore_case() |
| 1569 | words.sort_by_len() |
| 1570 | } |
| 1571 | ') |
| 1572 | assert src.contains('Array_string__sort_ignore_case(&words);') |
| 1573 | assert src.contains('Array_string__sort_by_len(&words);') |
| 1574 | assert src.contains('array__sort_with_compare(words, (FnSortCB)compare_lower_strings);') |
| 1575 | assert src.contains('array__sort_with_compare(words, (FnSortCB)compare_strings_by_len);') |
| 1576 | assert !src.contains('array__sort_with_compare(words, compare_lower_strings);') |
| 1577 | assert !src.contains('array__sort_with_compare(words, compare_strings_by_len);') |
| 1578 | } |
| 1579 | |
| 1580 | fn test_sort_with_compare_capturing_fn_literal_casts_statement_expr_to_fnsortcb() { |
| 1581 | src := generated_c_for_target_program('sort_capturing_fn_literal_fnsortcb_cast', ' |
| 1582 | type FnSortCB = fn (voidptr, voidptr) int |
| 1583 | |
| 1584 | struct Item { |
| 1585 | value int |
| 1586 | } |
| 1587 | |
| 1588 | fn array__sort_with_compare(mut items []Item, callback FnSortCB) |
| 1589 | |
| 1590 | fn main() { |
| 1591 | bias := 1 |
| 1592 | mut items := [Item{value: 2}, Item{value: 1}] |
| 1593 | array__sort_with_compare(mut items, fn [bias] (a &Item, b &Item) int { |
| 1594 | return bias + a.value - b.value |
| 1595 | }) |
| 1596 | } |
| 1597 | ') |
| 1598 | assert src.contains('array__sort_with_compare(&items, (FnSortCB)({') |
| 1599 | assert src.contains('_anon_fn_') |
| 1600 | assert src.contains('_capture_0 = bias;') |
| 1601 | assert !src.contains('array__sort_with_compare(&items, ({') |
| 1602 | } |
| 1603 | |
| 1604 | fn test_sorted_with_compare_fn_literal_casts_to_fnsortcb() { |
| 1605 | src := generated_c_for_target_program('sorted_fn_literal_fnsortcb_cast', ' |
| 1606 | type FnSortCB = fn (voidptr, voidptr) int |
| 1607 | |
| 1608 | struct Item { |
| 1609 | value int |
| 1610 | } |
| 1611 | |
| 1612 | fn array__sorted_with_compare(items []Item, callback FnSortCB) []Item |
| 1613 | |
| 1614 | fn main() { |
| 1615 | items := [Item{value: 2}, Item{value: 1}] |
| 1616 | sorted := array__sorted_with_compare(items, fn (a &Item, b &Item) int { |
| 1617 | return a.value - b.value |
| 1618 | }) |
| 1619 | _ = sorted |
| 1620 | } |
| 1621 | ') |
| 1622 | assert src.contains('array__sorted_with_compare(items, (FnSortCB)_anon_fn_') |
| 1623 | assert !src.contains('array__sorted_with_compare(items, _anon_fn_') |
| 1624 | } |
| 1625 | |
| 1626 | fn test_sort_with_compare_nil_callback_is_not_cast_to_fnsortcb() { |
| 1627 | src := generated_c_for_target_program('sort_nil_callback_no_fnsortcb_cast', ' |
| 1628 | type FnSortCB = fn (voidptr, voidptr) int |
| 1629 | |
| 1630 | struct Item { |
| 1631 | value int |
| 1632 | } |
| 1633 | |
| 1634 | fn array__sort_with_compare(mut items []Item, callback FnSortCB) |
| 1635 | |
| 1636 | fn main() { |
| 1637 | mut items := [Item{value: 2}, Item{value: 1}] |
| 1638 | array__sort_with_compare(mut items, nil) |
| 1639 | } |
| 1640 | ') |
| 1641 | assert src.contains('array__sort_with_compare(&items, NULL);') |
| 1642 | assert !src.contains('(FnSortCB)nil') |
| 1643 | assert !src.contains('array__sort_with_compare(&items, (FnSortCB)') |
| 1644 | } |
| 1645 | |
| 1646 | fn test_non_sort_callback_arg_is_not_cast_to_fnsortcb() { |
| 1647 | src := generated_c_for_target_program('non_sort_callback_no_fnsortcb_cast', ' |
| 1648 | struct Item { |
| 1649 | value int |
| 1650 | } |
| 1651 | |
| 1652 | type ItemCallback = fn (&Item, &Item) int |
| 1653 | |
| 1654 | fn compare_items(a &Item, b &Item) int { |
| 1655 | return a.value - b.value |
| 1656 | } |
| 1657 | |
| 1658 | fn use_callback(callback ItemCallback) { |
| 1659 | _ = callback |
| 1660 | } |
| 1661 | |
| 1662 | fn main() { |
| 1663 | use_callback(compare_items) |
| 1664 | } |
| 1665 | ') |
| 1666 | assert src.contains('use_callback(compare_items);') |
| 1667 | assert !src.contains('(FnSortCB)compare_items') |
| 1668 | } |
| 1669 | |
| 1670 | fn test_non_sort_fn_literal_callback_arg_is_not_cast_to_fnsortcb() { |
| 1671 | src := generated_c_for_target_program('non_sort_fn_literal_callback_no_fnsortcb_cast', ' |
| 1672 | struct Item { |
| 1673 | value int |
| 1674 | } |
| 1675 | |
| 1676 | type ItemCallback = fn (&Item, &Item) int |
| 1677 | |
| 1678 | fn use_callback(callback ItemCallback) { |
| 1679 | _ = callback |
| 1680 | } |
| 1681 | |
| 1682 | fn main() { |
| 1683 | bias := 1 |
| 1684 | use_callback(fn [bias] (a &Item, b &Item) int { |
| 1685 | return bias + a.value - b.value |
| 1686 | }) |
| 1687 | } |
| 1688 | ') |
| 1689 | assert src.contains('use_callback(({') |
| 1690 | assert src.contains('_anon_fn_') |
| 1691 | assert src.contains('_capture_0 = bias;') |
| 1692 | assert !src.contains('(FnSortCB)') |
| 1693 | assert !src.contains('use_callback((FnSortCB)') |
| 1694 | } |
| 1695 | |
| 1696 | fn test_fn_pointer_alias_cast_from_voidptr_uses_alias_value_cast() { |
| 1697 | src := generated_c_for_target_program('fn_pointer_alias_voidptr_cast', ' |
| 1698 | module os |
| 1699 | |
| 1700 | type Signal = int |
| 1701 | type SignalHandler = fn (Signal) |
| 1702 | |
| 1703 | fn handler_from_ptr(prev_handler voidptr) !SignalHandler { |
| 1704 | return SignalHandler(prev_handler) |
| 1705 | } |
| 1706 | ') |
| 1707 | assert src.contains('_result_os__SignalHandler os__handler_from_ptr(void* prev_handler)') |
| 1708 | assert src.contains('os__SignalHandler _val = ((os__SignalHandler)(prev_handler));') |
| 1709 | assert !src.contains('os__SignalHandler _val = ((os__SignalHandler*)(prev_handler));') |
| 1710 | } |
| 1711 | |
| 1712 | fn test_option_fn_pointer_alias_cast_from_voidptr_uses_alias_value_cast() { |
| 1713 | src := generated_c_for_target_program('option_fn_pointer_alias_voidptr_cast', ' |
| 1714 | module os |
| 1715 | |
| 1716 | type Signal = int |
| 1717 | type SignalHandler = fn (Signal) |
| 1718 | |
| 1719 | fn handler_from_ptr(prev_handler voidptr) ?SignalHandler { |
| 1720 | return SignalHandler(prev_handler) |
| 1721 | } |
| 1722 | ') |
| 1723 | assert src.contains('_option_os__SignalHandler os__handler_from_ptr(void* prev_handler)') |
| 1724 | assert src.contains('os__SignalHandler _val = ((os__SignalHandler)(prev_handler));') |
| 1725 | assert !src.contains('os__SignalHandler _val = ((os__SignalHandler*)(prev_handler));') |
| 1726 | } |
| 1727 | |
| 1728 | fn test_explicit_pointer_to_fn_pointer_alias_cast_preserves_star() { |
| 1729 | src := generated_c_for_target_program('fn_pointer_alias_explicit_pointer_cast', ' |
| 1730 | module os |
| 1731 | |
| 1732 | type Signal = int |
| 1733 | type SignalHandler = fn (Signal) |
| 1734 | |
| 1735 | fn handler_ptr_from_ptr(prev_handler voidptr) &SignalHandler { |
| 1736 | return &SignalHandler(prev_handler) |
| 1737 | } |
| 1738 | ') |
| 1739 | assert src.contains('os__SignalHandler* os__handler_ptr_from_ptr(void* prev_handler)') |
| 1740 | assert src.contains('return ((os__SignalHandler*)(prev_handler));') |
| 1741 | assert !src.contains('return ((os__SignalHandler)(prev_handler));') |
| 1742 | } |
| 1743 | |
| 1744 | fn test_result_pointer_to_fn_pointer_alias_cast_preserves_payload_pointer() { |
| 1745 | src := generated_c_for_target_program('result_fn_pointer_alias_explicit_pointer_cast', ' |
| 1746 | module os |
| 1747 | |
| 1748 | type Signal = int |
| 1749 | type SignalHandler = fn (Signal) |
| 1750 | |
| 1751 | fn handler_ptr_from_ptr(prev_handler voidptr) !&SignalHandler { |
| 1752 | return &SignalHandler(prev_handler) |
| 1753 | } |
| 1754 | ') |
| 1755 | assert src.contains('_result_os__SignalHandlerptr os__handler_ptr_from_ptr(void* prev_handler)') |
| 1756 | assert src.contains('os__SignalHandler* _val = ((os__SignalHandler*)(prev_handler));') |
| 1757 | assert !src.contains('os__SignalHandler _val = ((os__SignalHandler)(prev_handler));') |
| 1758 | } |
| 1759 | |
| 1760 | fn test_option_pointer_to_fn_pointer_alias_cast_preserves_payload_pointer() { |
| 1761 | src := generated_c_for_target_program('option_fn_pointer_alias_explicit_pointer_cast', ' |
| 1762 | module os |
| 1763 | |
| 1764 | type Signal = int |
| 1765 | type SignalHandler = fn (Signal) |
| 1766 | |
| 1767 | fn handler_ptr_from_ptr(prev_handler voidptr) ?&SignalHandler { |
| 1768 | return &SignalHandler(prev_handler) |
| 1769 | } |
| 1770 | ') |
| 1771 | assert src.contains('_option_os__SignalHandlerptr os__handler_ptr_from_ptr(void* prev_handler)') |
| 1772 | assert src.contains('os__SignalHandler* _val = ((os__SignalHandler*)(prev_handler));') |
| 1773 | assert !src.contains('os__SignalHandler _val = ((os__SignalHandler)(prev_handler));') |
| 1774 | } |
| 1775 | |
| 1776 | fn test_non_fn_pointer_result_payload_keeps_pointer_cast() { |
| 1777 | src := generated_c_for_target_program('non_fn_pointer_result_payload', ' |
| 1778 | module os |
| 1779 | |
| 1780 | struct Payload { |
| 1781 | value int |
| 1782 | } |
| 1783 | |
| 1784 | fn payload_from_ptr(raw voidptr) !&Payload { |
| 1785 | return &Payload(raw) |
| 1786 | } |
| 1787 | ') |
| 1788 | assert src.contains('_result_os__Payloadptr os__payload_from_ptr(void* raw)') |
| 1789 | assert src.contains('os__Payload* _val = ((os__Payload*)(raw));') |
| 1790 | assert !src.contains('os__Payload _val = ((os__Payload)(raw));') |
| 1791 | } |
| 1792 | |
| 1793 | fn test_fn_pointer_alias_helper_has_no_suffix_fallback() { |
| 1794 | g := Gen{ |
| 1795 | fn_type_aliases: { |
| 1796 | 'os__SignalHandler': true |
| 1797 | } |
| 1798 | } |
| 1799 | assert g.c_type_is_fn_pointer_alias('os__SignalHandler') |
| 1800 | assert !g.c_type_is_fn_pointer_alias('other__SignalHandler') |
| 1801 | assert !g.c_type_is_fn_pointer_alias('os__SignalHandlerptr') |
| 1802 | assert !g.c_type_is_fn_pointer_alias('os__HandlerFn') |
| 1803 | assert g.result_value_type('_result_os__SignalHandlerptr') == 'os__SignalHandler*' |
| 1804 | assert g.result_value_type('_result_os__Payloadptr') == 'os__Payload*' |
| 1805 | } |
| 1806 | |
| 1807 | fn test_inter_module_non_fn_alias_cast_is_not_remapped_to_fn_alias_homonym() { |
| 1808 | foo_file := os.join_path(os.temp_dir(), |
| 1809 | 'v2_cleanc_target_codegen_signalhandler_foo_${os.getpid()}.v') |
| 1810 | bar_file := os.join_path(os.temp_dir(), |
| 1811 | 'v2_cleanc_target_codegen_signalhandler_bar_${os.getpid()}.v') |
| 1812 | os.write_file(foo_file, ' |
| 1813 | module foo |
| 1814 | |
| 1815 | pub type SignalHandler = fn (int) |
| 1816 | ') or { panic(err) } |
| 1817 | os.write_file(bar_file, ' |
| 1818 | module bar |
| 1819 | |
| 1820 | pub type SignalHandler = int |
| 1821 | ') or { panic(err) } |
| 1822 | defer { |
| 1823 | os.rm(foo_file) or {} |
| 1824 | os.rm(bar_file) or {} |
| 1825 | } |
| 1826 | src := generated_c_for_target_program_with_extra_files('inter_module_signalhandler_alias_collision', ' |
| 1827 | module main |
| 1828 | |
| 1829 | import bar |
| 1830 | import foo |
| 1831 | |
| 1832 | fn keep_foo(handler foo.SignalHandler) { |
| 1833 | _ = handler |
| 1834 | } |
| 1835 | |
| 1836 | fn cast_bar() bar.SignalHandler { |
| 1837 | return bar.SignalHandler(1) |
| 1838 | } |
| 1839 | ', |
| 1840 | 'linux', [], false, false, [foo_file, bar_file]) |
| 1841 | assert src.contains('typedef void (*foo__SignalHandler)(int);') |
| 1842 | assert src.contains('typedef int bar__SignalHandler;') |
| 1843 | assert src.contains('return ((bar__SignalHandler)(1));') |
| 1844 | assert !src.contains('return ((foo__SignalHandler)(1));') |
| 1845 | assert !src.contains('return ((os__SignalHandler)(1));') |
| 1846 | } |
| 1847 | |
| 1848 | fn test_module_local_non_fn_alias_cast_prefers_local_alias_over_main_fn_alias_homonym() { |
| 1849 | bar_file := os.join_path(os.temp_dir(), |
| 1850 | 'v2_cleanc_target_codegen_signalhandler_local_bar_${os.getpid()}.v') |
| 1851 | os.write_file(bar_file, ' |
| 1852 | module bar |
| 1853 | |
| 1854 | pub type SignalHandler = int |
| 1855 | |
| 1856 | pub fn cast_local() SignalHandler { |
| 1857 | return SignalHandler(1) |
| 1858 | } |
| 1859 | ') or { |
| 1860 | panic(err) |
| 1861 | } |
| 1862 | defer { |
| 1863 | os.rm(bar_file) or {} |
| 1864 | } |
| 1865 | src := generated_c_for_target_program_with_extra_files('module_local_signalhandler_alias_collision', ' |
| 1866 | module main |
| 1867 | |
| 1868 | type SignalHandler = fn (int) |
| 1869 | |
| 1870 | fn main() { |
| 1871 | } |
| 1872 | ', |
| 1873 | 'linux', [], false, false, [bar_file]) |
| 1874 | assert src.contains('typedef void (*SignalHandler)(int);') |
| 1875 | assert src.contains('typedef int bar__SignalHandler;') |
| 1876 | assert src.contains('return ((bar__SignalHandler)(1));') |
| 1877 | assert !src.contains('return ((SignalHandler)(1));') |
| 1878 | assert !src.contains('return ((foo__SignalHandler)(1));') |
| 1879 | } |
| 1880 | |
| 1881 | fn test_optional_sumtype_field_initializer_wraps_cast_value_as_option() { |
| 1882 | src := generated_c_for_target_program('optional_sumtype_field_initializer', ' |
| 1883 | type Type = Bool | Int |
| 1884 | |
| 1885 | struct Bool {} |
| 1886 | struct Int {} |
| 1887 | |
| 1888 | struct FnType { |
| 1889 | return_type ?Type |
| 1890 | } |
| 1891 | |
| 1892 | fn make_fn_type() FnType { |
| 1893 | return FnType{ |
| 1894 | return_type: Type(Bool{}) |
| 1895 | } |
| 1896 | } |
| 1897 | |
| 1898 | fn main() { |
| 1899 | _ = make_fn_type() |
| 1900 | } |
| 1901 | ') |
| 1902 | assert src.contains('struct FnType {\n\t_option_Type return_type;\n};') |
| 1903 | assert src.contains('.return_type = ({ _option_Type _opt = (_option_Type){ .state = 2 }; Type _val = ((Type){') |
| 1904 | assert src.contains('_option_ok(&_val, (_option*)&_opt, sizeof(_val)); _opt; })') |
| 1905 | assert !src.contains('.return_type = ((Type){') |
| 1906 | assert !src.contains('.return_type = ((main__Type){') |
| 1907 | } |
| 1908 | |
| 1909 | fn test_freestanding_minimal_preamble_avoids_implicit_os_runtime_headers() { |
| 1910 | src := preamble_for_freestanding_field('linux') |
| 1911 | assert src.contains('#include <stdbool.h>') |
| 1912 | assert src.contains('#include <stdint.h>') |
| 1913 | assert src.contains('#include <stddef.h>') |
| 1914 | assert src.contains('#include <string.h>') |
| 1915 | assert !src.contains('#include <stdio.h>') |
| 1916 | assert !src.contains('#include <stdlib.h>') |
| 1917 | assert !src.contains('#include <windows.h>') |
| 1918 | assert !src.contains('#include <dirent.h>') |
| 1919 | assert !src.contains('#include <pthread.h>') |
| 1920 | assert !src.contains('#include <mach/mach.h>') |
| 1921 | assert src.contains('typedef struct sync__RwMutex { u32 inited; } sync__RwMutex;') |
| 1922 | assert !src.contains('pthread_rwlock_t') |
| 1923 | } |
| 1924 | |
| 1925 | fn test_freestanding_full_preamble_avoids_implicit_os_runtime_headers() { |
| 1926 | src := full_preamble_for_freestanding_field('linux') |
| 1927 | assert src.contains('#include <stdbool.h>') |
| 1928 | assert src.contains('#include <stdint.h>') |
| 1929 | assert src.contains('#include <stddef.h>') |
| 1930 | assert src.contains('#include <string.h>') |
| 1931 | assert !src.contains('#include <unistd.h>') |
| 1932 | assert !src.contains('#include <windows.h>') |
| 1933 | assert !src.contains('#include <pthread.h>') |
| 1934 | assert !src.contains('#include <dirent.h>') |
| 1935 | assert !src.contains('#include <termios.h>') |
| 1936 | assert !src.contains('#include <sys/wait.h>') |
| 1937 | assert src.contains('typedef struct sync__RwMutex { u32 inited; } sync__RwMutex;') |
| 1938 | assert !src.contains('pthread_rwlock_t') |
| 1939 | } |
| 1940 | |
| 1941 | fn test_freestanding_field_full_preamble_avoids_implicit_os_runtime_headers() { |
| 1942 | src := full_preamble_for_freestanding_field('linux') |
| 1943 | assert src.contains('#include <stdbool.h>') |
| 1944 | assert src.contains('#include <stdint.h>') |
| 1945 | assert src.contains('#include <stddef.h>') |
| 1946 | assert src.contains('#include <string.h>') |
| 1947 | assert !src.contains('#include <unistd.h>') |
| 1948 | assert !src.contains('#include <windows.h>') |
| 1949 | assert !src.contains('#include <pthread.h>') |
| 1950 | assert !src.contains('#include <dirent.h>') |
| 1951 | assert !src.contains('#include <termios.h>') |
| 1952 | assert !src.contains('#include <sys/wait.h>') |
| 1953 | assert src.contains('typedef struct sync__RwMutex { u32 inited; } sync__RwMutex;') |
| 1954 | assert !src.contains('pthread_rwlock_t') |
| 1955 | } |
| 1956 | |
| 1957 | fn test_user_define_freestanding_does_not_enable_freestanding_codegen() { |
| 1958 | src := full_preamble_for_target('linux', ['freestanding']) |
| 1959 | assert src.contains('#include <unistd.h>') |
| 1960 | assert src.contains('#include <pthread.h>') |
| 1961 | assert src.contains('#include <stdio.h>') |
| 1962 | assert src.contains('#include <stdlib.h>') |
| 1963 | assert src.contains('pthread_rwlock_t') |
| 1964 | assert !src.contains('typedef struct sync__RwMutex { u32 inited; } sync__RwMutex;') |
| 1965 | } |
| 1966 | |
| 1967 | fn test_freestanding_none_preamble_avoids_implicit_os_runtime_headers() { |
| 1968 | src := full_preamble_for_options('none', [], true, false) |
| 1969 | assert src.contains('#include <stdbool.h>') |
| 1970 | assert src.contains('#include <stdint.h>') |
| 1971 | assert src.contains('#include <stddef.h>') |
| 1972 | assert src.contains('#include <string.h>') |
| 1973 | assert src.contains('typedef struct sync__RwMutex { u32 inited; } sync__RwMutex;') |
| 1974 | assert_no_os_runtime_headers(src) |
| 1975 | } |
| 1976 | |
| 1977 | fn test_freestanding_prealloc_preamble_avoids_implicit_free_contract() { |
| 1978 | freestanding_src := full_preamble_for_options('linux', [], true, true) |
| 1979 | assert !freestanding_src.contains('#define _VPREALLOC (1)') |
| 1980 | assert !freestanding_src.contains('static inline void _v_cfree') |
| 1981 | assert !freestanding_src.contains('#define free(p)') |
| 1982 | |
| 1983 | hosted_src := full_preamble_for_options('linux', [], false, true) |
| 1984 | assert hosted_src.contains('#define _VPREALLOC (1)') |
| 1985 | assert hosted_src.contains('static inline void _v_cfree(void *p) { free(p); }') |
| 1986 | assert hosted_src.contains('#define free(p) ((void)(p), (void)0)') |
| 1987 | } |
| 1988 | |
| 1989 | fn test_freestanding_platform_hooks_preamble_declares_requested_hooks_without_hosted_headers() { |
| 1990 | generic_src := full_preamble_for_options('linux', ['freestanding_hooks'], true, false) |
| 1991 | assert !generic_src.contains('v_platform_write') |
| 1992 | assert !generic_src.contains('v_platform_panic') |
| 1993 | assert !generic_src.contains('v_platform_malloc') |
| 1994 | |
| 1995 | spoofed_src := full_preamble_for_options('linux', ['freestanding_hooks', 'freestanding_output', |
| 1996 | 'freestanding_panic', 'freestanding_alloc'], true, false) |
| 1997 | assert !spoofed_src.contains('v_platform_write') |
| 1998 | assert !spoofed_src.contains('v_platform_panic') |
| 1999 | assert !spoofed_src.contains('v_platform_malloc') |
| 2000 | |
| 2001 | output_src := full_preamble_for_freestanding_hooks(['output']) |
| 2002 | assert output_src.contains('isize v_platform_write(int stream, const u8* buf, isize len);') |
| 2003 | assert !output_src.contains('v_platform_panic') |
| 2004 | assert !output_src.contains('v_platform_malloc') |
| 2005 | assert !output_src.contains('#include <stdio.h>') |
| 2006 | assert !output_src.contains('#include <stdlib.h>') |
| 2007 | assert !output_src.contains('#include <unistd.h>') |
| 2008 | |
| 2009 | panic_src := full_preamble_for_freestanding_hooks(['panic']) |
| 2010 | assert panic_src.contains('void v_platform_panic(const u8* msg, isize len);') |
| 2011 | assert !panic_src.contains('v_platform_write') |
| 2012 | assert !panic_src.contains('v_platform_malloc') |
| 2013 | |
| 2014 | alloc_src := full_preamble_for_freestanding_hooks(['alloc']) |
| 2015 | assert alloc_src.contains('void* v_platform_malloc(isize n);') |
| 2016 | assert alloc_src.contains('void* v_platform_realloc(void* ptr, isize n);') |
| 2017 | assert alloc_src.contains('void v_platform_free(void* ptr);') |
| 2018 | assert !alloc_src.contains('v_platform_write') |
| 2019 | assert !alloc_src.contains('v_platform_panic') |
| 2020 | |
| 2021 | minimal_src := full_preamble_for_freestanding_hooks(['output', 'panic', 'alloc']) |
| 2022 | assert minimal_src.contains('isize v_platform_write(int stream, const u8* buf, isize len);') |
| 2023 | assert minimal_src.contains('void v_platform_panic(const u8* msg, isize len);') |
| 2024 | assert minimal_src.contains('void* v_platform_malloc(isize n);') |
| 2025 | assert minimal_src.contains('void* v_platform_realloc(void* ptr, isize n);') |
| 2026 | assert minimal_src.contains('void v_platform_free(void* ptr);') |
| 2027 | assert !minimal_src.contains('#include <stdio.h>') |
| 2028 | assert !minimal_src.contains('#include <stdlib.h>') |
| 2029 | assert !minimal_src.contains('#include <unistd.h>') |
| 2030 | } |
| 2031 | |
| 2032 | fn test_windows_minimal_preamble_avoids_posix_headers() { |
| 2033 | src := preamble_for_target('windows', []) |
| 2034 | assert !src.contains('#include <dirent.h>') |
| 2035 | assert !src.contains('#include <pthread.h>') |
| 2036 | assert !src.contains('#include <mach/mach.h>') |
| 2037 | assert src.contains('#include <windows.h>') |
| 2038 | assert src.contains('typedef struct sync__RwMutex { SRWLOCK mutex; u32 inited; } sync__RwMutex;') |
| 2039 | assert src.contains('AcquireSRWLockExclusive') |
| 2040 | assert !src.contains('static inline void sync__RwMutex_lock(sync__RwMutex* m) { (void)m; }') |
| 2041 | assert !src.contains('pthread_rwlock_t') |
| 2042 | assert src.contains('#include <stdio.h>') |
| 2043 | assert src.contains('#include <stdlib.h>') |
| 2044 | } |
| 2045 | |
| 2046 | fn test_windows_full_preamble_avoids_posix_headers() { |
| 2047 | src := full_preamble_for_target('windows', []) |
| 2048 | assert !src.contains('#include <unistd.h>') |
| 2049 | assert !src.contains('#include <sys/wait.h>') |
| 2050 | assert !src.contains('#include <termios.h>') |
| 2051 | assert !src.contains('#include <sys/ioctl.h>') |
| 2052 | assert !src.contains('#include <dirent.h>') |
| 2053 | assert !src.contains('#include <pthread.h>') |
| 2054 | assert src.contains('#include <windows.h>') |
| 2055 | assert src.contains('typedef struct sync__RwMutex { SRWLOCK mutex; u32 inited; } sync__RwMutex;') |
| 2056 | assert src.contains('AcquireSRWLockExclusive') |
| 2057 | assert !src.contains('static inline void sync__RwMutex_lock(sync__RwMutex* m) { (void)m; }') |
| 2058 | assert !src.contains('pthread_rwlock_t') |
| 2059 | assert src.contains('#include <stdio.h>') |
| 2060 | assert src.contains('#include <stdlib.h>') |
| 2061 | assert src.contains('#include <time.h>') |
| 2062 | } |
| 2063 | |
| 2064 | fn test_minimal_generated_c_does_not_emit_unused_runtime_fallbacks() { |
| 2065 | csrc := generated_c_for_target_program('minimal_runtime_fallbacks', 'module main |
| 2066 | |
| 2067 | fn main() {} |
| 2068 | ') |
| 2069 | assert csrc.contains('int main(') |
| 2070 | assert_no_hosted_runtime_fallbacks(csrc) |
| 2071 | assert !csrc.contains('__v_live_init') |
| 2072 | } |
| 2073 | |
| 2074 | fn test_freestanding_skip_builtin_minimal_does_not_emit_hosted_runtime_fallbacks() { |
| 2075 | csrc := generated_c_for_target_program_with_options('freestanding_minimal_runtime_fallbacks', 'module main |
| 2076 | |
| 2077 | fn main() {} |
| 2078 | ', |
| 2079 | 'linux', true, true) |
| 2080 | assert csrc.contains('int main(') |
| 2081 | assert_no_hosted_runtime_fallbacks(csrc) |
| 2082 | assert !csrc.contains('__v_live_init') |
| 2083 | } |
| 2084 | |
| 2085 | fn test_live_reload_detects_live_functions_in_active_comptime_blocks() { |
| 2086 | csrc := generated_c_for_target_program('active_live_comptime', 'module main |
| 2087 | |
| 2088 | \$if linux { |
| 2089 | @[live] |
| 2090 | fn hot_reload() {} |
| 2091 | } |
| 2092 | |
| 2093 | fn main() {} |
| 2094 | ') |
| 2095 | assert csrc.contains('void __v_live_init(void);') |
| 2096 | assert csrc.contains('__v_live_init();') |
| 2097 | } |
| 2098 | |
| 2099 | fn test_live_reload_ignores_live_functions_in_inactive_comptime_blocks() { |
| 2100 | csrc := generated_c_for_target_program('inactive_live_comptime', 'module main |
| 2101 | |
| 2102 | \$if windows { |
| 2103 | @[live] |
| 2104 | fn hot_reload() {} |
| 2105 | } |
| 2106 | |
| 2107 | fn main() {} |
| 2108 | ') |
| 2109 | assert !csrc.contains('__v_live_init') |
| 2110 | } |
| 2111 | |
| 2112 | fn test_runtime_fallbacks_emit_arguments_only_when_referenced() { |
| 2113 | minimal_src := runtime_fallbacks_for_called_functions([]) |
| 2114 | assert !minimal_src.contains('__attribute__((weak)) Array_string arguments()') |
| 2115 | |
| 2116 | args_src := runtime_fallbacks_for_called_functions(['arguments']) |
| 2117 | assert args_src.contains('__attribute__((weak)) Array_string arguments()') |
| 2118 | assert !args_src.contains('__attribute__((weak)) void _write_buf_to_fd') |
| 2119 | } |
| 2120 | |
| 2121 | fn test_freestanding_without_alloc_hook_arguments_refuses_without_hosted_alloc() { |
| 2122 | src := runtime_fallbacks_for_called_functions_with_hooks(['arguments'], ['output']) |
| 2123 | assert src.contains('__attribute__((weak)) Array_string arguments()') |
| 2124 | assert !src.contains('__new_array_with_default_noscan') |
| 2125 | assert !src.contains('tos_clone') |
| 2126 | assert !src.contains('array__push') |
| 2127 | assert_freestanding_alloc_refusal_without_hosted_heap(src) |
| 2128 | } |
| 2129 | |
| 2130 | fn test_runtime_fallbacks_emit_stdout_helpers_only_when_printing_is_referenced() { |
| 2131 | print_src := runtime_fallbacks_for_called_functions(['println']) |
| 2132 | assert print_src.contains('__attribute__((weak)) void _write_buf_to_fd') |
| 2133 | assert print_src.contains('__attribute__((weak)) void _writeln_to_fd') |
| 2134 | assert print_src.contains('isize written = write(fd, ptr, remaining_bytes)') |
| 2135 | assert !print_src.contains('WriteFile(') |
| 2136 | assert !print_src.contains('__attribute__((weak)) Array_string arguments()') |
| 2137 | assert !print_src.contains('__attribute__((weak)) void eprint') |
| 2138 | assert !print_src.contains('__attribute__((weak)) void flush_stdout') |
| 2139 | assert !print_src.contains('__attribute__((weak)) void flush_stderr') |
| 2140 | } |
| 2141 | |
| 2142 | fn test_windows_runtime_fallbacks_use_writefile_for_stdout_helpers() { |
| 2143 | src := runtime_fallbacks_for_called_functions_for_target('windows', ['println']) |
| 2144 | assert src.contains('__attribute__((weak)) void _write_buf_to_fd') |
| 2145 | assert !src.contains('write(fd, ptr, remaining_bytes)') |
| 2146 | assert src.contains('GetStdHandle(fd == 2 ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE)') |
| 2147 | assert src.contains('WriteFile(handle, ptr, (DWORD)remaining_bytes, &win_written, NULL)') |
| 2148 | } |
| 2149 | |
| 2150 | fn test_cross_runtime_fallbacks_guard_windows_stdout_helpers() { |
| 2151 | src := runtime_fallbacks_for_called_functions_for_target('cross', ['println']) |
| 2152 | assert src.contains('__attribute__((weak)) void _write_buf_to_fd') |
| 2153 | assert src.contains('#if defined(_WIN32)') |
| 2154 | assert src.contains('GetStdHandle(fd == 2 ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE)') |
| 2155 | assert src.contains('WriteFile(handle, ptr, (DWORD)remaining_bytes, &win_written, NULL)') |
| 2156 | assert src.contains('#else\n\t\tisize written = write(fd, ptr, remaining_bytes);\n#endif') |
| 2157 | } |
| 2158 | |
| 2159 | fn test_runtime_fallbacks_emit_eprint_and_flush_only_when_referenced() { |
| 2160 | minimal_src := runtime_fallbacks_for_called_functions([]) |
| 2161 | assert !minimal_src.contains('__attribute__((weak)) void eprint') |
| 2162 | assert !minimal_src.contains('__attribute__((weak)) void flush_stdout') |
| 2163 | assert !minimal_src.contains('__attribute__((weak)) void flush_stderr') |
| 2164 | |
| 2165 | eprint_src := runtime_fallbacks_for_called_functions(['eprint']) |
| 2166 | assert eprint_src.contains('__attribute__((weak)) void eprint') |
| 2167 | assert eprint_src.contains('__attribute__((weak)) void _write_buf_to_fd') |
| 2168 | assert eprint_src.contains('__attribute__((weak)) void flush_stdout') |
| 2169 | assert eprint_src.contains('__attribute__((weak)) void flush_stderr') |
| 2170 | } |
| 2171 | |
| 2172 | fn test_runtime_fallbacks_emit_structural_helpers_only_when_referenced() { |
| 2173 | minimal_src := runtime_fallbacks_for_called_functions([]) |
| 2174 | assert_no_hosted_runtime_fallbacks(minimal_src) |
| 2175 | |
| 2176 | memdup_src := runtime_fallbacks_for_called_functions(['memdup']) |
| 2177 | assert memdup_src.contains('__attribute__((weak)) u8* malloc_noscan') |
| 2178 | assert memdup_src.contains('__attribute__((weak)) void* memdup') |
| 2179 | |
| 2180 | array_src := runtime_fallbacks_for_called_functions(['Array_string_contains', |
| 2181 | 'Array_string_index', 'Array_int_str']) |
| 2182 | assert array_src.contains('__attribute__((weak)) bool Array_string_contains') |
| 2183 | assert array_src.contains('__attribute__((weak)) int Array_string_index') |
| 2184 | assert array_src.contains('__attribute__((weak)) string Array_int_str') |
| 2185 | assert !array_src.contains('__attribute__((weak)) u8* malloc_noscan') |
| 2186 | |
| 2187 | bits_src := runtime_fallbacks_for_called_functions(['__at_least_one', 'bits__leading_zeros_64', |
| 2188 | 'bits__rotate_left_32', 'f32__str']) |
| 2189 | assert bits_src.contains('__attribute__((weak)) u64 __at_least_one') |
| 2190 | assert bits_src.contains('__attribute__((weak)) int bits__leading_zeros_64') |
| 2191 | assert bits_src.contains('__attribute__((weak)) u32 bits__rotate_left_32') |
| 2192 | assert bits_src.contains('__attribute__((weak)) string f64__str') |
| 2193 | assert bits_src.contains('__attribute__((weak)) string f32__str') |
| 2194 | } |
| 2195 | |
| 2196 | fn test_runtime_fallbacks_emit_float_strg_dependency_closure() { |
| 2197 | f32_strg_src := runtime_fallbacks_for_called_functions(['f32__strg']) |
| 2198 | f64_str_idx := f32_strg_src.index('__attribute__((weak)) string f64__str(f64 x)') or { |
| 2199 | panic('missing f64__str fallback') |
| 2200 | } |
| 2201 | f64_strg_idx := f32_strg_src.index('__attribute__((weak)) string f64__strg') or { |
| 2202 | panic('missing f64__strg fallback') |
| 2203 | } |
| 2204 | f32_strg_idx := f32_strg_src.index('__attribute__((weak)) string f32__strg') or { |
| 2205 | panic('missing f32__strg fallback') |
| 2206 | } |
| 2207 | assert f32_strg_src.contains('__attribute__((weak)) u8* malloc_noscan') |
| 2208 | assert f64_str_idx < f64_strg_idx |
| 2209 | assert f64_strg_idx < f32_strg_idx |
| 2210 | assert f32_strg_src.contains('\treturn f64__str(x);') |
| 2211 | assert f32_strg_src.contains('\treturn f64__strg((f64)x);') |
| 2212 | |
| 2213 | f64_strg_src := runtime_fallbacks_for_called_functions(['f64__strg']) |
| 2214 | f64_str_direct_idx := f64_strg_src.index('__attribute__((weak)) string f64__str(f64 x)') or { |
| 2215 | panic('missing f64__str fallback for f64__strg') |
| 2216 | } |
| 2217 | f64_strg_direct_idx := f64_strg_src.index('__attribute__((weak)) string f64__strg') or { |
| 2218 | panic('missing f64__strg fallback for direct reference') |
| 2219 | } |
| 2220 | assert f64_strg_src.contains('__attribute__((weak)) u8* malloc_noscan') |
| 2221 | assert f64_str_direct_idx < f64_strg_direct_idx |
| 2222 | |
| 2223 | existing_c_src := |
| 2224 | runtime_fallbacks_for_existing_c_source('void keep_generated_code(void) { f32__strg((f32)0); }') |
| 2225 | existing_f64_str_idx := existing_c_src.index('__attribute__((weak)) string f64__str(f64 x)') or { |
| 2226 | panic('missing f64__str fallback for existing C reference') |
| 2227 | } |
| 2228 | existing_f64_strg_idx := existing_c_src.index('__attribute__((weak)) string f64__strg') or { |
| 2229 | panic('missing f64__strg fallback for existing C reference') |
| 2230 | } |
| 2231 | existing_f32_strg_idx := existing_c_src.index('__attribute__((weak)) string f32__strg') or { |
| 2232 | panic('missing f32__strg fallback for existing C reference') |
| 2233 | } |
| 2234 | assert existing_f64_str_idx < existing_f64_strg_idx |
| 2235 | assert existing_f64_strg_idx < existing_f32_strg_idx |
| 2236 | } |
| 2237 | |
| 2238 | fn test_float_str_fallbacks_rely_on_kept_builtin_float_runtime() { |
| 2239 | for path in [ |
| 2240 | 'vlib/builtin/float.c.v', |
| 2241 | 'vlib/strconv/ftoa.c.v', |
| 2242 | 'vlib/strconv/f32_str.c.v', |
| 2243 | 'vlib/strconv/f64_str.c.v', |
| 2244 | ] { |
| 2245 | assert is_builtin_runtime_keep_file(path), path |
| 2246 | } |
| 2247 | } |
| 2248 | |
| 2249 | fn test_runtime_fallbacks_emit_structural_helpers_for_existing_c_references() { |
| 2250 | src := |
| 2251 | runtime_fallbacks_for_existing_c_source('void keep_generated_code(void) { memdup(0, 0); Array_string_contains((Array_string){0}, (string){0}); }') |
| 2252 | assert src.contains('void keep_generated_code(void)') |
| 2253 | assert src.contains('__attribute__((weak)) u8* malloc_noscan') |
| 2254 | assert src.contains('__attribute__((weak)) void* memdup') |
| 2255 | assert src.contains('__attribute__((weak)) bool Array_string_contains') |
| 2256 | assert !src.contains('__attribute__((weak)) bool Array_int_contains') |
| 2257 | } |
| 2258 | |
| 2259 | fn test_freestanding_output_hooks_runtime_fallbacks_avoid_hosted_io() { |
| 2260 | src := runtime_fallbacks_for_called_functions_with_hooks(['println', 'eprint'], [ |
| 2261 | 'output', |
| 2262 | ]) |
| 2263 | assert src.contains('__attribute__((weak)) void _write_buf_to_fd') |
| 2264 | assert src.contains('v_platform_write(fd, ptr, remaining_bytes)') |
| 2265 | assert src.contains('__attribute__((weak)) void _writeln_to_fd') |
| 2266 | assert src.contains('__attribute__((weak)) void eprint') |
| 2267 | assert src.contains('__attribute__((weak)) void flush_stdout') |
| 2268 | assert src.contains('__attribute__((weak)) void flush_stderr') |
| 2269 | assert_no_hosted_output_runtime(src) |
| 2270 | } |
| 2271 | |
| 2272 | fn test_freestanding_without_output_hook_runtime_fallbacks_refuse_without_hosted_io() { |
| 2273 | src := runtime_fallbacks_for_called_functions_with_hooks(['println', 'eprint'], [ |
| 2274 | 'panic', |
| 2275 | ]) |
| 2276 | assert src.contains('__attribute__((weak)) void _write_buf_to_fd') |
| 2277 | assert src.contains('__attribute__((weak)) void _writeln_to_fd') |
| 2278 | assert src.contains('__attribute__((weak)) void eprint') |
| 2279 | assert src.contains('__attribute__((weak)) void flush_stdout') |
| 2280 | assert src.contains('__attribute__((weak)) void flush_stderr') |
| 2281 | assert !src.contains('v_platform_write') |
| 2282 | assert_freestanding_output_refusal_without_hosted_io(src) |
| 2283 | } |
| 2284 | |
| 2285 | fn test_freestanding_spoofed_hook_defines_do_not_call_platform_hooks() { |
| 2286 | src := runtime_fallbacks_for_called_functions_with_options(['println', 'panic', 'memdup'], [ |
| 2287 | 'freestanding_hooks', |
| 2288 | 'freestanding_output', |
| 2289 | 'freestanding_panic', |
| 2290 | 'freestanding_alloc', |
| 2291 | ], true) |
| 2292 | assert !src.contains('v_platform_write') |
| 2293 | assert !src.contains('v_platform_panic') |
| 2294 | assert !src.contains('v_platform_malloc') |
| 2295 | assert_freestanding_alloc_refusal_without_hosted_heap(src) |
| 2296 | } |
| 2297 | |
| 2298 | fn test_freestanding_output_hook_non_string_print_refuses_without_hosted_formatting() { |
| 2299 | src := generated_c_for_target_program_with_hooks('freestanding_output_print_non_string', 'module main |
| 2300 | |
| 2301 | fn println(v int) {} |
| 2302 | |
| 2303 | fn main() { |
| 2304 | println(123) |
| 2305 | } |
| 2306 | ', [ |
| 2307 | 'output', |
| 2308 | ]) |
| 2309 | assert !src.contains('int__str(') |
| 2310 | assert_freestanding_format_refusal_without_hosted_formatting(src) |
| 2311 | } |
| 2312 | |
| 2313 | fn test_freestanding_output_hook_voidptr_print_refuses_without_hosted_formatting() { |
| 2314 | src := generated_c_for_target_program_with_hooks('freestanding_output_print_voidptr', 'module main |
| 2315 | |
| 2316 | fn print(v voidptr) {} |
| 2317 | |
| 2318 | fn main() { |
| 2319 | print(voidptr(0)) |
| 2320 | } |
| 2321 | ', [ |
| 2322 | 'output', |
| 2323 | ]) |
| 2324 | assert !src.contains('int__str(') |
| 2325 | assert_freestanding_format_refusal_without_hosted_formatting(src) |
| 2326 | } |
| 2327 | |
| 2328 | fn test_freestanding_string_interpolation_refuses_without_hosted_formatting() { |
| 2329 | src := generated_c_for_target_program_with_hooks('freestanding_string_interpolation', "module main |
| 2330 | |
| 2331 | fn main() { |
| 2332 | _ := '\${1}' |
| 2333 | } |
| 2334 | ", [ |
| 2335 | 'alloc', |
| 2336 | ]) |
| 2337 | assert !src.contains('snprintf(') |
| 2338 | assert !src.contains('memdup(') |
| 2339 | assert_freestanding_format_refusal_without_hosted_formatting(src) |
| 2340 | assert_no_raw_heap_runtime(src) |
| 2341 | } |
| 2342 | |
| 2343 | fn test_freestanding_alloc_hooks_runtime_fallbacks_avoid_hosted_alloc() { |
| 2344 | src := runtime_fallbacks_for_called_functions_with_hooks(['memdup', 'DenseArray__zeros_to_end'], [ |
| 2345 | 'alloc', |
| 2346 | ]) |
| 2347 | assert src.contains('__attribute__((weak)) u8* malloc_noscan') |
| 2348 | assert src.contains('return (u8*)v_platform_malloc(n);') |
| 2349 | assert src.contains('__attribute__((weak)) void* memdup') |
| 2350 | assert src.contains('void* res = v_platform_malloc(sz);') |
| 2351 | assert src.contains('__attribute__((weak)) void DenseArray__zeros_to_end') |
| 2352 | assert src.contains('void* tmp_value = v_platform_malloc(d->value_bytes);') |
| 2353 | assert src.contains('v_platform_free(tmp_value);') |
| 2354 | assert src.contains('v_platform_realloc(d->values, d->value_bytes * d->cap);') |
| 2355 | assert_no_hosted_alloc_runtime(src) |
| 2356 | assert_no_raw_heap_runtime(src) |
| 2357 | } |
| 2358 | |
| 2359 | fn test_freestanding_without_alloc_hook_heap_helpers_refuse_without_hosted_alloc() { |
| 2360 | g := new_target_test_gen_with_freestanding('linux', [ |
| 2361 | 'freestanding_hooks', |
| 2362 | 'freestanding_output', |
| 2363 | 'freestanding_panic', |
| 2364 | ], true) |
| 2365 | malloc_expr := g.c_heap_malloc_call('n') |
| 2366 | realloc_expr := g.c_heap_realloc_call('p', 'n') |
| 2367 | free_expr := g.c_heap_free_call('p') |
| 2368 | helper_src := '${malloc_expr}\n${realloc_expr}\n${free_expr}' |
| 2369 | assert helper_src.contains('(void*)0') |
| 2370 | assert helper_src.contains('(void)0') |
| 2371 | assert_freestanding_alloc_refusal_without_hosted_heap(helper_src) |
| 2372 | } |
| 2373 | |
| 2374 | fn test_freestanding_without_alloc_hook_runtime_heap_paths_refuse_without_hosted_alloc() { |
| 2375 | src := runtime_fallbacks_for_existing_c_source_with_hooks('void keep_generated_code(void) { memdup(0, 1); DenseArray__zeros_to_end(0); }', [ |
| 2376 | 'output', |
| 2377 | 'panic', |
| 2378 | ]) |
| 2379 | assert src.contains('__attribute__((weak)) void* memdup') |
| 2380 | assert src.contains('__attribute__((weak)) void DenseArray__zeros_to_end') |
| 2381 | assert src.contains('void* res = ({ _Static_assert') |
| 2382 | assert src.contains('d->values = ({ _Static_assert') |
| 2383 | assert_freestanding_alloc_refusal_without_hosted_heap(src) |
| 2384 | } |
| 2385 | |
| 2386 | fn test_freestanding_skip_builtin_runtime_heap_helpers_refuse_in_generated_c() { |
| 2387 | src := runtime_fallbacks_for_existing_c_source_with_hooks_skip_builtin('void keep_generated_code(void) { array__push(0, 0); string__plus((string){0}, (string){0}); new_map(); }', [ |
| 2388 | 'output', |
| 2389 | 'panic', |
| 2390 | 'alloc', |
| 2391 | ]) |
| 2392 | assert_freestanding_heap_runtime_refusal(src, 'array__push') |
| 2393 | assert_freestanding_heap_runtime_refusal(src, 'string__plus') |
| 2394 | assert_freestanding_heap_runtime_refusal(src, 'new_map') |
| 2395 | assert_no_raw_heap_runtime(src) |
| 2396 | } |
| 2397 | |
| 2398 | fn test_freestanding_skip_builtin_runtime_heap_helper_table_refuses_each_helper() { |
| 2399 | for helper in freestanding_heap_runtime_helper_names() { |
| 2400 | src := runtime_fallbacks_for_existing_c_source_with_hooks_skip_builtin('void keep_generated_code(void) { ${helper}(); }', [ |
| 2401 | 'output', |
| 2402 | 'panic', |
| 2403 | 'alloc', |
| 2404 | ]) |
| 2405 | assert_freestanding_heap_runtime_refusal(src, helper) |
| 2406 | assert !src.contains('__attribute__((weak))') |
| 2407 | } |
| 2408 | } |
| 2409 | |
| 2410 | fn test_runtime_symbol_scan_ignores_comments_and_string_literals() { |
| 2411 | src := runtime_fallbacks_for_existing_c_source_with_hooks_skip_builtin('void keep_generated_code(void) { |
| 2412 | const char* literal = "array__push(0, 0)"; |
| 2413 | /* array__push(0, 0); */ |
| 2414 | // &array__push |
| 2415 | array__push_many(0, 0, 0); |
| 2416 | }', [ |
| 2417 | 'output', |
| 2418 | 'panic', |
| 2419 | 'alloc', |
| 2420 | ]) |
| 2421 | assert !src.contains('_Static_assert(0, "${freestanding_missing_heap_runtime_message}: array__push")') |
| 2422 | assert_freestanding_heap_runtime_refusal(src, 'array__push_many') |
| 2423 | } |
| 2424 | |
| 2425 | fn test_runtime_symbol_scan_detects_bare_callback_identifier() { |
| 2426 | src := runtime_fallbacks_for_existing_c_source_with_hooks_skip_builtin('void keep_generated_code(void) { |
| 2427 | array__sort_with_compare(0, compare_strings); |
| 2428 | }', [ |
| 2429 | 'output', |
| 2430 | 'panic', |
| 2431 | 'alloc', |
| 2432 | ]) |
| 2433 | assert_freestanding_heap_runtime_refusal(src, 'array__sort_with_compare') |
| 2434 | assert_freestanding_heap_runtime_refusal(src, 'compare_strings') |
| 2435 | } |
| 2436 | |
| 2437 | fn test_freestanding_skip_builtin_structural_array_fallbacks_refuse_clearly() { |
| 2438 | for helper in ['Array_int_contains', 'Array_string_contains', 'Array_string_index', |
| 2439 | 'Array_int_str'] { |
| 2440 | src := runtime_fallbacks_for_existing_c_source_with_hooks_skip_builtin('void keep_generated_code(void) { ${helper}(); }', [ |
| 2441 | 'output', |
| 2442 | 'panic', |
| 2443 | 'alloc', |
| 2444 | ]) |
| 2445 | assert_freestanding_heap_runtime_refusal(src, helper) |
| 2446 | } |
| 2447 | } |
| 2448 | |
| 2449 | fn test_freestanding_skip_builtin_ierror_str_refuses_with_panic_hook() { |
| 2450 | src := runtime_fallbacks_for_existing_c_source_with_hooks_skip_builtin('void keep_generated_code(void) { v_panic(IError__str((IError){0})); }', [ |
| 2451 | 'output', |
| 2452 | 'panic', |
| 2453 | 'alloc', |
| 2454 | ]) |
| 2455 | assert src.contains('v_platform_panic(s.str, s.len);') |
| 2456 | assert_freestanding_heap_runtime_refusal(src, 'IError__str') |
| 2457 | } |
| 2458 | |
| 2459 | fn test_freestanding_skip_builtin_string_lt_from_v_code_refuses_runtime_helper() { |
| 2460 | src := generated_c_for_target_program_with_hooks('freestanding_string_lt_runtime_helper', 'module main |
| 2461 | |
| 2462 | fn main() { |
| 2463 | a := "a" |
| 2464 | b := "b" |
| 2465 | _ := a < b |
| 2466 | } |
| 2467 | ', [ |
| 2468 | 'output', |
| 2469 | 'panic', |
| 2470 | 'alloc', |
| 2471 | ]) |
| 2472 | assert_freestanding_heap_runtime_refusal(src, 'string__lt') |
| 2473 | } |
| 2474 | |
| 2475 | fn test_freestanding_skip_builtin_string_concat_from_v_code_refuses_runtime_helper() { |
| 2476 | src := generated_c_for_target_program_with_hooks('freestanding_string_concat_runtime_helper', 'module main |
| 2477 | |
| 2478 | fn main() { |
| 2479 | a := "a" |
| 2480 | b := "b" |
| 2481 | c := "c" |
| 2482 | _ := a + b + c |
| 2483 | } |
| 2484 | ', [ |
| 2485 | 'output', |
| 2486 | 'panic', |
| 2487 | 'alloc', |
| 2488 | ]) |
| 2489 | assert_freestanding_heap_runtime_refusal(src, 'string__plus_two') |
| 2490 | } |
| 2491 | |
| 2492 | fn test_freestanding_skip_builtin_string_method_runtime_helpers_refuse_in_generated_c() { |
| 2493 | src := runtime_fallbacks_for_existing_c_source_with_hooks_skip_builtin('void keep_generated_code(void) { |
| 2494 | string__contains((string){0}, (string){0}); |
| 2495 | string__starts_with((string){0}, (string){0}); |
| 2496 | string__ends_with((string){0}, (string){0}); |
| 2497 | string__clone((string){0}); |
| 2498 | string__split((string){0}, (string){0}); |
| 2499 | }', [ |
| 2500 | 'output', |
| 2501 | 'panic', |
| 2502 | 'alloc', |
| 2503 | ]) |
| 2504 | assert_freestanding_heap_runtime_refusal(src, 'string__contains') |
| 2505 | assert_freestanding_heap_runtime_refusal(src, 'string__starts_with') |
| 2506 | assert_freestanding_heap_runtime_refusal(src, 'string__ends_with') |
| 2507 | assert_freestanding_heap_runtime_refusal(src, 'string__clone') |
| 2508 | assert_freestanding_heap_runtime_refusal(src, 'string__split') |
| 2509 | } |
| 2510 | |
| 2511 | fn test_freestanding_skip_builtin_array_equality_from_v_code_refuses_runtime_helper() { |
| 2512 | src := generated_c_for_target_program_with_hooks('freestanding_array_eq_runtime_helper', 'module main |
| 2513 | |
| 2514 | fn main() { |
| 2515 | a := [1] |
| 2516 | b := [1] |
| 2517 | _ := a == b |
| 2518 | } |
| 2519 | ', [ |
| 2520 | 'output', |
| 2521 | 'panic', |
| 2522 | 'alloc', |
| 2523 | ]) |
| 2524 | assert_freestanding_heap_runtime_refusal(src, '__v2_array_eq') |
| 2525 | } |
| 2526 | |
| 2527 | fn test_freestanding_skip_builtin_array_sort_runtime_helper_refuses_in_generated_c() { |
| 2528 | src := runtime_fallbacks_for_existing_c_source_with_hooks_skip_builtin('void keep_generated_code(void) { array__sort(0, 0); array__sort_with_compare(0, 0); }', [ |
| 2529 | 'output', |
| 2530 | 'panic', |
| 2531 | 'alloc', |
| 2532 | ]) |
| 2533 | assert_freestanding_heap_runtime_refusal(src, 'array__sort') |
| 2534 | assert_freestanding_heap_runtime_refusal(src, 'array__sort_with_compare') |
| 2535 | } |
| 2536 | |
| 2537 | fn test_freestanding_skip_builtin_map_equality_from_v_code_refuses_runtime_helpers() { |
| 2538 | src := generated_c_for_target_program_with_hooks('freestanding_map_eq_runtime_helpers', 'module main |
| 2539 | |
| 2540 | fn main() { |
| 2541 | a := { |
| 2542 | "x": 1 |
| 2543 | } |
| 2544 | b := { |
| 2545 | "x": 1 |
| 2546 | } |
| 2547 | _ := a == b |
| 2548 | } |
| 2549 | ', [ |
| 2550 | 'output', |
| 2551 | 'panic', |
| 2552 | 'alloc', |
| 2553 | ]) |
| 2554 | assert_freestanding_heap_runtime_refusal(src, 'map__exists') |
| 2555 | assert_freestanding_heap_runtime_refusal(src, 'map__get') |
| 2556 | assert_freestanding_heap_runtime_refusal(src, 'DenseArray__has_index') |
| 2557 | assert_freestanding_heap_runtime_refusal(src, 'DenseArray__key') |
| 2558 | assert_freestanding_heap_runtime_refusal(src, 'DenseArray__value') |
| 2559 | } |
| 2560 | |
| 2561 | fn test_freestanding_skip_builtin_map_method_runtime_helpers_refuse_in_generated_c() { |
| 2562 | src := runtime_fallbacks_for_existing_c_source_with_hooks_skip_builtin('void keep_generated_code(void) { |
| 2563 | map__clear(0); |
| 2564 | map__delete(0, 0); |
| 2565 | map__keys(0); |
| 2566 | map__reserve(0, 0); |
| 2567 | map__move(0); |
| 2568 | map__values(0); |
| 2569 | }', [ |
| 2570 | 'output', |
| 2571 | 'panic', |
| 2572 | 'alloc', |
| 2573 | ]) |
| 2574 | assert_freestanding_heap_runtime_refusal(src, 'map__clear') |
| 2575 | assert_freestanding_heap_runtime_refusal(src, 'map__delete') |
| 2576 | assert_freestanding_heap_runtime_refusal(src, 'map__keys') |
| 2577 | assert_freestanding_heap_runtime_refusal(src, 'map__reserve') |
| 2578 | assert_freestanding_heap_runtime_refusal(src, 'map__move') |
| 2579 | assert_freestanding_heap_runtime_refusal(src, 'map__values') |
| 2580 | } |
| 2581 | |
| 2582 | fn test_freestanding_skip_builtin_generic_array_contains_from_v_code_refuses_runtime_helper() { |
| 2583 | src := generated_c_for_target_program_with_hooks('freestanding_generic_array_contains_runtime_helper', 'module main |
| 2584 | |
| 2585 | struct Point { |
| 2586 | x int |
| 2587 | } |
| 2588 | |
| 2589 | fn main() { |
| 2590 | points := [Point{ |
| 2591 | x: 1 |
| 2592 | }] |
| 2593 | needle := Point{ |
| 2594 | x: 1 |
| 2595 | } |
| 2596 | _ := needle in points |
| 2597 | } |
| 2598 | ', [ |
| 2599 | 'output', |
| 2600 | 'panic', |
| 2601 | 'alloc', |
| 2602 | ]) |
| 2603 | assert src.contains('_Static_assert(0, "${freestanding_missing_heap_runtime_message}: Array_') |
| 2604 | assert src.contains('_contains")') |
| 2605 | assert !src.contains('__attribute__((weak)) bool Array_') |
| 2606 | assert !src.contains('array__get(') |
| 2607 | } |
| 2608 | |
| 2609 | fn test_freestanding_skip_builtin_string_substr_or_runtime_helper_refuses_in_generated_c() { |
| 2610 | src := runtime_fallbacks_for_existing_c_source_with_hooks_skip_builtin('void keep_generated_code(void) { string__substr_or((string){0}, 0, 1, (string){0}); }', [ |
| 2611 | 'output', |
| 2612 | 'panic', |
| 2613 | 'alloc', |
| 2614 | ]) |
| 2615 | assert_freestanding_heap_runtime_refusal(src, 'string__substr_or') |
| 2616 | } |
| 2617 | |
| 2618 | fn test_freestanding_panic_hook_runtime_fallback_avoids_hosted_exit() { |
| 2619 | src := runtime_fallbacks_for_called_functions_with_hooks(['panic'], ['panic']) |
| 2620 | assert src.contains('__attribute__((weak)) void v_panic(string s)') |
| 2621 | assert src.contains('v_platform_panic(s.str, s.len);') |
| 2622 | assert src.contains('for (;;) {}') |
| 2623 | assert !src.contains('C.exit') |
| 2624 | assert !src.contains('exit(') |
| 2625 | } |
| 2626 | |
| 2627 | fn test_freestanding_without_panic_hook_runtime_fallback_refuses_without_unresolved_symbol() { |
| 2628 | src := runtime_fallbacks_for_called_functions_with_hooks(['panic'], ['output']) |
| 2629 | assert src.contains('__attribute__((weak)) void v_panic(string s)') |
| 2630 | assert src.contains('for (;;) {}') |
| 2631 | assert_freestanding_panic_refusal_without_hosted_exit(src) |
| 2632 | } |
| 2633 | |
| 2634 | fn test_freestanding_hooks_for_existing_c_references_do_not_drop_generated_c() { |
| 2635 | src := runtime_fallbacks_for_existing_c_source_with_hooks('void keep_generated_code(void) { v_panic((string){0}); memdup(0, 1); }', [ |
| 2636 | 'output', |
| 2637 | 'panic', |
| 2638 | 'alloc', |
| 2639 | ]) |
| 2640 | assert src.contains('void keep_generated_code(void)') |
| 2641 | assert src.contains('v_platform_panic(s.str, s.len);') |
| 2642 | assert src.contains('void* res = v_platform_malloc(sz);') |
| 2643 | assert_no_hosted_alloc_runtime(src) |
| 2644 | assert_no_raw_heap_runtime(src) |
| 2645 | } |
| 2646 | |
| 2647 | fn test_freestanding_alloc_hook_generated_c_heap_address_uses_platform_alloc() { |
| 2648 | src := generated_c_for_target_program_with_hooks('freestanding_alloc_heap_address', 'module main |
| 2649 | |
| 2650 | struct Point { |
| 2651 | x int |
| 2652 | } |
| 2653 | |
| 2654 | fn main() { |
| 2655 | p := &Point{ |
| 2656 | x: 1 |
| 2657 | } |
| 2658 | _ := p.x |
| 2659 | } |
| 2660 | ', [ |
| 2661 | 'alloc', |
| 2662 | ]) |
| 2663 | assert src.contains('v_platform_malloc(sizeof(Point))') |
| 2664 | assert_no_raw_heap_runtime(src) |
| 2665 | } |
| 2666 | |
| 2667 | fn test_freestanding_without_alloc_hook_heap_address_refuses_without_hosted_alloc() { |
| 2668 | src := generated_c_for_target_program_with_hooks('freestanding_missing_alloc_heap_address', 'module main |
| 2669 | |
| 2670 | struct Point { |
| 2671 | x int |
| 2672 | } |
| 2673 | |
| 2674 | fn main() { |
| 2675 | p := &Point{ |
| 2676 | x: 1 |
| 2677 | } |
| 2678 | _ := p.x |
| 2679 | } |
| 2680 | ', [ |
| 2681 | 'output', |
| 2682 | 'panic', |
| 2683 | ]) |
| 2684 | assert_freestanding_alloc_refusal_without_hosted_heap(src) |
| 2685 | } |
| 2686 | |
| 2687 | fn test_freestanding_without_alloc_hook_returned_local_clone_refuses_without_hosted_alloc() { |
| 2688 | src := generated_c_for_target_program_with_hooks('freestanding_missing_alloc_returned_local_clone', 'module main |
| 2689 | |
| 2690 | struct Point { |
| 2691 | x int |
| 2692 | } |
| 2693 | |
| 2694 | fn make() &Point { |
| 2695 | value := Point{ |
| 2696 | x: 1 |
| 2697 | } |
| 2698 | ptr := &value |
| 2699 | return ptr |
| 2700 | } |
| 2701 | |
| 2702 | fn main() { |
| 2703 | p := make() |
| 2704 | _ := p.x |
| 2705 | } |
| 2706 | ', [ |
| 2707 | 'output', |
| 2708 | 'panic', |
| 2709 | ]) |
| 2710 | assert_freestanding_alloc_refusal_without_hosted_heap(src) |
| 2711 | } |
| 2712 | |
| 2713 | fn test_freestanding_alloc_hook_interface_heap_cast_uses_platform_alloc() { |
| 2714 | src := generated_c_for_target_program_with_hooks('freestanding_alloc_interface_heap_cast', 'module main |
| 2715 | |
| 2716 | interface Runner { |
| 2717 | next() int |
| 2718 | } |
| 2719 | |
| 2720 | struct Concrete {} |
| 2721 | |
| 2722 | fn (mut c Concrete) next() int { |
| 2723 | _ = c |
| 2724 | return 7 |
| 2725 | } |
| 2726 | |
| 2727 | fn make() &Runner { |
| 2728 | mut c := Concrete{} |
| 2729 | return &Runner(c) |
| 2730 | } |
| 2731 | |
| 2732 | fn main() { |
| 2733 | r := make() |
| 2734 | _ := r.next() |
| 2735 | } |
| 2736 | ', [ |
| 2737 | 'alloc', |
| 2738 | ]) |
| 2739 | assert src.contains('Runner* _iface_t = (Runner*)v_platform_malloc(sizeof(Runner))') |
| 2740 | assert_no_raw_heap_runtime(src) |
| 2741 | } |
| 2742 | |
| 2743 | fn test_freestanding_without_alloc_hook_interface_heap_cast_refuses_without_hosted_alloc() { |
| 2744 | src := generated_c_for_target_program_with_hooks('freestanding_missing_alloc_interface_heap_cast', 'module main |
| 2745 | |
| 2746 | interface Runner { |
| 2747 | next() int |
| 2748 | } |
| 2749 | |
| 2750 | struct Concrete {} |
| 2751 | |
| 2752 | fn (mut c Concrete) next() int { |
| 2753 | _ = c |
| 2754 | return 7 |
| 2755 | } |
| 2756 | |
| 2757 | fn make() &Runner { |
| 2758 | mut c := Concrete{} |
| 2759 | return &Runner(c) |
| 2760 | } |
| 2761 | |
| 2762 | fn main() { |
| 2763 | r := make() |
| 2764 | _ := r.next() |
| 2765 | } |
| 2766 | ', [ |
| 2767 | 'output', |
| 2768 | 'panic', |
| 2769 | ]) |
| 2770 | assert_freestanding_alloc_refusal_without_hosted_heap(src) |
| 2771 | } |
| 2772 | |
| 2773 | fn test_freestanding_alloc_hook_soa_helpers_use_platform_alloc() { |
| 2774 | src := soa_companion_for_hooks(['alloc']) |
| 2775 | assert src.contains('v_platform_malloc(cap * sizeof(int))') |
| 2776 | assert src.contains('memset(soa.x, 0, cap * sizeof(int));') |
| 2777 | assert src.contains('v_platform_realloc(soa->x, new_cap * sizeof(int))') |
| 2778 | assert src.contains('v_platform_free(soa->x);') |
| 2779 | assert_no_raw_heap_runtime(src) |
| 2780 | } |
| 2781 | |
| 2782 | fn test_freestanding_without_alloc_hook_soa_helpers_refuse_without_hosted_alloc() { |
| 2783 | src := soa_companion_for_hooks(['output', 'panic']) |
| 2784 | assert src.contains('soa.x = (int*)({ _Static_assert') |
| 2785 | assert src.contains('soa->x = (int*)({ _Static_assert') |
| 2786 | assert src.contains('({ _Static_assert(0, "${freestanding_missing_alloc_hook_message}"); (void)0; });') |
| 2787 | assert_freestanding_alloc_refusal_without_hosted_heap(src) |
| 2788 | } |
| 2789 | |
| 2790 | fn test_freestanding_minimal_hooks_runtime_and_heap_paths_avoid_raw_alloc() { |
| 2791 | hooks := ['output', 'panic', 'alloc'] |
| 2792 | runtime_src := runtime_fallbacks_for_existing_c_source_with_hooks('void keep_generated_code(void) { v_panic((string){0}); memdup(0, 1); DenseArray__zeros_to_end(0); }', |
| 2793 | hooks) |
| 2794 | assert runtime_src.contains('v_platform_panic(s.str, s.len);') |
| 2795 | assert runtime_src.contains('v_platform_malloc(sz);') |
| 2796 | assert runtime_src.contains('v_platform_realloc(d->values, d->value_bytes * d->cap);') |
| 2797 | assert_no_raw_heap_runtime(runtime_src) |
| 2798 | |
| 2799 | soa_src := soa_companion_for_hooks(hooks) |
| 2800 | assert soa_src.contains('v_platform_malloc(cap * sizeof(int))') |
| 2801 | assert soa_src.contains('v_platform_realloc(soa->x, new_cap * sizeof(int))') |
| 2802 | assert_no_raw_heap_runtime(soa_src) |
| 2803 | } |
| 2804 | |
| 2805 | fn test_freestanding_fixed_array_voidptr_stringification_avoids_hosted_formatting() { |
| 2806 | src := fixed_array_voidptr_str_write_for_hooks(['alloc']) |
| 2807 | assert src.contains('uintptr_t _addr = (uintptr_t)(items[0]);') |
| 2808 | assert src.contains('"0123456789abcdef"') |
| 2809 | assert src.contains('strings__Builder__write_string(&sb') |
| 2810 | assert_no_hosted_formatting_or_abort_runtime(src) |
| 2811 | } |
| 2812 | |
| 2813 | fn test_freestanding_unresolved_generic_stub_uses_panic_hook_without_hosted_abort() { |
| 2814 | src := unresolved_generic_stub_for_hooks(['panic']) |
| 2815 | assert src.contains('v_platform_panic((const u8*)') |
| 2816 | assert src.contains('for (;;) {}') |
| 2817 | assert !src.contains('#error') |
| 2818 | assert_no_hosted_formatting_or_abort_runtime(src) |
| 2819 | } |
| 2820 | |
| 2821 | fn test_freestanding_unresolved_generic_stub_without_panic_hook_refuses_without_hosted_abort() { |
| 2822 | src := unresolved_generic_stub_for_hooks(['output']) |
| 2823 | assert src.contains('#error "v2: unresolved generic stub requires freestanding panic platform hook"') |
| 2824 | assert_no_hosted_formatting_or_abort_runtime(src) |
| 2825 | } |
| 2826 | |