From 51388fea7565533a48866fdaa4551945300dbeab Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Sat, 12 Oct 2019 09:17:37 +1100 Subject: [PATCH] compiler: module init function & init consts for cached modules --- compiler/cc.v | 23 ++++--- compiler/cheaders.v | 6 -- compiler/fn.v | 4 +- compiler/gen_c.v | 1 + compiler/main.v | 128 ++++++++++++++++++++++++++++++++------- compiler/module_header.v | 16 ++++- compiler/modules.v | 8 +++ compiler/parser.v | 25 +++++--- vlib/builtin/builtin.v | 7 +-- vlib/http/http.v | 1 - vlib/net/net.v | 4 -- 11 files changed, 164 insertions(+), 59 deletions(-) diff --git a/compiler/cc.v b/compiler/cc.v index f4cdf9936d..393ec89356 100644 --- a/compiler/cc.v +++ b/compiler/cc.v @@ -66,7 +66,15 @@ fn (v mut V) cc() { // Create the modules & out directory if it's not there. out_dir := '$v_modules_path${os.PathSeparator}$v.dir' if !os.dir_exists(out_dir) { - os.mkdir(out_dir) + // create recursive + mut mkpath := v_modules_path + for subdir in v.dir.split(os.PathSeparator) { + mkpath += os.PathSeparator + subdir + if !os.dir_exists(mkpath) { + os.mkdir(mkpath) + } + } + //os.mkdir(out_dir) } v.out_name = '${out_dir}.o' //v.out_name println('Building ${v.out_name}...') @@ -120,16 +128,17 @@ fn (v mut V) cc() { os.system('$vexe build module vlib/builtin') } for imp in v.table.imports { - if imp == 'webview' { - continue - } - path := '$v_modules_path/vlib/${imp}.o' - println('adding ${imp}.o') + if imp.contains('vweb') { continue } // not working + if imp == 'webview' { continue } + + imp_path := imp.replace('.', os.PathSeparator) + path := '$v_modules_path/vlib/${imp_path}.o' + println('adding ${imp_path}.o') if os.file_exists(path) { libs += ' ' + path } else { println('$path not found... building module $imp') - os.system('$vexe build module vlib/$imp') + os.system('$vexe build module vlib/$imp_path') } } } diff --git a/compiler/cheaders.v b/compiler/cheaders.v index 2753c09f28..4978d1d6b0 100644 --- a/compiler/cheaders.v +++ b/compiler/cheaders.v @@ -150,7 +150,6 @@ typedef map map_string; byteptr g_str_buf; int load_so(byteptr); void reload_so(); -void init_consts(); ' js_headers = ' @@ -176,10 +175,5 @@ var rune = function() {} var map_string = function() {} var map_int = function() {} -function init_consts() { - -} - ' - ) diff --git a/compiler/fn.v b/compiler/fn.v index 8cf3572aa1..1c535146b3 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -254,7 +254,8 @@ fn (p mut Parser) fn_decl() { } // full mod function name // os.exit ==> os__exit() - if !is_c && !p.builtin_mod && receiver_typ.len == 0 { + // if !is_c && !p.builtin_mod && receiver_typ.len == 0 { + if !is_c && receiver_typ.len == 0 && (!p.builtin_mod || (p.builtin_mod && f.name == 'init')) { f.name = p.prepend_mod(f.name) } if p.first_pass() && receiver_typ.len == 0 { @@ -363,6 +364,7 @@ fn (p mut Parser) fn_decl() { p.gen_fn_decl(f, typ, str_args) } } + if is_fn_header { p.genln('$typ $fn_name_cgen($str_args);') p.fgenln('') diff --git a/compiler/gen_c.v b/compiler/gen_c.v index 6bbf42ae67..f9cc3f285d 100644 --- a/compiler/gen_c.v +++ b/compiler/gen_c.v @@ -227,6 +227,7 @@ fn (table mut Table) fn_gen_name(f &Fn) string { f.mod != 'darwin' && f.mod != 'os' && f.mod != 'json' && + !f.name.ends_with('_init') && !f.name.contains('window_proc') && !name.ends_with('_str') && !name.contains('contains') { diff --git a/compiler/main.v b/compiler/main.v index 1e48c9dc01..fd6e9bae07 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -71,6 +71,7 @@ mut: mod string // module being built with -lib parsers []Parser vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc) + cached_mods []string } struct Preferences { @@ -269,6 +270,15 @@ fn (v mut V) compile() { for file in v.files { v.parse(file, .decl) } + + // generate missing module init's + init_parsers := v.module_gen_init_parsers() + // run decl pass + for i in 0..init_parsers.len { + mut ip := init_parsers[i] + ip.parse(.decl) + } + // Main pass cgen.pass = Pass.main if v.pref.is_debug { @@ -334,6 +344,11 @@ fn (v mut V) compile() { // new vfmt is not ready yet } } + // run main parser on the init parsers + for i in 0..init_parsers.len { + mut ip := init_parsers[i] + ip.parse(.main) + } // Generate .vh if we are building a module if v.pref.build_mode == .build_module { v.generate_vh() @@ -345,9 +360,13 @@ fn (v mut V) compile() { v.vgen_buf.free() vgen_parser.parse(.main) // v.parsers.add(vgen_parser) - v.log('Done parsing.') + // All definitions mut def := strings.new_builder(10000)// Avoid unnecessary allocations + if v.pref.build_mode == .build_module { + init_fn_name := v.mod.replace('.', '__') + '__init_consts' + def.writeln('void $init_fn_name();') + } $if !js { def.writeln(cgen.includes.join_lines()) def.writeln(cgen.typedefs.join_lines()) @@ -365,6 +384,7 @@ fn (v mut V) compile() { def.writeln(v.prof_counters()) } cgen.lines[defs_pos] = def.str() + v.generate_init() v.generate_main() v.generate_hot_reload_code() if v.pref.is_verbose { @@ -380,28 +400,68 @@ fn (v mut V) compile() { v.cc() } -fn (v mut V) generate_main() { - mut cgen := v.cgen - $if js { return } +fn (v mut V) module_gen_init_parsers() []Parser { + mut parsers := []Parser + if v.pref.build_mode == .build_module { + init_fn_name := mod_gen_name(v.mod) + '__init' + if !v.table.known_fn(init_fn_name) { + mod_def := if v.mod.contains('.') { v.mod.all_after('.') } else { v.mod } + mut fn_v := 'module $mod_def\n\n' + fn_v += 'fn init() { /*println(\'$v.mod module init\')*/ }' + mut p := v.new_parser_from_string(fn_v, 'init_gen_$v.mod') + p.mod = v.mod + parsers << p + } + } + else if v.pref.build_mode == .default_mode { + for mod in v.table.imports { + if mod in v.cached_mods { continue } + init_fn_name := mod_gen_name(mod) + '__init' + if !v.table.known_fn(init_fn_name) { + mod_def := if mod.contains('.') { mod.all_after('.') } else { mod } + mut fn_v := 'module $mod_def\n\n' + fn_v += 'fn init() { /*println(\'$v.mod module init\')*/ }' + mut p := v.new_parser_from_string(fn_v, 'init_gen_$mod') + p.mod = mod + parsers << p + } + } + } + return parsers +} - ///// After this point, the v files are compiled. - ///// The rest is auto generated code, which will not have - ///// different .v source file/line numbers. - lines_so_far := cgen.lines.join('\n').count('\n') + 5 - cgen.genln('') - cgen.genln('////////////////// Reset the file/line numbers //////////') - cgen.lines << '#line $lines_so_far "${cescaped_path(os.realpath(cgen.out_path))}"' - cgen.genln('') - +fn (v mut V) generate_init() { + $if js { return } + if v.pref.build_mode == .build_module { + nogen := v.cgen.nogen + v.cgen.nogen = false + consts_init_body := v.cgen.consts_init.join_lines() + init_fn_name := mod_gen_name(v.mod) + '__init_consts' + println('generating init for $v.mod: $init_fn_name') + v.cgen.genln('void ${init_fn_name}() {\n$consts_init_body\n}') + v.cgen.nogen = nogen + } if v.pref.build_mode == .default_mode { - mut consts_init_body := cgen.consts_init.join_lines() + mut call_mod_init := '' + mut call_mod_init_consts := '' + for mod in v.table.imports { + init_fn_name := mod_gen_name(mod) + '__init' + call_mod_init += '${init_fn_name}();\n' + if mod in v.cached_mods { + call_mod_init_consts += '${init_fn_name}_consts();\n' + } + } + consts_init_body := v.cgen.consts_init.join_lines() // vlib can't have `init_consts()` - cgen.genln('void init_consts() { + v.cgen.genln('void init() { g_str_buf=malloc(1000); +$call_mod_init_consts $consts_init_body +builtin__init(); +$call_mod_init }') // _STR function can't be defined in vlib - cgen.genln(' + v.cgen.genln(' string _STR(const char *fmt, ...) { va_list argptr; va_start(argptr, fmt); @@ -435,6 +495,20 @@ string _STR_TMP(const char *fmt, ...) { ') } +} + +fn (v mut V) generate_main() { + mut cgen := v.cgen + $if js { return } + + ///// After this point, the v files are compiled. + ///// The rest is auto generated code, which will not have + ///// different .v source file/line numbers. + lines_so_far := cgen.lines.join('\n').count('\n') + 5 + cgen.genln('') + cgen.genln('////////////////// Reset the file/line numbers //////////') + cgen.lines << '#line $lines_so_far "${cescaped_path(os.realpath(cgen.out_path))}"' + cgen.genln('') // Make sure the main function exists // Obviously we don't need it in libraries @@ -483,7 +557,7 @@ string _STR_TMP(const char *fmt, ...) { fn (v mut V) gen_main_start(add_os_args bool){ v.cgen.genln('int main(int argc, char** argv) { ') - v.cgen.genln(' init_consts();') + v.cgen.genln(' init();') if add_os_args && 'os' in v.table.imports { v.cgen.genln(' os__args = os__init_os_args(argc, (byteptr*)argv);') } @@ -597,6 +671,10 @@ fn (v mut V) add_v_files_to_compile() { // Parse user imports for file in v.get_user_files() { mut p := v.new_parser_from_file(file) + // set mod so we dont have to resolve submodule + if v.pref.build_mode == .build_module && file.contains(v.mod.replace('.', os.PathSeparator)) { + p.mod = v.mod + } p.parse(.imports) //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } v.add_parser(p) @@ -617,11 +695,12 @@ fn (v mut V) add_v_files_to_compile() { } // use cached built module if exists - if v.pref.build_mode != .build_module { - vh_path := '$v_modules_path/${mod}.vh' - //println(vh_path) + if v.pref.build_mode != .build_module && !mod.contains('vweb') { + mod_path := mod.replace('.', os.PathSeparator) + vh_path := '$v_modules_path/${mod_path}.vh' if v.pref.is_debug && os.file_exists(vh_path) { println('using cached module `$mod`: $vh_path') + v.cached_mods << mod v.files << vh_path continue } @@ -791,15 +870,18 @@ fn new_v(args[]string) &V { if joined_args.contains('build module ') { build_mode = .build_module // v build module ~/v/os => os.o - mod = if adir.contains(os.PathSeparator) { + mod_path := if adir.contains('vlib') { + adir.all_after('vlib'+os.PathSeparator) + } + else if adir.contains(os.PathSeparator) { adir.all_after(os.PathSeparator) } else { adir } + mod = mod_path.replace(os.PathSeparator, '.') println('Building module "${mod}" (dir="$dir")...') //out_name = '$TmpPath/vlib/${base}.o' - out_name = mod + '.o' - println('$out_name') + out_name = mod // Cross compiling? Use separate dirs for each os /* if target_os != os.user_os() { diff --git a/compiler/module_header.v b/compiler/module_header.v index f3d3c063e2..a75da90a91 100644 --- a/compiler/module_header.v +++ b/compiler/module_header.v @@ -106,17 +106,27 @@ fn v_type_str(typ_ string) string { fn (v &V) generate_vh() { println('\n\n\n\nGenerating a V header file for module `$v.mod`') - dir := '$v_modules_path${os.PathSeparator}$v.mod' + mod_path := v.mod.replace('.', os.PathSeparator) + dir := '$v_modules_path${os.PathSeparator}$mod_path' path := dir + '.vh' if !os.dir_exists(dir) { - os.mkdir(dir) + // create recursive + mut mkpath := v_modules_path + for subdir in mod_path.split(os.PathSeparator) { + mkpath += os.PathSeparator + subdir + if !os.dir_exists(mkpath) { + os.mkdir(mkpath) + } + } + // os.mkdir(os.realpath(dir)) } println(path) file := os.create(path) or { panic(err) } // Consts + mod_def := if v.mod.contains('.') { v.mod.all_after('.') } else { v.mod } file.writeln('// $v.mod module header \n') - file.writeln('module $v.mod') + file.writeln('module $mod_def') file.writeln('// Consts') if v.table.consts.len > 0 { file.writeln('const (') diff --git a/compiler/modules.v b/compiler/modules.v index b47e06a60c..f06e0e2bb3 100644 --- a/compiler/modules.v +++ b/compiler/modules.v @@ -60,3 +60,11 @@ fn (v &V) find_module_path(mod string) ?string { } return import_path } + +fn mod_gen_name(mod string) string { + return mod.replace('.', '_dot_') +} + +fn mod_gen_name_rev(mod string) string { + return mod.replace('_dot_', '.') +} \ No newline at end of file diff --git a/compiler/parser.v b/compiler/parser.v index 3211ef32fd..9568ea762c 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -245,18 +245,26 @@ fn (p mut Parser) parse(pass Pass) { //p.log('\nparse() run=$p.pass file=$p.file_name tok=${p.strtok()}')// , "script_file=", script_file) // `module main` is not required if it's a single file program if p.is_script || p.pref.is_test { - p.mod = 'main' // User may still specify `module main` if p.tok == .key_module { p.next() p.fgen('module ') - p.mod = p.check_name() + mod := p.check_name() + if p.mod == '' { + p.mod = mod + } + } else { + p.mod = 'main' } } else { p.check(.key_module) p.fspace() - p.mod = p.check_name() + // setting mod manually for mod init parsers + mod := p.check_name() + if p.mod == '' { + p.mod = mod + } } // @@ -270,12 +278,13 @@ fn (p mut Parser) parse(pass Pass) { p.builtin_mod = p.mod == 'builtin' p.can_chash = p.mod=='ui' || p.mod == 'darwin'// TODO tmp remove // Import pass - the first and the smallest pass that only analyzes imports - // fully qualify the module name, eg base64 to encoding.base64 + fq_mod := p.table.qualify_module(p.mod, p.file_path) p.import_table.module_name = fq_mod p.table.register_module(fq_mod) // replace "." with "_dot_" in module name for C variable names - p.mod = fq_mod.replace('.', '_dot_') + p.mod = mod_gen_name(fq_mod) + if p.pass == .imports { for p.tok == .key_import && p.peek() != .key_const { p.imports() @@ -1063,7 +1072,7 @@ fn (p mut Parser) get_type() string { if !p.builtin_mod && p.import_table.known_alias(typ) { mod := p.import_table.resolve_alias(typ) if mod.contains('.') { - typ = mod.replace('.', '_dot_') + typ = mod_gen_name(mod) } } p.next() @@ -1791,7 +1800,7 @@ fn (p mut Parser) name_expr() string { p.import_table.register_used_import(name) // we replaced "." with "_dot_" in p.mod for C variable names, // do same here. - mod = p.import_table.resolve_alias(name).replace('.', '_dot_') + mod = mod_gen_name(p.import_table.resolve_alias(name)) } p.next() p.check(.dot) @@ -1956,7 +1965,7 @@ fn (p mut Parser) name_expr() string { // If orig_name is a mod, then printing undefined: `mod` tells us nothing // if p.table.known_mod(orig_name) { if p.table.known_mod(orig_name) || p.import_table.known_alias(orig_name) { - name = name.replace('__', '.').replace('_dot_', '.') + name = mod_gen_name_rev(name.replace('__', '.')) p.error('undefined: `$name`') } else { diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index 90e216437e..e630f40dbf 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -4,7 +4,7 @@ module builtin -fn builtin_init() int { +fn init() { $if windows { if is_atty(0) { C._setmode(C._fileno(C.stdin), C._O_U16TEXT) @@ -15,13 +15,8 @@ fn builtin_init() int { C.SetConsoleMode(C.GetStdHandle(C.STD_OUTPUT_HANDLE), C.ENABLE_PROCESSED_OUTPUT | 0x0004) // ENABLE_VIRTUAL_TERMINAL_PROCESSING C.setbuf(C.stdout,0) } - return 1 } -const ( - _ = builtin_init() -) - fn C.memcpy(byteptr, byteptr, int) fn C.memmove(byteptr, byteptr, int) //fn C.malloc(int) byteptr diff --git a/vlib/http/http.v b/vlib/http/http.v index 8493be3d3f..9271c98870 100644 --- a/vlib/http/http.v +++ b/vlib/http/http.v @@ -9,7 +9,6 @@ import http.chunked const ( max_redirects = 4 - _ = http.init() ) struct Request { diff --git a/vlib/net/net.v b/vlib/net/net.v index 2101d36cc8..c04f402fc4 100644 --- a/vlib/net/net.v +++ b/vlib/net/net.v @@ -1,9 +1,5 @@ module net -const ( - _ = net.init() -) - // hostname returns the host name reported by the kernel. pub fn hostname() ?string { mut name := [256]byte