// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. @[has_globals] module builtin // g_original_codepage - used to restore the original windows console code page when exiting __global g_original_codepage = u32(0) // See https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types // See https://www.codeproject.com/KB/string/cppstringguide1.aspx pub type C.BOOL = int pub type C.HINSTANCE = voidptr pub type C.HICON = voidptr pub type C.HCURSOR = voidptr pub type C.HBRUSH = voidptr pub type C.HWND = voidptr pub type C.HGLOBAL = voidptr pub type C.HANDLE = voidptr pub type C.LRESULT = voidptr pub type C.CHAR = char pub type C.TCHAR = u16 // It is u8 if UNICODE is not defined, but for V programs it always is pub type C.WCHAR = u16 pub type C.LPSTR = &char pub type C.LPWSTR = &C.WCHAR pub type C.LPTSTR = &C.TCHAR pub type C.LPCTSTR = &C.TCHAR // utf8 to stdout needs C.SetConsoleOutputCP(cp_utf8) fn C.GetConsoleOutputCP() u32 fn C.SetConsoleOutputCP(wCodePageID u32) bool fn restore_codepage() { C.SetConsoleOutputCP(g_original_codepage) } fn is_terminal(fd int) int { mut mode := u32(0) osfh := voidptr(C._get_osfhandle(fd)) C.GetConsoleMode(osfh, voidptr(&mode)) return int(mode) } const std_output_handle = -11 const std_error_handle = -12 const enable_processed_output = 1 const enable_wrap_at_eol_output = 2 const evable_virtual_terminal_processing = 4 @[markused] fn builtin_init() { $if gcboehm ? { $if !gc_warn_on_stderr ? { gc_set_warn_proc(internal_gc_warn_proc_none) } } g_original_codepage = C.GetConsoleOutputCP() C.SetConsoleOutputCP(cp_utf8) at_exit(restore_codepage) or {} if is_terminal(1) > 0 { C.SetConsoleMode(C.GetStdHandle(std_output_handle), enable_processed_output | enable_wrap_at_eol_output | evable_virtual_terminal_processing) C.SetConsoleMode(C.GetStdHandle(std_error_handle), enable_processed_output | enable_wrap_at_eol_output | evable_virtual_terminal_processing) unsafe { C.setbuf(C.stdout, 0) C.setbuf(C.stderr, 0) } } $if !no_backtrace ? { add_unhandled_exception_handler() } // On windows, the default buffering is block based (~4096bytes), which interferes badly with non cmd shells // It is much better to have it off by default instead. unbuffer_stdout() } // TODO: copypaste from os // we want to be able to use this here without having to `import os` struct ExceptionRecord { pub: // status_ constants code u32 flags u32 record &ExceptionRecord = unsafe { nil } address voidptr param_count u32 // params []voidptr } struct ContextRecord { // TODO } struct ExceptionPointers { pub: exception_record &ExceptionRecord = unsafe { nil } context_record &ContextRecord = unsafe { nil } } type VectoredExceptionHandler = fn (&ExceptionPointers) int fn C.AddVectoredExceptionHandler(int, voidptr) fn add_vectored_exception_handler(handler VectoredExceptionHandler) { C.AddVectoredExceptionHandler(1, voidptr(handler)) } @[callconv: stdcall] fn unhandled_exception_handler(e &ExceptionPointers) int { match e.exception_record.code { // These are 'used' by the backtrace printer // so we dont want to catch them... 0x4001000A, 0x40010006, 0xE06D7363 { return 0 } else { println('Unhandled Exception 0x' + ptr_str(e.exception_record.code)) print_backtrace_skipping_top_frames(5) } } return 0 } fn add_unhandled_exception_handler() { add_vectored_exception_handler(VectoredExceptionHandler(voidptr(unhandled_exception_handler))) } fn C.IsDebuggerPresent() bool fn C.__debugbreak() fn break_if_debugger_attached() { $if tinyc { unsafe { mut ptr := &voidptr(0) *ptr = nil _ = ptr } } $else { if C.IsDebuggerPresent() { C.__debugbreak() } } } const format_message_allocate_buffer = 0x00000100 const format_message_argument_array = 0x00002000 const format_message_from_hmodule = 0x00000800 const format_message_from_string = 0x00000400 const format_message_from_system = 0x00001000 const format_message_ignore_inserts = 0x00000200 // return an error message generated from WinAPI's `LastError` pub fn winapi_lasterr_str() string { err_msg_id := C.GetLastError() if err_msg_id == 8 { // handle this case special since `FormatMessageW()` might not work anymore return 'insufficient memory' } mut msgbuf := &u16(unsafe { nil }) res := C.FormatMessageW(format_message_allocate_buffer | format_message_from_system | format_message_ignore_inserts, 0, err_msg_id, 0, voidptr(&msgbuf), 0, 0) err_msg := if res == 0 { 'Win-API error ${err_msg_id}' } else { unsafe { string_from_wide(msgbuf) } } return err_msg } // panic with an error message generated from WinAPI's `LastError` @[noreturn] pub fn panic_lasterr(base string) { panic(base + winapi_lasterr_str()) }