cgen: fix os.args initialization, so that `const x = os.args[0]` works
							parent
							
								
									0081e5740d
								
							
						
					
					
						commit
						722a603222
					
				| 
						 | 
				
			
			@ -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() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 (
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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])) )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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();')
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue