compiler: module init function & init consts for cached modules

pull/2294/head
joe-conigliaro 2019-10-12 09:17:37 +11:00 committed by Alexander Medvednikov
parent c7e47e6884
commit 51388fea75
11 changed files with 164 additions and 59 deletions

View File

@ -66,7 +66,15 @@ fn (v mut V) cc() {
// Create the modules & out directory if it's not there. // Create the modules & out directory if it's not there.
out_dir := '$v_modules_path${os.PathSeparator}$v.dir' out_dir := '$v_modules_path${os.PathSeparator}$v.dir'
if !os.dir_exists(out_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 v.out_name = '${out_dir}.o' //v.out_name
println('Building ${v.out_name}...') println('Building ${v.out_name}...')
@ -120,16 +128,17 @@ fn (v mut V) cc() {
os.system('$vexe build module vlib/builtin') os.system('$vexe build module vlib/builtin')
} }
for imp in v.table.imports { for imp in v.table.imports {
if imp == 'webview' { if imp.contains('vweb') { continue } // not working
continue if imp == 'webview' { continue }
}
path := '$v_modules_path/vlib/${imp}.o' imp_path := imp.replace('.', os.PathSeparator)
println('adding ${imp}.o') path := '$v_modules_path/vlib/${imp_path}.o'
println('adding ${imp_path}.o')
if os.file_exists(path) { if os.file_exists(path) {
libs += ' ' + path libs += ' ' + path
} else { } else {
println('$path not found... building module $imp') println('$path not found... building module $imp')
os.system('$vexe build module vlib/$imp') os.system('$vexe build module vlib/$imp_path')
} }
} }
} }

View File

@ -150,7 +150,6 @@ typedef map map_string;
byteptr g_str_buf; byteptr g_str_buf;
int load_so(byteptr); int load_so(byteptr);
void reload_so(); void reload_so();
void init_consts();
' '
js_headers = ' js_headers = '
@ -176,10 +175,5 @@ var rune = function() {}
var map_string = function() {} var map_string = function() {}
var map_int = function() {} var map_int = function() {}
function init_consts() {
}
' '
) )

View File

@ -254,7 +254,8 @@ fn (p mut Parser) fn_decl() {
} }
// full mod function name // full mod function name
// os.exit ==> os__exit() // 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) f.name = p.prepend_mod(f.name)
} }
if p.first_pass() && receiver_typ.len == 0 { 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) p.gen_fn_decl(f, typ, str_args)
} }
} }
if is_fn_header { if is_fn_header {
p.genln('$typ $fn_name_cgen($str_args);') p.genln('$typ $fn_name_cgen($str_args);')
p.fgenln('') p.fgenln('')

View File

@ -227,6 +227,7 @@ fn (table mut Table) fn_gen_name(f &Fn) string {
f.mod != 'darwin' && f.mod != 'darwin' &&
f.mod != 'os' && f.mod != 'os' &&
f.mod != 'json' && f.mod != 'json' &&
!f.name.ends_with('_init') &&
!f.name.contains('window_proc') && !f.name.contains('window_proc') &&
!name.ends_with('_str') && !name.ends_with('_str') &&
!name.contains('contains') { !name.contains('contains') {

View File

@ -71,6 +71,7 @@ mut:
mod string // module being built with -lib mod string // module being built with -lib
parsers []Parser parsers []Parser
vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc) vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc)
cached_mods []string
} }
struct Preferences { struct Preferences {
@ -269,6 +270,15 @@ fn (v mut V) compile() {
for file in v.files { for file in v.files {
v.parse(file, .decl) 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 // Main pass
cgen.pass = Pass.main cgen.pass = Pass.main
if v.pref.is_debug { if v.pref.is_debug {
@ -334,6 +344,11 @@ fn (v mut V) compile() {
// new vfmt is not ready yet // 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 // Generate .vh if we are building a module
if v.pref.build_mode == .build_module { if v.pref.build_mode == .build_module {
v.generate_vh() v.generate_vh()
@ -345,9 +360,13 @@ fn (v mut V) compile() {
v.vgen_buf.free() v.vgen_buf.free()
vgen_parser.parse(.main) vgen_parser.parse(.main)
// v.parsers.add(vgen_parser) // v.parsers.add(vgen_parser)
v.log('Done parsing.')
// All definitions // All definitions
mut def := strings.new_builder(10000)// Avoid unnecessary allocations 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 { $if !js {
def.writeln(cgen.includes.join_lines()) def.writeln(cgen.includes.join_lines())
def.writeln(cgen.typedefs.join_lines()) def.writeln(cgen.typedefs.join_lines())
@ -365,6 +384,7 @@ fn (v mut V) compile() {
def.writeln(v.prof_counters()) def.writeln(v.prof_counters())
} }
cgen.lines[defs_pos] = def.str() cgen.lines[defs_pos] = def.str()
v.generate_init()
v.generate_main() v.generate_main()
v.generate_hot_reload_code() v.generate_hot_reload_code()
if v.pref.is_verbose { if v.pref.is_verbose {
@ -380,28 +400,68 @@ fn (v mut V) compile() {
v.cc() v.cc()
} }
fn (v mut V) generate_main() { fn (v mut V) module_gen_init_parsers() []Parser {
mut cgen := v.cgen 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
}
fn (v mut V) generate_init() {
$if js { return } $if js { return }
if v.pref.build_mode == .build_module {
///// After this point, the v files are compiled. nogen := v.cgen.nogen
///// The rest is auto generated code, which will not have v.cgen.nogen = false
///// different .v source file/line numbers. consts_init_body := v.cgen.consts_init.join_lines()
lines_so_far := cgen.lines.join('\n').count('\n') + 5 init_fn_name := mod_gen_name(v.mod) + '__init_consts'
cgen.genln('') println('generating init for $v.mod: $init_fn_name')
cgen.genln('////////////////// Reset the file/line numbers //////////') v.cgen.genln('void ${init_fn_name}() {\n$consts_init_body\n}')
cgen.lines << '#line $lines_so_far "${cescaped_path(os.realpath(cgen.out_path))}"' v.cgen.nogen = nogen
cgen.genln('') }
if v.pref.build_mode == .default_mode { 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()` // vlib can't have `init_consts()`
cgen.genln('void init_consts() { v.cgen.genln('void init() {
g_str_buf=malloc(1000); g_str_buf=malloc(1000);
$call_mod_init_consts
$consts_init_body $consts_init_body
builtin__init();
$call_mod_init
}') }')
// _STR function can't be defined in vlib // _STR function can't be defined in vlib
cgen.genln(' v.cgen.genln('
string _STR(const char *fmt, ...) { string _STR(const char *fmt, ...) {
va_list argptr; va_list argptr;
va_start(argptr, fmt); 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 // Make sure the main function exists
// Obviously we don't need it in libraries // 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){ fn (v mut V) gen_main_start(add_os_args bool){
v.cgen.genln('int main(int argc, char** argv) { ') 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 { if add_os_args && 'os' in v.table.imports {
v.cgen.genln(' os__args = os__init_os_args(argc, (byteptr*)argv);') 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 // Parse user imports
for file in v.get_user_files() { for file in v.get_user_files() {
mut p := v.new_parser_from_file(file) 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) p.parse(.imports)
//if p.pref.autofree { p.scanner.text.free() free(p.scanner) } //if p.pref.autofree { p.scanner.text.free() free(p.scanner) }
v.add_parser(p) v.add_parser(p)
@ -617,11 +695,12 @@ fn (v mut V) add_v_files_to_compile() {
} }
// use cached built module if exists // use cached built module if exists
if v.pref.build_mode != .build_module { if v.pref.build_mode != .build_module && !mod.contains('vweb') {
vh_path := '$v_modules_path/${mod}.vh' mod_path := mod.replace('.', os.PathSeparator)
//println(vh_path) vh_path := '$v_modules_path/${mod_path}.vh'
if v.pref.is_debug && os.file_exists(vh_path) { if v.pref.is_debug && os.file_exists(vh_path) {
println('using cached module `$mod`: $vh_path') println('using cached module `$mod`: $vh_path')
v.cached_mods << mod
v.files << vh_path v.files << vh_path
continue continue
} }
@ -791,15 +870,18 @@ fn new_v(args[]string) &V {
if joined_args.contains('build module ') { if joined_args.contains('build module ') {
build_mode = .build_module build_mode = .build_module
// v build module ~/v/os => os.o // 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) adir.all_after(os.PathSeparator)
} else { } else {
adir adir
} }
mod = mod_path.replace(os.PathSeparator, '.')
println('Building module "${mod}" (dir="$dir")...') println('Building module "${mod}" (dir="$dir")...')
//out_name = '$TmpPath/vlib/${base}.o' //out_name = '$TmpPath/vlib/${base}.o'
out_name = mod + '.o' out_name = mod
println('$out_name')
// Cross compiling? Use separate dirs for each os // Cross compiling? Use separate dirs for each os
/* /*
if target_os != os.user_os() { if target_os != os.user_os() {

View File

@ -106,17 +106,27 @@ fn v_type_str(typ_ string) string {
fn (v &V) generate_vh() { fn (v &V) generate_vh() {
println('\n\n\n\nGenerating a V header file for module `$v.mod`') 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' path := dir + '.vh'
if !os.dir_exists(dir) { 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) println(path)
file := os.create(path) or { panic(err) } file := os.create(path) or { panic(err) }
// Consts // Consts
mod_def := if v.mod.contains('.') { v.mod.all_after('.') } else { v.mod }
file.writeln('// $v.mod module header \n') file.writeln('// $v.mod module header \n')
file.writeln('module $v.mod') file.writeln('module $mod_def')
file.writeln('// Consts') file.writeln('// Consts')
if v.table.consts.len > 0 { if v.table.consts.len > 0 {
file.writeln('const (') file.writeln('const (')

View File

@ -60,3 +60,11 @@ fn (v &V) find_module_path(mod string) ?string {
} }
return import_path 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_', '.')
}

View File

@ -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) //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 // `module main` is not required if it's a single file program
if p.is_script || p.pref.is_test { if p.is_script || p.pref.is_test {
p.mod = 'main'
// User may still specify `module main` // User may still specify `module main`
if p.tok == .key_module { if p.tok == .key_module {
p.next() p.next()
p.fgen('module ') p.fgen('module ')
p.mod = p.check_name() mod := p.check_name()
if p.mod == '' {
p.mod = mod
}
} else {
p.mod = 'main'
} }
} }
else { else {
p.check(.key_module) p.check(.key_module)
p.fspace() 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.builtin_mod = p.mod == 'builtin'
p.can_chash = p.mod=='ui' || p.mod == 'darwin'// TODO tmp remove p.can_chash = p.mod=='ui' || p.mod == 'darwin'// TODO tmp remove
// Import pass - the first and the smallest pass that only analyzes imports // 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) fq_mod := p.table.qualify_module(p.mod, p.file_path)
p.import_table.module_name = fq_mod p.import_table.module_name = fq_mod
p.table.register_module(fq_mod) p.table.register_module(fq_mod)
// replace "." with "_dot_" in module name for C variable names // 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 { if p.pass == .imports {
for p.tok == .key_import && p.peek() != .key_const { for p.tok == .key_import && p.peek() != .key_const {
p.imports() p.imports()
@ -1063,7 +1072,7 @@ fn (p mut Parser) get_type() string {
if !p.builtin_mod && p.import_table.known_alias(typ) { if !p.builtin_mod && p.import_table.known_alias(typ) {
mod := p.import_table.resolve_alias(typ) mod := p.import_table.resolve_alias(typ)
if mod.contains('.') { if mod.contains('.') {
typ = mod.replace('.', '_dot_') typ = mod_gen_name(mod)
} }
} }
p.next() p.next()
@ -1791,7 +1800,7 @@ fn (p mut Parser) name_expr() string {
p.import_table.register_used_import(name) p.import_table.register_used_import(name)
// we replaced "." with "_dot_" in p.mod for C variable names, // we replaced "." with "_dot_" in p.mod for C variable names,
// do same here. // 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.next()
p.check(.dot) 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 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) {
if p.table.known_mod(orig_name) || p.import_table.known_alias(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`') p.error('undefined: `$name`')
} }
else { else {

View File

@ -4,7 +4,7 @@
module builtin module builtin
fn builtin_init() int { fn init() {
$if windows { $if windows {
if is_atty(0) { if is_atty(0) {
C._setmode(C._fileno(C.stdin), C._O_U16TEXT) 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.SetConsoleMode(C.GetStdHandle(C.STD_OUTPUT_HANDLE), C.ENABLE_PROCESSED_OUTPUT | 0x0004) // ENABLE_VIRTUAL_TERMINAL_PROCESSING
C.setbuf(C.stdout,0) C.setbuf(C.stdout,0)
} }
return 1
} }
const (
_ = builtin_init()
)
fn C.memcpy(byteptr, byteptr, int) fn C.memcpy(byteptr, byteptr, int)
fn C.memmove(byteptr, byteptr, int) fn C.memmove(byteptr, byteptr, int)
//fn C.malloc(int) byteptr //fn C.malloc(int) byteptr

View File

@ -9,7 +9,6 @@ import http.chunked
const ( const (
max_redirects = 4 max_redirects = 4
_ = http.init()
) )
struct Request { struct Request {

View File

@ -1,9 +1,5 @@
module net module net
const (
_ = net.init()
)
// hostname returns the host name reported by the kernel. // hostname returns the host name reported by the kernel.
pub fn hostname() ?string { pub fn hostname() ?string {
mut name := [256]byte mut name := [256]byte