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.
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')
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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_', '.')
}

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)
// `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 {

View File

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

View File

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

View File

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