cgen: fix os.args initialization, so that `const x = os.args[0]` works

pull/8009/head
Delyan Angelov 2021-01-10 14:31:20 +02:00
parent 0081e5740d
commit 722a603222
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
4 changed files with 51 additions and 40 deletions

View File

@ -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() {

View File

@ -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 (

View File

@ -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])) )

View File

@ -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 <setjmp.h> // 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();')
}