v / vlib / builtin
Raw file | 726 loc (686 sloc) | 17.38 KB | Latest commit hash 90941b3b1
1[has_globals]
2module builtin
3
4type FnExitCb = fn ()
5
6fn C.atexit(f FnExitCb) int
7fn C.strerror(int) &char
8
9[noreturn]
10fn vhalt() {
11 for {}
12}
13
14[markused]
15fn v_segmentation_fault_handler(signal_number int) {
16 $if freestanding {
17 eprintln('signal 11: segmentation fault')
18 } $else {
19 C.fprintf(C.stderr, c'signal %d: segmentation fault\n', signal_number)
20 }
21 $if use_libbacktrace ? {
22 eprint_libbacktrace(1)
23 } $else {
24 print_backtrace()
25 }
26 exit(128 + signal_number)
27}
28
29// exit terminates execution immediately and returns exit `code` to the shell.
30[noreturn]
31pub fn exit(code int) {
32 C.exit(code)
33}
34
35fn vcommithash() string {
36 return unsafe { tos5(&char(C.V_CURRENT_COMMIT_HASH)) }
37}
38
39// panic_debug private function that V uses for panics, -cg/-g is passed
40// recent versions of tcc print nicer backtraces automatically
41// Note: the duplication here is because tcc_backtrace should be called directly
42// inside the panic functions.
43[noreturn]
44fn panic_debug(line_no int, file string, mod string, fn_name string, s string) {
45 // Note: the order here is important for a stabler test output
46 // module is less likely to change than function, etc...
47 // During edits, the line number will change most frequently,
48 // so it is last
49 $if freestanding {
50 bare_panic(s)
51 } $else {
52 eprintln('================ V panic ================')
53 eprintln(' module: ${mod}')
54 eprintln(' function: ${fn_name}()')
55 eprintln(' message: ${s}')
56 eprintln(' file: ${file}:${line_no}')
57 eprintln(' v hash: ${vcommithash()}')
58 eprintln('=========================================')
59 $if exit_after_panic_message ? {
60 C.exit(1)
61 } $else $if no_backtrace ? {
62 C.exit(1)
63 } $else {
64 $if tinyc {
65 $if panics_break_into_debugger ? {
66 break_if_debugger_attached()
67 } $else {
68 C.tcc_backtrace(c'Backtrace')
69 }
70 C.exit(1)
71 }
72 $if use_libbacktrace ? {
73 eprint_libbacktrace(1)
74 } $else {
75 print_backtrace_skipping_top_frames(1)
76 }
77 $if panics_break_into_debugger ? {
78 break_if_debugger_attached()
79 }
80 C.exit(1)
81 }
82 }
83 vhalt()
84}
85
86// panic_option_not_set is called by V, when you use option error propagation in your main function.
87// It ends the program with a panic.
88[noreturn]
89pub fn panic_option_not_set(s string) {
90 panic('option not set (${s})')
91}
92
93// panic_result_not_set is called by V, when you use result error propagation in your main function
94// It ends the program with a panic.
95[noreturn]
96pub fn panic_result_not_set(s string) {
97 panic('result not set (${s})')
98}
99
100// panic prints a nice error message, then exits the process with exit code of 1.
101// It also shows a backtrace on most platforms.
102[noreturn]
103pub fn panic(s string) {
104 $if freestanding {
105 bare_panic(s)
106 } $else {
107 eprint('V panic: ')
108 eprintln(s)
109 eprintln('v hash: ${vcommithash()}')
110 $if exit_after_panic_message ? {
111 C.exit(1)
112 } $else $if no_backtrace ? {
113 C.exit(1)
114 } $else {
115 $if tinyc {
116 $if panics_break_into_debugger ? {
117 break_if_debugger_attached()
118 } $else {
119 C.tcc_backtrace(c'Backtrace')
120 }
121 C.exit(1)
122 }
123 $if use_libbacktrace ? {
124 eprint_libbacktrace(1)
125 } $else {
126 print_backtrace_skipping_top_frames(1)
127 }
128 $if panics_break_into_debugger ? {
129 break_if_debugger_attached()
130 }
131 C.exit(1)
132 }
133 }
134 vhalt()
135}
136
137// return a C-API error message matching to `errnum`
138pub fn c_error_number_str(errnum int) string {
139 mut err_msg := ''
140 $if freestanding {
141 err_msg = 'error ${errnum}'
142 } $else {
143 $if !vinix {
144 c_msg := C.strerror(errnum)
145 err_msg = string{
146 str: &u8(c_msg)
147 len: unsafe { C.strlen(c_msg) }
148 is_lit: 1
149 }
150 }
151 }
152 return err_msg
153}
154
155// panic with a C-API error message matching `errnum`
156[noreturn]
157pub fn panic_error_number(basestr string, errnum int) {
158 panic(basestr + c_error_number_str(errnum))
159}
160
161// eprintln prints a message with a line end, to stderr. Both stderr and stdout are flushed.
162pub fn eprintln(s string) {
163 if s.str == 0 {
164 eprintln('eprintln(NIL)')
165 return
166 }
167 $if freestanding {
168 // flushing is only a thing with C.FILE from stdio.h, not on the syscall level
169 bare_eprint(s.str, u64(s.len))
170 bare_eprint(c'\n', 1)
171 } $else $if ios {
172 C.WrappedNSLog(s.str)
173 } $else {
174 C.fflush(C.stdout)
175 C.fflush(C.stderr)
176 // eprintln is used in panics, so it should not fail at all
177 $if android && !termux {
178 C.android_print(C.stderr, c'%.*s\n', s.len, s.str)
179 }
180 _writeln_to_fd(2, s)
181 C.fflush(C.stderr)
182 }
183}
184
185// eprint prints a message to stderr. Both stderr and stdout are flushed.
186pub fn eprint(s string) {
187 if s.str == 0 {
188 eprint('eprint(NIL)')
189 return
190 }
191 $if freestanding {
192 // flushing is only a thing with C.FILE from stdio.h, not on the syscall level
193 bare_eprint(s.str, u64(s.len))
194 } $else $if ios {
195 // TODO: Implement a buffer as NSLog doesn't have a "print"
196 C.WrappedNSLog(s.str)
197 } $else {
198 C.fflush(C.stdout)
199 C.fflush(C.stderr)
200 $if android && !termux {
201 C.android_print(C.stderr, c'%.*s', s.len, s.str)
202 }
203 _write_buf_to_fd(2, s.str, s.len)
204 C.fflush(C.stderr)
205 }
206}
207
208pub fn flush_stdout() {
209 $if freestanding {
210 not_implemented := 'flush_stdout is not implemented\n'
211 bare_eprint(not_implemented.str, u64(not_implemented.len))
212 } $else {
213 C.fflush(C.stdout)
214 }
215}
216
217pub fn flush_stderr() {
218 $if freestanding {
219 not_implemented := 'flush_stderr is not implemented\n'
220 bare_eprint(not_implemented.str, u64(not_implemented.len))
221 } $else {
222 C.fflush(C.stderr)
223 }
224}
225
226// print prints a message to stdout. Note that unlike `eprint`, stdout is not automatically flushed.
227[manualfree]
228pub fn print(s string) {
229 $if android && !termux {
230 C.android_print(C.stdout, c'%.*s\n', s.len, s.str)
231 } $else $if ios {
232 // TODO: Implement a buffer as NSLog doesn't have a "print"
233 C.WrappedNSLog(s.str)
234 } $else $if freestanding {
235 bare_print(s.str, u64(s.len))
236 } $else {
237 _write_buf_to_fd(1, s.str, s.len)
238 }
239}
240
241// println prints a message with a line end, to stdout. Note that unlike `eprintln`, stdout is not automatically flushed.
242[manualfree]
243pub fn println(s string) {
244 if s.str == 0 {
245 println('println(NIL)')
246 return
247 }
248 $if android && !termux {
249 C.android_print(C.stdout, c'%.*s\n', s.len, s.str)
250 return
251 } $else $if ios {
252 C.WrappedNSLog(s.str)
253 return
254 } $else $if freestanding {
255 bare_print(s.str, u64(s.len))
256 bare_print(c'\n', 1)
257 return
258 } $else {
259 _writeln_to_fd(1, s)
260 }
261}
262
263[manualfree]
264fn _writeln_to_fd(fd int, s string) {
265 $if !bultin_writeln_should_write_at_once ? {
266 lf := u8(`\n`)
267 _write_buf_to_fd(fd, s.str, s.len)
268 _write_buf_to_fd(fd, &lf, 1)
269 return
270 }
271 unsafe {
272 buf_len := s.len + 1 // space for \n
273 mut buf := malloc(buf_len)
274 defer {
275 free(buf)
276 }
277 C.memcpy(buf, s.str, s.len)
278 buf[s.len] = `\n`
279 _write_buf_to_fd(fd, buf, buf_len)
280 }
281}
282
283[manualfree]
284fn _write_buf_to_fd(fd int, buf &u8, buf_len int) {
285 if buf_len <= 0 {
286 return
287 }
288 mut ptr := unsafe { buf }
289 mut remaining_bytes := isize(buf_len)
290 mut x := isize(0)
291 $if freestanding || vinix || bultin_write_buf_to_fd_should_use_c_write ? {
292 unsafe {
293 for remaining_bytes > 0 {
294 x = C.write(fd, ptr, remaining_bytes)
295 ptr += x
296 remaining_bytes -= x
297 }
298 }
299 } $else {
300 mut stream := voidptr(C.stdout)
301 if fd == 2 {
302 stream = voidptr(C.stderr)
303 }
304 unsafe {
305 for remaining_bytes > 0 {
306 x = isize(C.fwrite(ptr, 1, remaining_bytes, stream))
307 ptr += x
308 remaining_bytes -= x
309 }
310 }
311 }
312}
313
314__global total_m = i64(0)
315// malloc dynamically allocates a `n` bytes block of memory on the heap.
316// malloc returns a `byteptr` pointing to the memory address of the allocated space.
317// unlike the `calloc` family of functions - malloc will not zero the memory block.
318[unsafe]
319pub fn malloc(n isize) &u8 {
320 $if trace_malloc ? {
321 total_m += n
322 C.fprintf(C.stderr, c'_v_malloc %6d total %10d\n', n, total_m)
323 // print_backtrace()
324 }
325 if n < 0 {
326 panic('malloc(${n} < 0)')
327 }
328 $if vplayground ? {
329 if n > 10000 {
330 panic('allocating more than 10 KB at once is not allowed in the V playground')
331 }
332 if total_m > 50 * 1024 * 1024 {
333 panic('allocating more than 50 MB is not allowed in the V playground')
334 }
335 }
336 mut res := &u8(0)
337 $if prealloc {
338 return unsafe { prealloc_malloc(n) }
339 } $else $if gcboehm ? {
340 unsafe {
341 res = C.GC_MALLOC(n)
342 }
343 } $else $if freestanding {
344 // todo: is this safe to call malloc there? We export __malloc as malloc and it uses dlmalloc behind the scenes
345 // so theoretically it is safe
346 res = unsafe { __malloc(usize(n)) }
347 } $else {
348 res = unsafe { C.malloc(n) }
349 }
350 if res == 0 {
351 panic('malloc(${n}) failed')
352 }
353 $if debug_malloc ? {
354 // Fill in the memory with something != 0 i.e. `M`, so it is easier to spot
355 // when the calling code wrongly relies on it being zeroed.
356 unsafe { C.memset(res, 0x4D, n) }
357 }
358 return res
359}
360
361[unsafe]
362pub fn malloc_noscan(n isize) &u8 {
363 $if trace_malloc ? {
364 total_m += n
365 C.fprintf(C.stderr, c'malloc_noscan %6d total %10d\n', n, total_m)
366 // print_backtrace()
367 }
368 if n < 0 {
369 panic('malloc_noscan(${n} < 0)')
370 }
371 $if vplayground ? {
372 if n > 10000 {
373 panic('allocating more than 10 KB at once is not allowed in the V playground')
374 }
375 if total_m > 50 * 1024 * 1024 {
376 panic('allocating more than 50 MB is not allowed in the V playground')
377 }
378 }
379 mut res := &u8(0)
380 $if prealloc {
381 return unsafe { prealloc_malloc(n) }
382 } $else $if gcboehm ? {
383 $if gcboehm_opt ? {
384 unsafe {
385 res = C.GC_MALLOC_ATOMIC(n)
386 }
387 } $else {
388 unsafe {
389 res = C.GC_MALLOC(n)
390 }
391 }
392 } $else $if freestanding {
393 res = unsafe { __malloc(usize(n)) }
394 } $else {
395 res = unsafe { C.malloc(n) }
396 }
397 if res == 0 {
398 panic('malloc_noscan(${n}) failed')
399 }
400 $if debug_malloc ? {
401 // Fill in the memory with something != 0 i.e. `M`, so it is easier to spot
402 // when the calling code wrongly relies on it being zeroed.
403 unsafe { C.memset(res, 0x4D, n) }
404 }
405 return res
406}
407
408[inline]
409fn __at_least_one(how_many u64) u64 {
410 // handle the case for allocating memory for empty structs, which have sizeof(EmptyStruct) == 0
411 // in this case, just allocate a single byte, avoiding the panic for malloc(0)
412 if how_many == 0 {
413 return 1
414 }
415 return how_many
416}
417
418// malloc_uncollectable dynamically allocates a `n` bytes block of memory
419// on the heap, which will NOT be garbage-collected (but its contents will).
420[unsafe]
421pub fn malloc_uncollectable(n isize) &u8 {
422 $if trace_malloc ? {
423 total_m += n
424 C.fprintf(C.stderr, c'malloc_uncollectable %6d total %10d\n', n, total_m)
425 // print_backtrace()
426 }
427 if n < 0 {
428 panic('malloc_uncollectable(${n} < 0)')
429 }
430 $if vplayground ? {
431 if n > 10000 {
432 panic('allocating more than 10 KB at once is not allowed in the V playground')
433 }
434 if total_m > 50 * 1024 * 1024 {
435 panic('allocating more than 50 MB is not allowed in the V playground')
436 }
437 }
438 mut res := &u8(0)
439 $if prealloc {
440 return unsafe { prealloc_malloc(n) }
441 } $else $if gcboehm ? {
442 unsafe {
443 res = C.GC_MALLOC_UNCOLLECTABLE(n)
444 }
445 } $else $if freestanding {
446 res = unsafe { __malloc(usize(n)) }
447 } $else {
448 res = unsafe { C.malloc(n) }
449 }
450 if res == 0 {
451 panic('malloc_uncollectable(${n}) failed')
452 }
453 $if debug_malloc ? {
454 // Fill in the memory with something != 0 i.e. `M`, so it is easier to spot
455 // when the calling code wrongly relies on it being zeroed.
456 unsafe { C.memset(res, 0x4D, n) }
457 }
458 return res
459}
460
461// v_realloc resizes the memory block `b` with `n` bytes.
462// The `b byteptr` must be a pointer to an existing memory block
463// previously allocated with `malloc`, `v_calloc` or `vcalloc`.
464// Please, see also realloc_data, and use it instead if possible.
465[unsafe]
466pub fn v_realloc(b &u8, n isize) &u8 {
467 $if trace_realloc ? {
468 C.fprintf(C.stderr, c'v_realloc %6d\n', n)
469 }
470 mut new_ptr := &u8(0)
471 $if prealloc {
472 unsafe {
473 new_ptr = malloc(n)
474 C.memcpy(new_ptr, b, n)
475 }
476 return new_ptr
477 } $else $if gcboehm ? {
478 new_ptr = unsafe { C.GC_REALLOC(b, n) }
479 } $else {
480 new_ptr = unsafe { C.realloc(b, n) }
481 }
482 if new_ptr == 0 {
483 panic('realloc(${n}) failed')
484 }
485 return new_ptr
486}
487
488// realloc_data resizes the memory block pointed by `old_data` to `new_size`
489// bytes. `old_data` must be a pointer to an existing memory block, previously
490// allocated with `malloc`, `v_calloc` or `vcalloc`, of size `old_data`.
491// realloc_data returns a pointer to the new location of the block.
492// Note: if you know the old data size, it is preferable to call `realloc_data`,
493// instead of `v_realloc`, at least during development, because `realloc_data`
494// can make debugging easier, when you compile your program with
495// `-d debug_realloc`.
496[unsafe]
497pub fn realloc_data(old_data &u8, old_size int, new_size int) &u8 {
498 $if trace_realloc ? {
499 C.fprintf(C.stderr, c'realloc_data old_size: %6d new_size: %6d\n', old_size, new_size)
500 }
501 $if prealloc {
502 return unsafe { prealloc_realloc(old_data, old_size, new_size) }
503 }
504 $if debug_realloc ? {
505 // Note: this is slower, but helps debugging memory problems.
506 // The main idea is to always force reallocating:
507 // 1) allocate a new memory block
508 // 2) copy the old to the new
509 // 3) fill the old with 0x57 (`W`)
510 // 4) free the old block
511 // => if there is still a pointer to the old block somewhere
512 // it will point to memory that is now filled with 0x57.
513 unsafe {
514 new_ptr := malloc(new_size)
515 min_size := if old_size < new_size { old_size } else { new_size }
516 C.memcpy(new_ptr, old_data, min_size)
517 C.memset(old_data, 0x57, old_size)
518 free(old_data)
519 return new_ptr
520 }
521 }
522 mut nptr := &u8(0)
523 $if gcboehm ? {
524 nptr = unsafe { C.GC_REALLOC(old_data, new_size) }
525 } $else {
526 nptr = unsafe { C.realloc(old_data, new_size) }
527 }
528 if nptr == 0 {
529 panic('realloc_data(${old_data}, ${old_size}, ${new_size}) failed')
530 }
531 return nptr
532}
533
534// vcalloc dynamically allocates a zeroed `n` bytes block of memory on the heap.
535// vcalloc returns a `byteptr` pointing to the memory address of the allocated space.
536// Unlike `v_calloc` vcalloc checks for negative values given in `n`.
537pub fn vcalloc(n isize) &u8 {
538 $if trace_vcalloc ? {
539 total_m += n
540 C.fprintf(C.stderr, c'vcalloc %6d total %10d\n', n, total_m)
541 }
542 if n < 0 {
543 panic('calloc(${n} < 0)')
544 } else if n == 0 {
545 return &u8(0)
546 }
547 $if prealloc {
548 return unsafe { prealloc_calloc(n) }
549 } $else $if gcboehm ? {
550 return unsafe { &u8(C.GC_MALLOC(n)) }
551 } $else {
552 return unsafe { C.calloc(1, n) }
553 }
554}
555
556// special versions of the above that allocate memory which is not scanned
557// for pointers (but is collected) when the Boehm garbage collection is used
558pub fn vcalloc_noscan(n isize) &u8 {
559 $if trace_vcalloc ? {
560 total_m += n
561 C.fprintf(C.stderr, c'vcalloc_noscan %6d total %10d\n', n, total_m)
562 }
563 $if prealloc {
564 return unsafe { prealloc_calloc(n) }
565 } $else $if gcboehm ? {
566 $if vplayground ? {
567 if n > 10000 {
568 panic('allocating more than 10 KB is not allowed in the playground')
569 }
570 }
571 if n < 0 {
572 panic('calloc_noscan(${n} < 0)')
573 }
574 return $if gcboehm_opt ? {
575 unsafe { &u8(C.memset(C.GC_MALLOC_ATOMIC(n), 0, n)) }
576 } $else {
577 unsafe { &u8(C.GC_MALLOC(n)) }
578 }
579 } $else {
580 return unsafe { vcalloc(n) }
581 }
582}
583
584// free allows for manually freeing memory allocated at the address `ptr`.
585[unsafe]
586pub fn free(ptr voidptr) {
587 $if prealloc {
588 return
589 } $else $if gcboehm ? {
590 // It is generally better to leave it to Boehm's gc to free things.
591 // Calling C.GC_FREE(ptr) was tried initially, but does not work
592 // well with programs that do manual management themselves.
593 //
594 // The exception is doing leak detection for manual memory management:
595 $if gcboehm_leak ? {
596 unsafe { C.GC_FREE(ptr) }
597 }
598 } $else {
599 C.free(ptr)
600 }
601}
602
603// memdup dynamically allocates a `sz` bytes block of memory on the heap
604// memdup then copies the contents of `src` into the allocated space and
605// returns a pointer to the newly allocated space.
606[unsafe]
607pub fn memdup(src voidptr, sz int) voidptr {
608 $if trace_memdup ? {
609 C.fprintf(C.stderr, c'memdup size: %10d\n', sz)
610 }
611 if sz == 0 {
612 return vcalloc(1)
613 }
614 unsafe {
615 mem := malloc(sz)
616 return C.memcpy(mem, src, sz)
617 }
618}
619
620[unsafe]
621pub fn memdup_noscan(src voidptr, sz int) voidptr {
622 $if trace_memdup ? {
623 C.fprintf(C.stderr, c'memdup_noscan size: %10d\n', sz)
624 }
625 if sz == 0 {
626 return vcalloc_noscan(1)
627 }
628 unsafe {
629 mem := malloc_noscan(sz)
630 return C.memcpy(mem, src, sz)
631 }
632}
633
634// memdup_uncollectable dynamically allocates a `sz` bytes block of memory
635// on the heap, which will NOT be garbage-collected (but its contents will).
636// memdup_uncollectable then copies the contents of `src` into the allocated
637// space and returns a pointer to the newly allocated space.
638[unsafe]
639pub fn memdup_uncollectable(src voidptr, sz int) voidptr {
640 $if trace_memdup ? {
641 C.fprintf(C.stderr, c'memdup_uncollectable size: %10d\n', sz)
642 }
643 if sz == 0 {
644 return vcalloc(1)
645 }
646 unsafe {
647 mem := malloc_uncollectable(sz)
648 return C.memcpy(mem, src, sz)
649 }
650}
651
652pub struct GCHeapUsage {
653pub:
654 heap_size usize
655 free_bytes usize
656 total_bytes usize
657 unmapped_bytes usize
658 bytes_since_gc usize
659}
660
661// gc_heap_usage returns the info about heap usage
662pub fn gc_heap_usage() GCHeapUsage {
663 $if gcboehm ? {
664 mut res := GCHeapUsage{}
665 C.GC_get_heap_usage_safe(&res.heap_size, &res.free_bytes, &res.unmapped_bytes,
666 &res.bytes_since_gc, &res.total_bytes)
667 return res
668 } $else {
669 return GCHeapUsage{}
670 }
671}
672
673// gc_memory_use returns the total memory use in bytes by all allocated blocks
674pub fn gc_memory_use() usize {
675 $if gcboehm ? {
676 return C.GC_get_memory_use()
677 } $else {
678 return 0
679 }
680}
681
682[inline]
683fn v_fixed_index(i int, len int) int {
684 $if !no_bounds_checking {
685 if i < 0 || i >= len {
686 s := 'fixed array index out of range (index: ${i}, len: ${len})'
687 panic(s)
688 }
689 }
690 return i
691}
692
693// print_backtrace shows a backtrace of the current call stack on stdout
694pub fn print_backtrace() {
695 // At the time of backtrace_symbols_fd call, the C stack would look something like this:
696 // * print_backtrace_skipping_top_frames
697 // * print_backtrace itself
698 // * the rest of the backtrace frames
699 // => top 2 frames should be skipped, since they will not be informative to the developer
700 $if !no_backtrace ? {
701 $if freestanding {
702 println(bare_backtrace())
703 } $else {
704 $if tinyc {
705 C.tcc_backtrace(c'Backtrace')
706 } $else {
707 // NOTE: TCC doesn't have the unwind library
708 $if use_libbacktrace ? {
709 print_libbacktrace(1)
710 } $else {
711 print_backtrace_skipping_top_frames(2)
712 }
713 }
714 }
715 }
716}
717
718// NOTE: g_main_argc and g_main_argv are filled in right after C's main start.
719// They are used internally by V's builtin; for user code, it is much
720// more convenient to just use `os.args` instead.
721
722[markused]
723__global g_main_argc = int(0)
724
725[markused]
726__global g_main_argv = unsafe { nil }