compiler: add back support for -live
							parent
							
								
									d6c1ee0202
								
							
						
					
					
						commit
						7038f59ca5
					
				|  | @ -2,8 +2,10 @@ module main | |||
| 
 | ||||
| import os | ||||
| import time | ||||
| import dl | ||||
| 
 | ||||
| const ( | ||||
| 	os_used = os.MAX_PATH | ||||
| 	time_used = time.now() | ||||
| 	dl_used = dl.version | ||||
| ) | ||||
|  |  | |||
|  | @ -20,7 +20,6 @@ const ( | |||
| 		'vlib/clipboard/clipboard_test.v', | ||||
| 		'vlib/sqlite/sqlite_test.v', | ||||
| 
 | ||||
| 		'vlib/v/tests/live_test.v', // Linux & Solaris only; since live does not actually work for now with v2, just skip
 | ||||
| 		'vlib/v/tests/asm_test.v', // skip everywhere for now, works on linux with cc != tcc
 | ||||
| 	] | ||||
| 	skip_on_linux = []string{} | ||||
|  |  | |||
|  | @ -123,14 +123,14 @@ fn parse_args(args []string) (&pref.Preferences, string) { | |||
| 			'-cg' { | ||||
| 				res.is_debug = true | ||||
| 			} | ||||
| 			'-live' { | ||||
| 				res.is_live = true | ||||
| 			} | ||||
| 			'-repl' { | ||||
| 				res.is_repl = true | ||||
| 			} | ||||
| 			'-live' { | ||||
| 				res.is_livemain = true | ||||
| 			} | ||||
| 			'-sharedlive' { | ||||
| 				res.is_live = true | ||||
| 				res.is_liveshared = true | ||||
| 				res.is_shared = true | ||||
| 			} | ||||
| 			'-shared' { | ||||
|  |  | |||
|  | @ -0,0 +1,5 @@ | |||
| module dl | ||||
| 
 | ||||
| pub const ( | ||||
| 	version = 1 | ||||
| ) | ||||
|  | @ -95,6 +95,7 @@ fn (mut v Builder) cc() { | |||
| 		'-Wno-unused-parameter', '-Wno-unused-result', '-Wno-unused-function', '-Wno-missing-braces', | ||||
| 			'-Wno-unused-label' | ||||
| 	] | ||||
| 	mut linker_flags := []string{} | ||||
| 	// TCC on Linux by default, unless -cc was provided
 | ||||
| 	// TODO if -cc = cc, TCC is still used, default compiler should be
 | ||||
| 	// used instead.
 | ||||
|  | @ -131,11 +132,15 @@ fn (mut v Builder) cc() { | |||
| 	// linux_host := os.user_os() == 'linux'
 | ||||
| 	v.log('cc() isprod=$v.pref.is_prod outname=$v.pref.out_name') | ||||
| 	if v.pref.is_shared { | ||||
| 		a << '-shared -fPIC ' // -Wl,-z,defs'
 | ||||
| 		linker_flags << '-shared' | ||||
| 		a << '-fPIC' // -Wl,-z,defs'
 | ||||
| 		v.pref.out_name += '.so' | ||||
| 	} | ||||
| 	if v.pref.is_bare { | ||||
| 		a << '-fno-stack-protector -static -ffreestanding -nostdlib' | ||||
| 		a << '-fno-stack-protector' | ||||
| 		a << '-ffreestanding' | ||||
| 		linker_flags << '-static' | ||||
| 		linker_flags << '-nostdlib' | ||||
| 	} | ||||
| 	if v.pref.build_mode == .build_module { | ||||
| 		// Create the modules & out directory if it's not there.
 | ||||
|  | @ -199,14 +204,14 @@ fn (mut v Builder) cc() { | |||
| 		a << optimization_options | ||||
| 	} | ||||
| 	if debug_mode && os.user_os() != 'windows' { | ||||
| 		a << ' -rdynamic ' // needed for nicer symbolic backtraces
 | ||||
| 		linker_flags << ' -rdynamic ' // needed for nicer symbolic backtraces
 | ||||
| 	} | ||||
| 	if v.pref.ccompiler != 'msvc' && v.pref.os != .freebsd { | ||||
| 		a << '-Werror=implicit-function-declaration' | ||||
| 	} | ||||
| 	if v.pref.is_shared || v.pref.is_live { | ||||
| 	if v.pref.is_liveshared || v.pref.is_livemain { | ||||
| 		if v.pref.os == .linux || os.user_os() == 'linux' { | ||||
| 			a << '-rdynamic' | ||||
| 			linker_flags << '-rdynamic' | ||||
| 		} | ||||
| 		if v.pref.os == .mac || os.user_os() == 'mac' { | ||||
| 			a << '-flat_namespace' | ||||
|  | @ -320,7 +325,8 @@ fn (mut v Builder) cc() { | |||
| 		} | ||||
| 		if !is_cc_tcc { | ||||
| 			$if linux { | ||||
| 				a << '-Xlinker -z -Xlinker muldefs' | ||||
| 				linker_flags << '-Xlinker -z' | ||||
| 				linker_flags << '-Xlinker muldefs' | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | @ -328,20 +334,21 @@ fn (mut v Builder) cc() { | |||
| 	// || os.user_os() == 'linux'
 | ||||
| 	if !v.pref.is_bare && v.pref.build_mode != .build_module && v.pref.os in [ .linux, .freebsd, | ||||
| 		.openbsd, .netbsd, .dragonfly, .solaris, .haiku] { | ||||
| 		a << '-lm -lpthread ' | ||||
| 		linker_flags << '-lm' | ||||
| 		linker_flags << '-lpthread' | ||||
| 		// -ldl is a Linux only thing. BSDs have it in libc.
 | ||||
| 		if v.pref.os == .linux { | ||||
| 			a << ' -ldl ' | ||||
| 			linker_flags << '-ldl' | ||||
| 		} | ||||
| 		if v.pref.os == .freebsd { | ||||
| 			// FreeBSD: backtrace needs execinfo library while linking
 | ||||
| 			a << ' -lexecinfo ' | ||||
| 			linker_flags << '-lexecinfo' | ||||
| 		} | ||||
| 	} | ||||
| 	if !v.pref.is_bare && v.pref.os == .js && os.user_os() == 'linux' { | ||||
| 		a << '-lm' | ||||
| 		linker_flags << '-lm' | ||||
| 	} | ||||
| 	args := a.join(' ') | ||||
| 	args := a.join(' ') + linker_flags.join(' ') | ||||
| 	start: | ||||
| 	todo() | ||||
| 	// TODO remove
 | ||||
|  |  | |||
|  | @ -151,10 +151,10 @@ pub fn (v Builder) get_user_files() []string { | |||
| 	// See cmd/tools/preludes/README.md for more info about what preludes are
 | ||||
| 	vroot := os.dir(pref.vexe_path()) | ||||
| 	preludes_path := os.join_path(vroot, 'cmd', 'tools', 'preludes') | ||||
| 	if v.pref.is_live { | ||||
| 	if v.pref.is_livemain { | ||||
| 		user_files << os.join_path(preludes_path, 'live_main.v') | ||||
| 	} | ||||
| 	if v.pref.is_live && v.pref.is_shared { | ||||
| 	if v.pref.is_liveshared { | ||||
| 		user_files << os.join_path(preludes_path, 'live_shared.v') | ||||
| 	} | ||||
| 	if v.pref.is_test { | ||||
|  |  | |||
|  | @ -1,242 +0,0 @@ | |||
| module builder | ||||
| 
 | ||||
| import os | ||||
| import time | ||||
| 
 | ||||
| fn (v &Builder) generate_hotcode_reloading_declarations() { | ||||
| 	/* | ||||
| 	QTODO | ||||
| 	mut cgen := v.cgen | ||||
| 	if v.pref.os != .windows { | ||||
| 		if v.pref.is_so { | ||||
| 			cgen.genln('pthread_mutex_t live_fn_mutex;') | ||||
| 		} | ||||
| 		if v.pref.is_live { | ||||
| 			cgen.genln('pthread_mutex_t live_fn_mutex = PTHREAD_MUTEX_INITIALIZER;') | ||||
| 		} | ||||
| 	} | ||||
| 	else { | ||||
| 		if v.pref.is_so { | ||||
| 			cgen.genln('HANDLE live_fn_mutex;') | ||||
| 			cgen.genln(' | ||||
| void pthread_mutex_lock(HANDLE *m) { | ||||
| 	WaitForSingleObject(*m, INFINITE); | ||||
| } | ||||
| 
 | ||||
| void pthread_mutex_unlock(HANDLE *m) { | ||||
| 	ReleaseMutex(*m); | ||||
| }') | ||||
| 		} | ||||
| 		if v.pref.is_live { | ||||
| 			cgen.genln('HANDLE live_fn_mutex = 0;') | ||||
| 		} | ||||
| 	} | ||||
| 	*/ | ||||
| } | ||||
| 
 | ||||
| fn (v &Builder) generate_hotcode_reloading_main_caller() { | ||||
| 	// QTODO
 | ||||
| 	/* | ||||
| 	if !v.pref.is_live { | ||||
| 		return | ||||
| 	} | ||||
| 	// We are in live code reload mode, so start the .so loader in the background
 | ||||
| 	mut cgen := v.cgen | ||||
| 	cgen.genln('') | ||||
| 	file_base := os.file_name(v.pref.path).replace('.v', '') | ||||
| 	if v.pref.os != .windows { | ||||
| 		// unix:
 | ||||
| 		so_name := file_base + '.so' | ||||
| 		cgen.genln('  char *live_library_name = "$so_name";') | ||||
| 		cgen.genln('  load_so(live_library_name);') | ||||
| 		cgen.genln('  pthread_t _thread_so;') | ||||
| 		cgen.genln('  pthread_create(&_thread_so , NULL, (void *)&reload_so, live_library_name);') | ||||
| 	} | ||||
| 	else { | ||||
| 		// windows:
 | ||||
| 		so_name := file_base + if v.pref.ccompiler == 'msvc' { '.dll' } else { '.so' } | ||||
| 		cgen.genln('  char *live_library_name = "$so_name";') | ||||
| 		cgen.genln('  live_fn_mutex = CreateMutexA(0, 0, 0);') | ||||
| 		cgen.genln('  load_so(live_library_name);') | ||||
| 		cgen.genln('  unsigned long _thread_so;') | ||||
| 		cgen.genln('  _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);') | ||||
| 	} | ||||
| 	*/ | ||||
| } | ||||
| 
 | ||||
| fn (v &Builder) generate_hot_reload_code() { | ||||
| 	/* | ||||
| 	QTODO | ||||
| 	mut cgen := v.cgen | ||||
| 	// Hot code reloading
 | ||||
| 	if v.pref.is_live { | ||||
| 		mut file := os.real_path(v.pref.path) | ||||
| 		file_base := os.file_name(file).replace('.v', '') | ||||
| 		so_name := file_base + '.so' | ||||
| 		// Need to build .so file before building the live application
 | ||||
| 		// The live app needs to load this .so file on initialization.
 | ||||
| 		mut vexe := os.args[0] | ||||
| 		if os.user_os() == 'windows' { | ||||
| 			vexe = cescaped_path(vexe) | ||||
| 			file = cescaped_path(file) | ||||
| 		} | ||||
| 		mut msvc := '' | ||||
| 		if v.pref.ccompiler == 'msvc' { | ||||
| 			msvc = '-cc msvc' | ||||
| 		} | ||||
| 		so_debug_flag := if v.pref.is_debug { '-g' } else { '' } | ||||
| 		cmd_compile_shared_library := '$vexe $msvc $so_debug_flag -o $file_base -solive -shared $file' | ||||
| 		if v.pref.verbosity.is_higher_or_equal(.level_one) { | ||||
| 			println(cmd_compile_shared_library) | ||||
| 		} | ||||
| 		ticks := time.ticks() | ||||
| 		so_compilation_result := os.system(cmd_compile_shared_library) | ||||
| 		if v.pref.verbosity.is_higher_or_equal(.level_two) { | ||||
| 			diff := time.ticks() - ticks | ||||
| 			println('compiling shared library took $diff ms') | ||||
| 			println('=========\n') | ||||
| 		} | ||||
| 		if so_compilation_result != 0 { | ||||
| 			exit(1) | ||||
| 		} | ||||
| 		cgen.genln(' | ||||
| 
 | ||||
| void lfnmutex_print(char *s){ | ||||
| #if 0 | ||||
| 	fflush(stderr); | ||||
| 	fprintf(stderr,">> live_fn_mutex: %p | %s\\n", &live_fn_mutex, s); | ||||
| 	fflush(stderr); | ||||
| #endif | ||||
| } | ||||
| ') | ||||
| 		if v.pref.os != .windows { | ||||
| 			cgen.genln(' | ||||
| #define _POSIX_C_SOURCE 1 | ||||
| #include <limits.h> | ||||
| #ifndef PATH_MAX | ||||
| #define PATH_MAX 1024 | ||||
| #endif | ||||
| 
 | ||||
| void* live_lib = 0; | ||||
| 
 | ||||
| int load_so(byteptr path) { | ||||
| 	char cpath[PATH_MAX] = {0}; | ||||
| 	int res = snprintf(cpath, sizeof (cpath), "./%s", path); | ||||
| 	if (res >= sizeof (cpath)) { | ||||
| 		fprintf (stderr, "path is too long"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	//printf("load_so %s\\n", cpath);
 | ||||
| 	if (live_lib) dlclose(live_lib); | ||||
| 	live_lib = dlopen(cpath, RTLD_LAZY); | ||||
| 	if (!live_lib) { | ||||
| 		fprintf(stderr, "open failed"); | ||||
| 		exit(1); | ||||
| 		return 0; | ||||
| 	} | ||||
| ') | ||||
| 			for so_fn in cgen.so_fns { | ||||
| 				cgen.genln('$so_fn = dlsym(live_lib, "$so_fn");  ') | ||||
| 			} | ||||
| 		} | ||||
| 		else { | ||||
| 			cgen.genln(' | ||||
| 
 | ||||
| #ifndef PATH_MAX | ||||
| #define PATH_MAX 1024 | ||||
| #endif | ||||
| 
 | ||||
| void pthread_mutex_lock(HANDLE *m) { | ||||
| 	WaitForSingleObject(*m, INFINITE); | ||||
| } | ||||
| 
 | ||||
| void pthread_mutex_unlock(HANDLE *m) { | ||||
| 	ReleaseMutex(*m); | ||||
| } | ||||
| 
 | ||||
| void* live_lib = NULL; | ||||
| int load_so(byteptr path) { | ||||
| 	char cpath[PATH_MAX]; | ||||
| 	int res = snprintf(cpath, sizeof (cpath), "./%s", path); | ||||
| 	if (res >= sizeof(cpath)) { | ||||
| 		puts("path is too long\\n"); | ||||
| 		exit(1); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	if (live_lib) FreeLibrary(live_lib); | ||||
| 	live_lib = LoadLibraryA(cpath); | ||||
| 	if (!live_lib) { | ||||
| 		puts("open failed\\n"); | ||||
| 		exit(1); | ||||
| 		return 0; | ||||
| 	} | ||||
| ') | ||||
| 			for so_fn in cgen.so_fns { | ||||
| 				cgen.genln('$so_fn = (void *)GetProcAddress(live_lib, "$so_fn");  ') | ||||
| 			} | ||||
| 		} | ||||
| 		cgen.genln('return 1; | ||||
| } | ||||
| 
 | ||||
| int _live_reloads = 0; | ||||
| void reload_so() { | ||||
| 	char new_so_base[PATH_MAX] = {0}; | ||||
| 	char new_so_name[PATH_MAX] = {0}; | ||||
| 	char compile_cmd[PATH_MAX] = {0}; | ||||
| 	int last = os__file_last_mod_unix(tos2("$file")); | ||||
| 	while (1) { | ||||
| 		// TODO use inotify
 | ||||
| 		int now = os__file_last_mod_unix(tos2("$file")); | ||||
| 		if (now != last) { | ||||
| 			last = now; | ||||
| 			_live_reloads++; | ||||
| 
 | ||||
| 			//v -o bounce -shared bounce.v
 | ||||
| 			snprintf(new_so_base, sizeof (new_so_base), ".tmp.%d.${file_base}", _live_reloads); | ||||
| 			#ifdef _WIN32 | ||||
| 			// We have to make this directory becuase windows WILL NOT
 | ||||
| 			// do it for us
 | ||||
| 			os__mkdir(string_all_before_last(tos2(new_so_base), tos2("/"))); | ||||
| 			#endif | ||||
| 			#ifdef _MSC_VER | ||||
| 			snprintf(new_so_name, sizeof (new_so_name), "%s.dll", new_so_base); | ||||
| 			#else | ||||
| 			snprintf(new_so_name, sizeof (new_so_name), "%s.so", new_so_base); | ||||
| 			#endif | ||||
| 			snprintf(compile_cmd, sizeof (compile_cmd), "$vexe $msvc -o %s -solive -shared $file", new_so_base); | ||||
| 			os__system(tos2(compile_cmd)); | ||||
| 
 | ||||
| 			if( !os__exists(tos2(new_so_name)) ) { | ||||
| 				puts("Errors while compiling $file\\n"); | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			lfnmutex_print("reload_so locking..."); | ||||
| 			pthread_mutex_lock(&live_fn_mutex); | ||||
| 			lfnmutex_print("reload_so locked"); | ||||
| 
 | ||||
| 			load_so(new_so_name); | ||||
| 			#ifndef _WIN32 | ||||
| 			unlink(new_so_name); // removing the .so file from the filesystem after dlopen-ing it is safe, since it will still be mapped in memory.
 | ||||
| 			#else | ||||
| 			_unlink(new_so_name); | ||||
| 			#endif | ||||
| 			//if(0 == rename(new_so_name, "${so_name}")){
 | ||||
| 			//	load_so("${so_name}");
 | ||||
| 			//}
 | ||||
| 
 | ||||
| 			lfnmutex_print("reload_so unlocking..."); | ||||
| 			pthread_mutex_unlock(&live_fn_mutex); | ||||
| 			lfnmutex_print("reload_so unlocked"); | ||||
| 
 | ||||
| 		} | ||||
| 		time__sleep_ms(100); | ||||
| 	} | ||||
| } | ||||
| ') | ||||
| 	} | ||||
| 	if v.pref.is_so { | ||||
| 		cgen.genln(' int load_so(byteptr path) { return 0; }') | ||||
| 	} | ||||
| 	*/ | ||||
| } | ||||
|  | @ -56,6 +56,7 @@ struct Gen { | |||
| 	auto_str_funcs       strings.Builder // function bodies of all auto generated _str funcs
 | ||||
| 	comptime_defines     strings.Builder // custom defines, given by -d/-define flags on the CLI
 | ||||
| 	pcs_declarations     strings.Builder // -prof profile counter declarations for each function
 | ||||
| 	hotcode_definitions  strings.Builder // -live declarations & functions
 | ||||
| 	table                &table.Table | ||||
| 	pref                 &pref.Preferences | ||||
| 	module_built         string | ||||
|  | @ -89,6 +90,9 @@ mut: | |||
| 	pcs                  []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
 | ||||
| 	attr                 string | ||||
| 	is_builtin_mod       bool | ||||
| 	hotcode_fn_names     []string | ||||
| 	//
 | ||||
| 	fn_main              &ast.FnDecl // the FnDecl of the main function. Needed in order to generate the main function code *last*
 | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
|  | @ -120,9 +124,11 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string | |||
| 		comptime_defines: strings.new_builder(100) | ||||
| 		inits: strings.new_builder(100) | ||||
| 		pcs_declarations: strings.new_builder(100) | ||||
| 		hotcode_definitions: strings.new_builder(100) | ||||
| 		table: table | ||||
| 		pref: pref | ||||
| 		fn_decl: 0 | ||||
| 		fn_main: 0 | ||||
| 		autofree: true | ||||
| 		indent: -1 | ||||
| 		module_built: pref.path.after('vlib/') | ||||
|  | @ -182,6 +188,8 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string | |||
| 	b.writeln(g.interface_table()) | ||||
| 	b.writeln('\n// V gowrappers:') | ||||
| 	b.writeln(g.gowrappers.str()) | ||||
| 	b.writeln('\n// V hotcode definitions:') | ||||
| 	b.writeln(g.hotcode_definitions.str()) | ||||
| 	b.writeln('\n// V stringliterals:') | ||||
| 	b.writeln(g.stringliterals.str()) | ||||
| 	b.writeln('\n// V auto str functions:') | ||||
|  | @ -233,6 +241,9 @@ pub fn (mut g Gen) init() { | |||
| 	if g.pref.is_debug || 'debug' in g.pref.compile_defines { | ||||
| 		g.comptime_defines.writeln('#define _VDEBUG (1)') | ||||
| 	} | ||||
| 	if g.pref.is_livemain || g.pref.is_liveshared { | ||||
| 		g.generate_hotcode_reloading_declarations() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn (mut g Gen) finish() { | ||||
|  | @ -244,6 +255,14 @@ pub fn (mut g Gen) finish() { | |||
| 	if g.pref.is_prof { | ||||
| 		g.gen_vprint_profile_stats() | ||||
| 	} | ||||
| 	if g.pref.is_livemain || g.pref.is_liveshared { | ||||
| 		g.generate_hotcode_reloader_code() | ||||
| 	} | ||||
| 	if g.fn_main != 0 { | ||||
| 		g.out.writeln('') | ||||
| 		g.fn_decl = g.fn_main | ||||
| 		g.gen_fn_decl( g.fn_main ) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn (mut g Gen) write_typeof_functions() { | ||||
|  | @ -449,11 +468,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { | |||
| 		} | ||||
| 		ast.Attr { | ||||
| 			g.attr = it.name | ||||
| 			if it.name == 'inline' { | ||||
| 				// g.writeln(it.name)
 | ||||
| 			} else { | ||||
| 				g.writeln('//[$it.name]') | ||||
| 			} | ||||
| 			g.writeln('// Attr: [$it.name]') | ||||
| 		} | ||||
| 		ast.Block { | ||||
| 			g.writeln('{') | ||||
|  | @ -530,16 +545,20 @@ fn (mut g Gen) stmt(node ast.Stmt) { | |||
| 					println('build module `$g.module_built` fn `$it.name`') | ||||
| 				} | ||||
| 			} | ||||
| 			fn_start_pos := g.out.len | ||||
| 			g.fn_decl = it // &it
 | ||||
| 			g.gen_fn_decl(it) | ||||
| 			if g.pref.printfn_list.len > 0 && g.last_fn_c_name in g.pref.printfn_list { | ||||
| 				println(g.out.after(fn_start_pos)) | ||||
| 			if it.name == 'main' { | ||||
| 				// just remember `it`; main code will be generated in finish()
 | ||||
| 				g.fn_main = it | ||||
| 			} else { | ||||
| 				g.gen_fn_decl(it) | ||||
| 			} | ||||
| 			g.fn_decl = 0 | ||||
| 			if skip { | ||||
| 				g.out.go_back_to(pos) | ||||
| 			} | ||||
| 			g.writeln('') | ||||
| 			// g.attr has to be reset after each function
 | ||||
| 			g.attr = '' | ||||
| 		} | ||||
| 		ast.ForCStmt { | ||||
| 			g.write('for (') | ||||
|  | @ -2123,6 +2142,9 @@ fn verror(s string) { | |||
| } | ||||
| 
 | ||||
| fn (mut g Gen) write_init_function() { | ||||
| 	if g.pref.is_liveshared { | ||||
| 		return | ||||
| 	} | ||||
| 	g.writeln('void _vinit() {') | ||||
| 	g.writeln('\tbuiltin_init();') | ||||
| 	g.writeln('\tvinit_string_literals();') | ||||
|  |  | |||
							
								
								
									
										135
									
								
								vlib/v/gen/fn.v
								
								
								
								
							
							
						
						
									
										135
									
								
								vlib/v/gen/fn.v
								
								
								
								
							|  | @ -12,25 +12,40 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { | |||
| 		// || it.no_body {
 | ||||
| 		return | ||||
| 	} | ||||
| 	is_main := it.name == 'main' | ||||
| 	//
 | ||||
| 	if is_main && g.pref.is_liveshared { | ||||
| 		return | ||||
| 	} | ||||
| 	//
 | ||||
| 	fn_start_pos := g.out.len | ||||
| 	if g.attr == 'inline' { | ||||
| 		g.write('inline ') | ||||
| 		g.attr = '' | ||||
| 	} | ||||
| 	//
 | ||||
| 	is_livefn     := g.attr == 'live' | ||||
| 	is_livemain   := g.pref.is_livemain && is_livefn | ||||
| 	is_liveshared := g.pref.is_liveshared && is_livefn | ||||
| 	is_livemode   := g.pref.is_livemain || g.pref.is_liveshared | ||||
| 	is_live_wrap  := is_livefn && is_livemode | ||||
| 	if is_livefn && !is_livemode { | ||||
| 		eprintln('INFO: compile with `v -live $g.pref.path `, if you want to use the [live] function ${it.name} .') | ||||
| 	} | ||||
| 	//
 | ||||
| 	g.reset_tmp_count() | ||||
| 	is_main := it.name == 'main' | ||||
| 	if is_main { | ||||
| 		if g.pref.os == .windows { | ||||
| 			if g.is_gui_app() { | ||||
| 				// GUI application
 | ||||
| 				g.writeln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd') | ||||
| 				g.writeln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd){') | ||||
| 				g.last_fn_c_name = 'wWinMain' | ||||
| 			} else { | ||||
| 				// Console application
 | ||||
| 				g.writeln('int wmain(int ___argc, wchar_t* ___argv[], wchar_t* ___envp[]') | ||||
| 				g.writeln('int wmain(int ___argc, wchar_t* ___argv[], wchar_t* ___envp[]){') | ||||
| 				g.last_fn_c_name = 'wmain' | ||||
| 			} | ||||
| 		} else { | ||||
| 			g.write('int ${it.name}(int ___argc, char** ___argv') | ||||
| 			g.writeln('int main(int ___argc, char** ___argv){') | ||||
| 			g.last_fn_c_name = it.name | ||||
| 		} | ||||
| 	} else { | ||||
|  | @ -51,40 +66,67 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { | |||
| 		// println(name)
 | ||||
| 		// }
 | ||||
| 		// type_name := g.table.Type_to_str(it.return_type)
 | ||||
| 		type_name := g.typ(it.return_type) | ||||
| 		g.write('$type_name ${name}(') | ||||
| 		g.last_fn_c_name = name | ||||
| 		g.definitions.write('$type_name ${name}(') | ||||
| 	} | ||||
| 	// Receiver is the first argument
 | ||||
| 	/* | ||||
| 	if it.is_method { | ||||
| 		mut styp := g.typ(it.receiver.typ) | ||||
| 		// if it.receiver.typ.nr_muls() > 0 {
 | ||||
| 		// if it.rec_mut {
 | ||||
| 		// styp += '*'
 | ||||
| 		// }
 | ||||
| 		g.write('$styp $it.receiver.name ') | ||||
| 		// TODO mut
 | ||||
| 		g.definitions.write('$styp $it.receiver.name') | ||||
| 		if it.args.len > 0 { | ||||
| 			g.write(', ') | ||||
| 			g.definitions.write(', ') | ||||
| 
 | ||||
| 		// Live functions are protected by a mutex, because otherwise they
 | ||||
| 		// can be changed by the live reload thread, *while* they are
 | ||||
| 		// running, with unpredictable results (usually just crashing).
 | ||||
| 		// For this purpose, the actual body of the live function,
 | ||||
| 		// is put under a non publicly accessible function, that is prefixed
 | ||||
| 		// with 'impl_live_' .
 | ||||
| 		if is_livemain { | ||||
| 			g.hotcode_fn_names << name | ||||
| 		} | ||||
| 		mut impl_fn_name := if is_live_wrap { 'impl_live_${name}' } else { name } | ||||
| 		g.last_fn_c_name = impl_fn_name | ||||
| 		type_name := g.typ(it.return_type) | ||||
| 		//
 | ||||
| 		if is_live_wrap { | ||||
| 			if is_livemain { | ||||
| 				g.definitions.write('$type_name (* ${impl_fn_name})(') | ||||
| 				g.write('$type_name no_impl_${name}(') | ||||
| 			} | ||||
| 			if is_liveshared { | ||||
| 				g.definitions.write('$type_name ${impl_fn_name}(') | ||||
| 				g.write('$type_name ${impl_fn_name}(') | ||||
| 			} | ||||
| 		}else{ | ||||
| 			g.definitions.write('$type_name ${name}(') | ||||
| 			g.write('$type_name ${name}(') | ||||
| 		} | ||||
| 		fargs, fargtypes := g.fn_args(it.args, it.is_variadic) | ||||
| 		if it.no_body || (g.pref.use_cache && it.is_builtin) { | ||||
| 			// Just a function header. Builtin function bodies are defined in builtin.o
 | ||||
| 			g.definitions.writeln(');') | ||||
| 			g.writeln(');') | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	*/ | ||||
| 	//
 | ||||
| 	g.fn_args(it.args, it.is_variadic) | ||||
| 	if it.no_body || (g.pref.use_cache && it.is_builtin) { | ||||
| 		// Just a function header.
 | ||||
| 		// Builtin function bodies are defined in builtin.o
 | ||||
| 		g.definitions.writeln(');') | ||||
| 		g.writeln(');') | ||||
| 		return | ||||
| 	} | ||||
| 	g.writeln(') {') | ||||
| 	if !is_main { | ||||
| 		g.definitions.writeln(');') | ||||
| 		g.writeln(') {') | ||||
| 
 | ||||
| 		if is_livemain { | ||||
| 			// The live function just calls its implementation dual, while ensuring
 | ||||
| 			// that the call is wrapped by the mutex lock & unlock calls.
 | ||||
| 			// Adding the mutex lock/unlock inside the body of the implementation
 | ||||
| 			// function is not reliable, because the implementation function can do
 | ||||
| 			// an early exit, which will leave the mutex locked.
 | ||||
| 			mut fn_args_list := []string{} | ||||
| 			for ia, fa in fargs { | ||||
| 				fn_args_list << '${fargtypes[ia]} ${fa}' | ||||
| 			} | ||||
| 			mut live_fncall := '${impl_fn_name}(' + fargs.join(', ') + ');' | ||||
| 			mut live_fnreturn := '' | ||||
| 			if type_name != 'void' { | ||||
| 				live_fncall = '${type_name} res = ${live_fncall}' | ||||
| 				live_fnreturn = 'return res;' | ||||
| 			} | ||||
| 			g.definitions.writeln('$type_name ${name}('+fn_args_list.join(', ')+');') | ||||
| 			g.hotcode_definitions.writeln('$type_name ${name}('+fn_args_list.join(', ')+'){') | ||||
| 			g.hotcode_definitions.writeln('  pthread_mutex_lock(&live_fn_mutex);') | ||||
| 			g.hotcode_definitions.writeln('  $live_fncall') | ||||
| 			g.hotcode_definitions.writeln('  pthread_mutex_unlock(&live_fn_mutex);') | ||||
| 			g.hotcode_definitions.writeln('  $live_fnreturn') | ||||
| 			g.hotcode_definitions.writeln('}') | ||||
| 		} | ||||
| 	} | ||||
| 	if is_main { | ||||
| 		if g.pref.os == .windows && g.is_gui_app() { | ||||
|  | @ -106,6 +148,10 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { | |||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if g.pref.is_livemain && is_main { | ||||
| 		g.generate_hotcode_reloading_main_caller() | ||||
| 	} | ||||
| 	// Profiling mode? Start counting at the beginning of the function (save current time).
 | ||||
| 	if g.pref.is_prof { | ||||
| 		g.profile_fn(it.name, is_main) | ||||
|  | @ -130,7 +176,9 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { | |||
| 	} | ||||
| 	g.writeln('}') | ||||
| 	g.defer_stmts = [] | ||||
| 	g.fn_decl = 0 | ||||
| 	if g.pref.printfn_list.len > 0 && g.last_fn_c_name in g.pref.printfn_list { | ||||
| 		println(g.out.after(fn_start_pos)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) write_defer_stmts_when_needed() { | ||||
|  | @ -145,7 +193,9 @@ fn (mut g Gen) write_defer_stmts_when_needed() { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) { | ||||
| fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) ([]string, []string) { | ||||
| 	mut fargs := []string{} | ||||
| 	mut fargtypes := []string{} | ||||
| 	no_names := args.len > 0 && args[0].name == 'arg_1' | ||||
| 	for i, arg in args { | ||||
| 		arg_type_sym := g.table.get_type_symbol(arg.typ) | ||||
|  | @ -164,6 +214,8 @@ fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) { | |||
| 			if !info.is_anon { | ||||
| 				g.write(arg_type_name + ' ' + arg.name) | ||||
| 				g.definitions.write(arg_type_name + ' ' + arg.name) | ||||
| 				fargs << arg.name | ||||
| 				fargtypes << arg_type_name | ||||
| 			} else { | ||||
| 				g.write('${g.typ(func.return_type)} (*$arg.name)(') | ||||
| 				g.definitions.write('${g.typ(func.return_type)} (*$arg.name)(') | ||||
|  | @ -174,6 +226,8 @@ fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) { | |||
| 		} else if no_names { | ||||
| 			g.write(arg_type_name) | ||||
| 			g.definitions.write(arg_type_name) | ||||
| 			fargs << '' | ||||
| 			fargtypes << arg_type_name | ||||
| 		} else { | ||||
| 			mut nr_muls := arg.typ.nr_muls() | ||||
| 			s := arg_type_name + ' ' + arg.name | ||||
|  | @ -186,12 +240,15 @@ fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) { | |||
| 			// }
 | ||||
| 			g.write(s) | ||||
| 			g.definitions.write(s) | ||||
| 			fargs << arg.name | ||||
| 			fargtypes << arg_type_name | ||||
| 		} | ||||
| 		if i < args.len - 1 { | ||||
| 			g.write(', ') | ||||
| 			g.definitions.write(', ') | ||||
| 		} | ||||
| 	} | ||||
| 	return fargs, fargtypes | ||||
| } | ||||
| 
 | ||||
| fn (mut g Gen) call_expr(node ast.CallExpr) { | ||||
|  |  | |||
|  | @ -0,0 +1,263 @@ | |||
| module gen | ||||
| 
 | ||||
| import os | ||||
| import time | ||||
| import v.pref | ||||
| import v.util | ||||
| 
 | ||||
| fn (g &Gen) generate_hotcode_reloading_declarations() { | ||||
| 	if g.pref.os != .windows { | ||||
| 		if g.pref.is_livemain { | ||||
| 			g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex = PTHREAD_MUTEX_INITIALIZER;') | ||||
| 		} | ||||
| 		if g.pref.is_liveshared { | ||||
| 			g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex;') | ||||
| 		} | ||||
| 	} else { | ||||
| 		if g.pref.is_livemain { | ||||
| 			g.hotcode_definitions.writeln('HANDLE live_fn_mutex = 0;') | ||||
| 		} | ||||
| 		if g.pref.is_liveshared { | ||||
| 			g.hotcode_definitions.writeln('HANDLE live_fn_mutex;') | ||||
| 			g.hotcode_definitions.writeln(' | ||||
| void pthread_mutex_lock(HANDLE *m) { | ||||
| 	WaitForSingleObject(*m, INFINITE); | ||||
| } | ||||
| 
 | ||||
| void pthread_mutex_unlock(HANDLE *m) { | ||||
| 	ReleaseMutex(*m); | ||||
| }') | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (g &Gen) generate_hotcode_reloader_code() { | ||||
| 	if g.pref.is_liveshared { | ||||
| 		g.hotcode_definitions.writeln('') | ||||
| 		g.hotcode_definitions.writeln('int load_so(byteptr path) { return 0; }') | ||||
| 		g.hotcode_definitions.writeln('') | ||||
| 		return | ||||
| 	} | ||||
| 	// Hot code reloading
 | ||||
| 	if g.pref.is_livemain { | ||||
| 		mut file := os.real_path(g.pref.path) | ||||
| 		file_base := os.file_name(file).replace('.v', '') | ||||
| 		// Need to build .so file before building the live application
 | ||||
| 		// The live app needs to load this .so file on initialization.
 | ||||
| 		mut vexe := pref.vexe_path() | ||||
| 		mut so_dir := os.cache_dir() | ||||
| 		if os.user_os() == 'windows' { | ||||
| 			vexe = util.cescaped_path(vexe) | ||||
| 			file = util.cescaped_path(file) | ||||
| 			so_dir = util.cescaped_path(so_dir) | ||||
| 		} | ||||
| 		mut msvc := '' | ||||
| 		if g.pref.ccompiler == 'msvc' { | ||||
| 			msvc = '-cc msvc' | ||||
| 		} | ||||
| 		so_debug_flag := if g.pref.is_debug { '-cg' } else { '' } | ||||
| 		cmd_compile_shared_library := '$vexe $msvc -cg -keepc $so_debug_flag -o ${so_dir}/${file_base} -sharedlive -shared $file' | ||||
| 		if g.pref.is_verbose { | ||||
| 			println(cmd_compile_shared_library) | ||||
| 		} | ||||
| 		ticks := time.ticks() | ||||
| 		so_compilation_result := os.system(cmd_compile_shared_library) | ||||
| 		if g.pref.is_verbose { | ||||
| 			diff := time.ticks() - ticks | ||||
| 			println('compiling shared library took $diff ms') | ||||
| 		} | ||||
| 		if so_compilation_result != 0 { | ||||
| 			exit(1) | ||||
| 		}		 | ||||
| 		mut phd := '' | ||||
| 		mut load_code := []string{} | ||||
| 		if g.pref.os != .windows { | ||||
| 			for so_fn in g.hotcode_fn_names { | ||||
| 				load_code << 'impl_live_${so_fn} = dlsym(live_lib, "impl_live_${so_fn}");' | ||||
| 			} | ||||
| 			phd = posix_hotcode_definitions_1 | ||||
| 		} else { | ||||
| 			for so_fn in g.hotcode_fn_names { | ||||
| 				load_code << 'impl_live_${so_fn} = (void *)GetProcAddress(live_lib, "impl_live_${so_fn}");  ' | ||||
| 			} | ||||
| 			phd = windows_hotcode_definitions_1 | ||||
| 		} | ||||
| 		g.hotcode_definitions.writeln(phd.replace('@LOAD_FNS@', load_code.join('\n'))) | ||||
| 		g.hotcode_definitions.writeln(' | ||||
| 
 | ||||
| void lfnmutex_print(char *s){ | ||||
| #if 0 | ||||
| 	fflush(stderr); | ||||
| 	fprintf(stderr,">> live_fn_mutex: %p | %s\\n", &live_fn_mutex, s); | ||||
| 	fflush(stderr); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void remove_so_file(char *s){ | ||||
| 	// removing the .so file from the filesystem after dlopen-ing it is safe, since it will still be mapped in memory.
 | ||||
| 	#ifndef _WIN32 | ||||
| 		unlink(s);  | ||||
| 	#else | ||||
| 		_unlink(s); | ||||
| 	#endif | ||||
| } | ||||
| 
 | ||||
| int _live_reloads = 0; | ||||
| void reload_so() { | ||||
| 	char new_so_base[PATH_MAX] = {0}; | ||||
| 	char new_so_name[PATH_MAX] = {0}; | ||||
| 	char compile_cmd[PATH_MAX] = {0}; | ||||
| 	int last = os__file_last_mod_unix(tos2("$file")); | ||||
| 	while (1) { | ||||
| 		// TODO use inotify
 | ||||
| 		int now = os__file_last_mod_unix(tos2("$file")); | ||||
| 		if (now != last) { | ||||
| 			last = now; | ||||
| 			_live_reloads++; | ||||
| 
 | ||||
| 			//v -o bounce -sharedlive -shared bounce.v
 | ||||
| 			snprintf(new_so_base, sizeof (new_so_base), "${so_dir}/tmp.%d.${file_base}", _live_reloads); | ||||
| 			#ifdef _MSC_VER | ||||
| 			snprintf(new_so_name, sizeof (new_so_name), "%s.dll", new_so_base); | ||||
| 			#else | ||||
| 			snprintf(new_so_name, sizeof (new_so_name), "%s.so", new_so_base); | ||||
| 			#endif | ||||
| 			snprintf(compile_cmd, sizeof (compile_cmd), "$vexe $msvc $so_debug_flag -cg -keepc -o %s -sharedlive -shared $file", new_so_base); | ||||
| 			os__system(tos2(compile_cmd)); | ||||
| 
 | ||||
| 			if( !os__exists(tos2(new_so_name)) ) { | ||||
| 				puts("Errors while compiling $file\\n"); | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			lfnmutex_print("reload_so locking..."); | ||||
| 			pthread_mutex_lock(&live_fn_mutex); | ||||
| 			lfnmutex_print("reload_so locked"); | ||||
| 
 | ||||
| 			load_so(new_so_name); | ||||
| 			remove_so_file( new_so_name ); | ||||
| 
 | ||||
| 			lfnmutex_print("reload_so unlocking..."); | ||||
| 			pthread_mutex_unlock(&live_fn_mutex); | ||||
| 			lfnmutex_print("reload_so unlocked"); | ||||
| 		} | ||||
| 		time__sleep_ms(100); | ||||
| 	} | ||||
| } | ||||
| ') | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| const ( | ||||
| 	posix_hotcode_definitions_1 = ' | ||||
| #include <limits.h> | ||||
| #ifndef PATH_MAX | ||||
| #define PATH_MAX 1024 | ||||
| #endif | ||||
| void* live_lib = 0; | ||||
| int load_so(byteptr path) { | ||||
| 	char cpath[PATH_MAX] = {0}; | ||||
| 	int res = snprintf(cpath, sizeof(cpath), "%s", path); | ||||
| 	if (res >= sizeof (cpath)) { | ||||
| 		fprintf (stderr, "path is too long"); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	if (live_lib) { | ||||
| 		int closing_status = dlclose(live_lib); | ||||
| 		//fprintf(stderr, "Closing status: %d\\n", closing_status);
 | ||||
| 		live_lib = 0; | ||||
| 	} | ||||
| 	//fprintf (stderr, "Opening shared library at: %s\\n", cpath);
 | ||||
| 	live_lib = dlopen(cpath, RTLD_LAZY); | ||||
| 	if (!live_lib) { | ||||
| 		fprintf(stderr, "open failed, reason: %s\\n", dlerror()); | ||||
| 		exit(1); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	dlerror(); // clear errors
 | ||||
| 	//fprintf(stderr, "live_lib: %p\\n", live_lib);
 | ||||
| 
 | ||||
|     @LOAD_FNS@ | ||||
| 
 | ||||
| 	char *dlsym_error = dlerror();  | ||||
| 	if (dlsym_error != NULL) { | ||||
| 		fprintf(stderr, "dlsym failed, reason: %s\\n", dlsym_error); | ||||
| 	} | ||||
|     return 1; | ||||
| } | ||||
| ' | ||||
| 	windows_hotcode_definitions_1 = ' | ||||
| #ifndef PATH_MAX | ||||
| #define PATH_MAX 1024 | ||||
| #endif | ||||
| void pthread_mutex_lock(HANDLE *m) { | ||||
| 	WaitForSingleObject(*m, INFINITE); | ||||
| } | ||||
| void pthread_mutex_unlock(HANDLE *m) { | ||||
| 	ReleaseMutex(*m); | ||||
| } | ||||
| void* live_lib = NULL; | ||||
| int load_so(byteptr path) { | ||||
| 	char cpath[PATH_MAX]; | ||||
| 	int res = snprintf(cpath, sizeof(cpath), "%s", path); | ||||
| 	if (res >= sizeof(cpath)) { | ||||
| 		puts("path is too long\\n"); | ||||
| 		exit(1); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	if (live_lib) FreeLibrary(live_lib); | ||||
| 	live_lib = LoadLibraryA(cpath); | ||||
| 	if (!live_lib) { | ||||
| 		puts("open failed\\n"); | ||||
| 		exit(1); | ||||
| 		return 0; | ||||
| 	} | ||||
|     @LOAD_FNS@ | ||||
|     return 1; | ||||
| } | ||||
| ' | ||||
| ) | ||||
| 
 | ||||
| //
 | ||||
| 
 | ||||
| fn (g &Gen) generate_hotcode_reloading_main_caller() { | ||||
| 	if !g.pref.is_livemain { | ||||
| 		return | ||||
| 	} | ||||
| 	g.writeln('') | ||||
| 	// We are in live code reload mode, so start the .so loader in the background
 | ||||
| 	file_base := os.file_name(g.pref.path).replace('.v', '') | ||||
| 	mut so_dir := os.cache_dir() | ||||
| 	if os.user_os() == 'windows' { | ||||
| 		so_dir = util.cescaped_path(so_dir) | ||||
| 	} | ||||
| 
 | ||||
| 	g.writeln('\t// live code initialization section:') | ||||
| 	g.writeln('\t{') | ||||
| 	g.writeln('\t\t// initialization of live function pointers') | ||||
| 	for fname in g.hotcode_fn_names { | ||||
| 		g.writeln('\t\timpl_live_${fname} = 0;') | ||||
| 	} | ||||
| 	 | ||||
| 	g.writeln('\t\t// start background reloading thread') | ||||
| 	if g.pref.os != .windows { | ||||
| 		// unix:
 | ||||
| 		so_name := file_base + '.so' | ||||
| 		g.writeln('\t\tchar *live_library_name = "${so_dir}/$so_name";') | ||||
| 		g.writeln('\t\tload_so(live_library_name);') | ||||
| 		g.writeln('\t\tpthread_t _thread_so;') | ||||
| 		g.writeln('\t\tpthread_create(&_thread_so , NULL, (void *)&reload_so, live_library_name);') | ||||
| 	} else { | ||||
| 		// windows:
 | ||||
| 		so_extension := if g.pref.ccompiler == 'msvc' { '.dll' } else { '.so' } | ||||
| 		so_name := file_base + so_extension | ||||
| 		g.writeln('\t\tchar *live_library_name = "${so_dir}/$so_name";') | ||||
| 		g.writeln('\t\tlive_fn_mutex = CreateMutexA(0, 0, 0);') | ||||
| 		g.writeln('\t\tload_so(live_library_name);') | ||||
| 		g.writeln('\t\tunsigned long _thread_so;') | ||||
| 		g.writeln('\t\t_thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0);') | ||||
| 	} | ||||
| 	g.writeln('\t}\t// end of live code initialization section') | ||||
| 	g.writeln('') | ||||
| } | ||||
|  | @ -1058,7 +1058,7 @@ fn (mut p Parser) return_stmt() ast.Return { | |||
| 
 | ||||
| // left hand side of `=` or `:=` in `a,b,c := 1,2,3`
 | ||||
| fn (mut p Parser) global_decl() ast.GlobalDecl { | ||||
| 	if !p.pref.translated && !p.pref.is_live && !p.builtin_mod && !p.pref.building_v && p.mod != | ||||
| 	if !p.pref.translated && !p.pref.is_livemain && !p.builtin_mod && !p.pref.building_v && p.mod != | ||||
| 		'ui' && p.mod != 'gg2' && p.mod != 'uiold' && !os.getwd().contains('/volt') && !p.pref.enable_globals { | ||||
| 		p.error('use `v --enable-globals ...` to enable globals') | ||||
| 	} | ||||
|  |  | |||
|  | @ -28,8 +28,9 @@ pub mut: | |||
| 	// nofmt            bool   // disable vfmt
 | ||||
| 	is_test             bool // `v test string_test.v`
 | ||||
| 	is_script           bool // single file mode (`v program.v`), main function can be skipped
 | ||||
| 	is_live             bool // main program that contains live/hot code
 | ||||
| 	is_shared               bool // an ordinary shared library, -shared, no matter if it is live or not
 | ||||
| 	is_livemain         bool // main program that contains live/hot code
 | ||||
| 	is_liveshared       bool // a shared library, that will be used in a -live main program
 | ||||
| 	is_shared           bool // an ordinary shared library, -shared, no matter if it is live or not
 | ||||
| 	is_prof             bool // benchmark every function
 | ||||
| 	profile_file        string // the profile results will be stored inside profile_file
 | ||||
| 	translated          bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc
 | ||||
|  | @ -80,7 +81,7 @@ pub mut: | |||
| 	mod                 string | ||||
| 	run_args            []string // `v run x.v 1 2 3` => `1 2 3`
 | ||||
| 	printfn_list        []string // a list of generated function names, whose source should be shown, for debugging
 | ||||
| 	print_v_files       bool     // when true, just print the list of all parsed .v files then stop. 
 | ||||
| 	print_v_files       bool     // when true, just print the list of all parsed .v files then stop.
 | ||||
| } | ||||
| 
 | ||||
| pub fn backend_from_string(s string) ?Backend { | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ fn pmessage() { | |||
| 
 | ||||
| fn main() { | ||||
| 	println('START') | ||||
| 	for i := 0; i<6*100; i++ { | ||||
| 	for i := 0; i<3*100; i++ { | ||||
| 		pmessage() | ||||
| 		time.sleep_ms(10) | ||||
| 	} | ||||
|  | @ -41,7 +41,7 @@ fn testsuite_end() { | |||
| 	eprintln('source: $source_file') | ||||
| 	eprintln('output: $output_file') | ||||
| 	$if !windows { | ||||
| 		os.system('cat $output_file') | ||||
| 		os.system('cat $output_file | sort | uniq -c | sort -n') | ||||
| 	} | ||||
| 	println('---------------------------------------------------------------------------') | ||||
| 	output_lines := os.read_lines(output_file) or { | ||||
|  | @ -57,8 +57,8 @@ fn testsuite_end() { | |||
| 	println('---------------------------------------------------------------------------') | ||||
| 	assert histogram['START'] > 0 | ||||
| 	assert histogram['END'] > 0 | ||||
| 	assert histogram['CHANGED'] + histogram['ANOTHER'] > 0 | ||||
| 	assert histogram['ORIGINAL'] > 0 | ||||
| 	assert histogram['CHANGED'] + histogram['ANOTHER'] > 0 | ||||
| } | ||||
| 
 | ||||
| fn change_source(new string) { | ||||
|  | @ -75,7 +75,7 @@ fn test_live_program_can_be_compiled() { | |||
| 	eprintln('Compiling and running with: $cmd') | ||||
| 	res := os.system(cmd) | ||||
| 	eprintln('... running in the background') | ||||
| 	time.sleep_ms(3000) | ||||
| 	time.sleep_ms(1500) | ||||
| 	assert res == 0 | ||||
| } | ||||
| 
 | ||||
|  | @ -90,6 +90,6 @@ fn test_live_program_can_be_changed_2() { | |||
| } | ||||
| 
 | ||||
| fn test_live_program_has_ended() { | ||||
| 	time.sleep_ms(10 * 1000) | ||||
| 	time.sleep_ms(3500) | ||||
| 	assert true | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue