From 722a6032229315d0ad8a64ccbb3931533df9df1d Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 10 Jan 2021 14:31:20 +0200 Subject: [PATCH] cgen: fix os.args initialization, so that `const x = os.args[0]` works --- vlib/os/os_test.v | 7 +++++++ vlib/v/gen/cgen.v | 42 ++++++++++++++++++++++++++++-------------- vlib/v/gen/cheaders.v | 2 +- vlib/v/gen/cmain.v | 40 +++++++++++++++------------------------- 4 files changed, 51 insertions(+), 40 deletions(-) diff --git a/vlib/os/os_test.v b/vlib/os/os_test.v index 660753b6a5..b17f9e5ebb 100644 --- a/vlib/os/os_test.v +++ b/vlib/os/os_test.v @@ -7,6 +7,10 @@ const ( tfolder = os.join_path(os.temp_dir(), 'v', 'tests', 'os_test') ) +// os.args has to be *already initialized* with the program's argc/argv at this point +// thus it can be used for other consts too: +const args_at_start = os.args.clone() + fn testsuite_begin() { eprintln('testsuite_begin, tfolder = $tfolder') os.rmdir_all(tfolder) @@ -14,6 +18,9 @@ fn testsuite_begin() { os.mkdir_all(tfolder) os.chdir(tfolder) assert os.is_dir(tfolder) + // println('args_at_start: $args_at_start') + assert args_at_start.len > 0 + assert args_at_start == os.args } fn testsuite_end() { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index ce6766eb0f..70a21d90b2 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -36,7 +36,7 @@ mut: typedefs2 strings.Builder type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) - inits map[string]strings.Builder // contents of `void _vinit(){}` + inits map[string]strings.Builder // contents of `void _vinit/2{}` cleanups map[string]strings.Builder // contents of `void _vcleanup(){}` gowrappers strings.Builder // all go callsite wrappers stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined @@ -224,7 +224,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string // anon fn may include assert and thus this needs // to be included before any test contents are written if g.is_test && !tests_inited { - g.write_tests_main() + g.write_tests_definitions() tests_inited = true } g.stmts(file.stmts) @@ -421,7 +421,9 @@ pub fn (mut g Gen) finish() { if g.pref.is_livemain || g.pref.is_liveshared { g.generate_hotcode_reloader_code() } - if !g.pref.is_test { + if g.pref.is_test { + g.gen_c_main_for_tests() + } else { g.gen_c_main() } } @@ -4397,13 +4399,20 @@ fn (mut g Gen) const_decl_simple_define(name string, val string) { } fn (mut g Gen) const_decl_init_later(mod string, name string, val string, typ table.Type) { - // Initialize more complex consts in `void _vinit(){}` + // Initialize more complex consts in `void _vinit/2{}` // (C doesn't allow init expressions that can't be resolved at compile time). styp := g.typ(typ) - // cname := '_const_$name' g.definitions.writeln('$styp $cname; // inited later') - g.inits[mod].writeln('\t$cname = $val;') + if cname == '_const_os__args' { + if g.pref.os == .windows { + g.inits[mod].writeln('\t_const_os__args = os__init_os_args_wide(___argc, (byteptr*)___argv);') + } else { + g.inits[mod].writeln('\t_const_os__args = os__init_os_args(___argc, (byte**)___argv);') + } + } else { + g.inits[mod].writeln('\t$cname = $val;') + } if g.is_autofree { if styp.starts_with('array_') { g.cleanups[mod].writeln('\tarray_free(&$cname);') @@ -4697,14 +4706,8 @@ fn (mut g Gen) write_init_function() { return } fn_vinit_start_pos := g.out.len - needs_constructor := g.pref.is_shared && g.pref.os != .windows - if needs_constructor { - g.writeln('__attribute__ ((constructor))') - g.writeln('void _vinit() {') - g.writeln('static bool once = false; if (once) {return;} once = true;') - } else { - g.writeln('void _vinit() {') - } + // ___argv is declared as voidptr here, because that unifies the windows/unix logic + g.writeln('void _vinit(int ___argc, voidptr ___argv) {') if g.is_autofree { // Pre-allocate the string buffer // s_str_buf_size := os.getenv('V_STRBUF_MB') @@ -4750,6 +4753,17 @@ fn (mut g Gen) write_init_function() { println(g.out.after(fn_vcleanup_start_pos)) } } + needs_constructor := g.pref.is_shared && g.pref.os != .windows + if needs_constructor { + // shared libraries need a way to call _vinit/2. For that purpose, + // provide a constructor, ensuring that all constants are initialized just once. + // NB: os.args in this case will be []. + g.writeln('__attribute__ ((constructor))') + g.writeln('void _vinit_caller() {') + g.writeln('\tstatic bool once = false; if (once) {return;} once = true;') + g.writeln('\t_vinit(0,0);') + g.writeln('}') + } } const ( diff --git a/vlib/v/gen/cheaders.v b/vlib/v/gen/cheaders.v index 5ff4843cda..5793bb9dcb 100644 --- a/vlib/v/gen/cheaders.v +++ b/vlib/v/gen/cheaders.v @@ -306,7 +306,7 @@ static inline bool _us64_lt(uint64_t a, int64_t b) { return a < INT64_MAX && (in byte* g_str_buf; int load_so(byteptr); void reload_so(); -void _vinit(); +void _vinit(int ___argc, voidptr ___argv); void _vcleanup(); #define sigaction_size sizeof(sigaction); #define _ARR_LEN(a) ( (sizeof(a)) / (sizeof(a[0])) ) diff --git a/vlib/v/gen/cmain.v b/vlib/v/gen/cmain.v index ad9b7ae9df..0cccfc9f57 100644 --- a/vlib/v/gen/cmain.v +++ b/vlib/v/gen/cmain.v @@ -49,6 +49,12 @@ fn (mut g Gen) gen_c_main_function_header() { } // GUI application g.writeln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd){') + g.writeln('\tLPWSTR full_cmd_line = GetCommandLineW(); // NB: do not use cmd_line') + g.writeln('\ttypedef LPWSTR*(WINAPI *cmd_line_to_argv)(LPCWSTR, int*);') + g.writeln('\tHMODULE shell32_module = LoadLibrary(L"shell32.dll");') + g.writeln('\tcmd_line_to_argv CommandLineToArgvW = (cmd_line_to_argv)GetProcAddress(shell32_module, "CommandLineToArgvW");') + g.writeln('\tint ___argc;') + g.writeln('\twchar_t** ___argv = CommandLineToArgvW(full_cmd_line, &___argc);') } else { // Console application g.writeln('int wmain(int ___argc, wchar_t* ___argv[], wchar_t* ___envp[]){') @@ -60,30 +66,12 @@ fn (mut g Gen) gen_c_main_function_header() { fn (mut g Gen) gen_c_main_header() { g.gen_c_main_function_header() - if g.pref.os == .windows && g.is_gui_app() { - g.writeln('\tLPWSTR full_cmd_line = GetCommandLineW(); // NB: do not use cmd_line') - g.writeln('\ttypedef LPWSTR*(WINAPI *cmd_line_to_argv)(LPCWSTR, int*);') - g.writeln('\tHMODULE shell32_module = LoadLibrary(L"shell32.dll");') - g.writeln('\tcmd_line_to_argv CommandLineToArgvW = (cmd_line_to_argv)GetProcAddress(shell32_module, "CommandLineToArgvW");') - g.writeln('\tint ___argc;') - g.writeln('\twchar_t** ___argv = CommandLineToArgvW(full_cmd_line, &___argc);') - } - g.writeln('\t_vinit();') + g.writeln('\t_vinit(___argc, (voidptr)___argv);') if g.pref.is_prof { g.writeln('') g.writeln('\tatexit(vprint_profile_stats);') g.writeln('') } - if g.is_importing_os() { - if g.is_autofree { - g.writeln('free(_const_os__args.data); // empty, inited in _vinit()') - } - if g.pref.os == .windows { - g.writeln('\t_const_os__args = os__init_os_args_wide(___argc, ___argv);') - } else { - g.writeln('\t_const_os__args = os__init_os_args(___argc, (byteptr*)___argv);') - } - } if g.pref.is_livemain { g.generate_hotcode_reloading_main_caller() } @@ -123,7 +111,7 @@ void (_vsokol_cleanup_userdata_cb)(void* user_data) { sapp_desc sokol_main(int argc, char* argv[]) { (void)argc; (void)argv; - _vinit(); + _vinit(argc, (voidptr)argv); main__main(); ') if g.is_autofree { @@ -142,21 +130,24 @@ sapp_desc sokol_main(int argc, char* argv[]) { g.writeln('}') } -pub fn (mut g Gen) write_tests_main() { +pub fn (mut g Gen) write_tests_definitions() { g.includes.writeln('#include // write_tests_main') g.definitions.writeln('int g_test_oks = 0;') g.definitions.writeln('int g_test_fails = 0;') g.definitions.writeln('jmp_buf g_jump_buffer;') +} + +pub fn (mut g Gen) gen_c_main_for_tests() { main_fn_start_pos := g.out.len - g.gen_c_main_function_header() - g.writeln('\t_vinit();') g.writeln('') + g.gen_c_main_function_header() + g.writeln('\t_vinit(___argc, (voidptr)___argv);') all_tfuncs := g.get_all_test_function_names() if g.pref.is_stats { g.writeln('\tmain__BenchedTests bt = main__start_testing($all_tfuncs.len, _SLIT("$g.pref.path"));') } + g.writeln('') for t in all_tfuncs { - g.writeln('') if g.pref.is_stats { g.writeln('\tmain__BenchedTests_testing_step_start(&bt, _SLIT("$t"));') } @@ -169,7 +160,6 @@ pub fn (mut g Gen) write_tests_main() { if g.pref.is_stats { g.writeln('\tmain__BenchedTests_end_testing(&bt);') } - g.writeln('') if g.is_autofree { g.writeln('\t_vcleanup();') }