mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
cgen: move spawn
and go
logic to a separate file
This commit is contained in:
parent
50247d5093
commit
2162230086
@ -2089,374 +2089,6 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) go_expr(node ast.GoExpr) {
|
|
||||||
g.writeln('/*go (coroutine) */')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) spawn_expr(node ast.SpawnExpr) {
|
|
||||||
g.writeln('/*spawn (thread) */')
|
|
||||||
line := g.go_before_stmt(0)
|
|
||||||
mut handle := ''
|
|
||||||
tmp := g.new_tmp_var()
|
|
||||||
mut expr := node.call_expr
|
|
||||||
mut name := expr.name
|
|
||||||
mut use_tmp_fn_var := false
|
|
||||||
tmp_fn := g.new_tmp_var()
|
|
||||||
|
|
||||||
if expr.concrete_types.len > 0 {
|
|
||||||
name = g.generic_fn_name(expr.concrete_types, name)
|
|
||||||
} else if expr.is_fn_var && expr.fn_var_type.has_flag(.generic) {
|
|
||||||
fn_var_type := g.unwrap_generic(expr.fn_var_type)
|
|
||||||
name = g.typ(fn_var_type)
|
|
||||||
}
|
|
||||||
|
|
||||||
if expr.is_method {
|
|
||||||
receiver_sym := g.table.sym(g.unwrap_generic(expr.receiver_type))
|
|
||||||
name = receiver_sym.cname + '_' + name
|
|
||||||
} else if mut expr.left is ast.AnonFn {
|
|
||||||
if expr.left.inherited_vars.len > 0 {
|
|
||||||
fn_var := g.fn_var_signature(expr.left.decl.return_type, expr.left.decl.params.map(it.typ),
|
|
||||||
tmp_fn)
|
|
||||||
g.write('\t${fn_var} = ')
|
|
||||||
g.gen_anon_fn(mut expr.left)
|
|
||||||
g.writeln(';')
|
|
||||||
use_tmp_fn_var = true
|
|
||||||
} else {
|
|
||||||
g.gen_anon_fn_decl(mut expr.left)
|
|
||||||
name = expr.left.decl.name
|
|
||||||
}
|
|
||||||
} else if expr.left is ast.IndexExpr {
|
|
||||||
if expr.is_fn_var {
|
|
||||||
fn_sym := g.table.sym(expr.fn_var_type)
|
|
||||||
func := (fn_sym.info as ast.FnType).func
|
|
||||||
fn_var := g.fn_var_signature(func.return_type, func.params.map(it.typ), tmp_fn)
|
|
||||||
g.write('\t${fn_var} = ')
|
|
||||||
g.expr(expr.left)
|
|
||||||
g.writeln(';')
|
|
||||||
name = fn_sym.cname
|
|
||||||
use_tmp_fn_var = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
name = util.no_dots(name)
|
|
||||||
if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') {
|
|
||||||
mut key := expr.name
|
|
||||||
if expr.is_method {
|
|
||||||
sym := g.table.sym(expr.receiver_type)
|
|
||||||
key = sym.name + '.' + expr.name
|
|
||||||
}
|
|
||||||
g.write('/* obf go: ${key} */')
|
|
||||||
name = g.obf_table[key] or {
|
|
||||||
panic('cgen: obf name "${key}" not found, this should never happen')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.empty_line = true
|
|
||||||
g.writeln('// start go')
|
|
||||||
wrapper_struct_name := 'thread_arg_' + name
|
|
||||||
wrapper_fn_name := name + '_thread_wrapper'
|
|
||||||
arg_tmp_var := 'arg_' + tmp
|
|
||||||
g.writeln('${wrapper_struct_name} *${arg_tmp_var} = malloc(sizeof(thread_arg_${name}));')
|
|
||||||
fn_name := if use_tmp_fn_var {
|
|
||||||
tmp_fn
|
|
||||||
} else if expr.is_fn_var {
|
|
||||||
expr.name
|
|
||||||
} else {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
if !(expr.is_method && g.table.sym(expr.receiver_type).kind == .interface_) {
|
|
||||||
g.writeln('${arg_tmp_var}->fn = ${fn_name};')
|
|
||||||
}
|
|
||||||
if expr.is_method {
|
|
||||||
g.write('${arg_tmp_var}->arg0 = ')
|
|
||||||
g.expr(expr.left)
|
|
||||||
g.writeln(';')
|
|
||||||
}
|
|
||||||
for i, arg in expr.args {
|
|
||||||
g.write('${arg_tmp_var}->arg${i + 1} = ')
|
|
||||||
g.expr(arg.expr)
|
|
||||||
g.writeln(';')
|
|
||||||
}
|
|
||||||
s_ret_typ := g.typ(node.call_expr.return_type)
|
|
||||||
if g.pref.os == .windows && node.call_expr.return_type != ast.void_type {
|
|
||||||
g.writeln('${arg_tmp_var}->ret_ptr = malloc(sizeof(${s_ret_typ}));')
|
|
||||||
}
|
|
||||||
is_opt := node.call_expr.return_type.has_flag(.option)
|
|
||||||
is_res := node.call_expr.return_type.has_flag(.result)
|
|
||||||
mut gohandle_name := ''
|
|
||||||
if node.call_expr.return_type == ast.void_type {
|
|
||||||
if is_opt {
|
|
||||||
gohandle_name = '__v_thread_Option_void'
|
|
||||||
} else if is_res {
|
|
||||||
gohandle_name = '__v_thread_Result_void'
|
|
||||||
} else {
|
|
||||||
gohandle_name = '__v_thread'
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
opt := if is_opt { '${option_name}_' } else { '' }
|
|
||||||
res := if is_res { '${result_name}_' } else { '' }
|
|
||||||
gohandle_name = '__v_thread_${opt}${res}${g.table.sym(g.unwrap_generic(node.call_expr.return_type)).cname}'
|
|
||||||
}
|
|
||||||
if g.pref.os == .windows {
|
|
||||||
simple_handle := if node.is_expr && node.call_expr.return_type != ast.void_type {
|
|
||||||
'thread_handle_${tmp}'
|
|
||||||
} else {
|
|
||||||
'thread_${tmp}'
|
|
||||||
}
|
|
||||||
stack_size := g.get_cur_thread_stack_size(expr.name)
|
|
||||||
g.writeln('HANDLE ${simple_handle} = CreateThread(0, ${stack_size}, (LPTHREAD_START_ROUTINE)${wrapper_fn_name}, ${arg_tmp_var}, 0, 0); // fn: ${expr.name}')
|
|
||||||
g.writeln('if (!${simple_handle}) panic_lasterr(tos3("`go ${name}()`: "));')
|
|
||||||
if node.is_expr && node.call_expr.return_type != ast.void_type {
|
|
||||||
g.writeln('${gohandle_name} thread_${tmp} = {')
|
|
||||||
g.writeln('\t.ret_ptr = ${arg_tmp_var}->ret_ptr,')
|
|
||||||
g.writeln('\t.handle = thread_handle_${tmp}')
|
|
||||||
g.writeln('};')
|
|
||||||
}
|
|
||||||
if !node.is_expr {
|
|
||||||
g.writeln('CloseHandle(thread_${tmp});')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
g.writeln('pthread_t thread_${tmp};')
|
|
||||||
mut sthread_attributes := 'NULL'
|
|
||||||
if g.pref.os != .vinix {
|
|
||||||
g.writeln('pthread_attr_t thread_${tmp}_attributes;')
|
|
||||||
g.writeln('pthread_attr_init(&thread_${tmp}_attributes);')
|
|
||||||
size := g.get_cur_thread_stack_size(expr.name)
|
|
||||||
g.writeln('pthread_attr_setstacksize(&thread_${tmp}_attributes, ${size}); // fn: ${expr.name}')
|
|
||||||
sthread_attributes = '&thread_${tmp}_attributes'
|
|
||||||
}
|
|
||||||
g.writeln('int ${tmp}_thr_res = pthread_create(&thread_${tmp}, ${sthread_attributes}, (void*)${wrapper_fn_name}, ${arg_tmp_var});')
|
|
||||||
g.writeln('if (${tmp}_thr_res) panic_error_number(tos3("`go ${name}()`: "), ${tmp}_thr_res);')
|
|
||||||
if !node.is_expr {
|
|
||||||
g.writeln('pthread_detach(thread_${tmp});')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.writeln('// end go')
|
|
||||||
if node.is_expr {
|
|
||||||
handle = 'thread_${tmp}'
|
|
||||||
// create wait handler for this return type if none exists
|
|
||||||
waiter_fn_name := gohandle_name + '_wait'
|
|
||||||
mut should_register := false
|
|
||||||
lock g.waiter_fns {
|
|
||||||
if waiter_fn_name !in g.waiter_fns {
|
|
||||||
g.waiter_fns << waiter_fn_name
|
|
||||||
should_register = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if should_register {
|
|
||||||
g.gowrappers.writeln('\n${s_ret_typ} ${waiter_fn_name}(${gohandle_name} thread) {')
|
|
||||||
mut c_ret_ptr_ptr := 'NULL'
|
|
||||||
if node.call_expr.return_type != ast.void_type {
|
|
||||||
g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr;')
|
|
||||||
c_ret_ptr_ptr = '&ret_ptr'
|
|
||||||
}
|
|
||||||
if g.pref.os == .windows {
|
|
||||||
if node.call_expr.return_type == ast.void_type {
|
|
||||||
g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);')
|
|
||||||
} else {
|
|
||||||
g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);')
|
|
||||||
g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)${c_ret_ptr_ptr});')
|
|
||||||
}
|
|
||||||
g.gowrappers.writeln('\tif (stat != 0) { _v_panic(_SLIT("unable to join thread")); }')
|
|
||||||
if g.pref.os == .windows {
|
|
||||||
if node.call_expr.return_type == ast.void_type {
|
|
||||||
g.gowrappers.writeln('\tCloseHandle(thread);')
|
|
||||||
} else {
|
|
||||||
g.gowrappers.writeln('\tCloseHandle(thread.handle);')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if node.call_expr.return_type != ast.void_type {
|
|
||||||
g.gowrappers.writeln('\t${s_ret_typ} ret = *ret_ptr;')
|
|
||||||
g.gowrappers.writeln('\tfree(ret_ptr);')
|
|
||||||
g.gowrappers.writeln('\treturn ret;')
|
|
||||||
}
|
|
||||||
g.gowrappers.writeln('}')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Register the wrapper type and function
|
|
||||||
mut should_register := false
|
|
||||||
lock g.threaded_fns {
|
|
||||||
if name !in g.threaded_fns {
|
|
||||||
g.threaded_fns << name
|
|
||||||
should_register = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if should_register {
|
|
||||||
g.type_definitions.writeln('\ntypedef struct ${wrapper_struct_name} {')
|
|
||||||
mut fn_var := ''
|
|
||||||
if node.call_expr.is_fn_var {
|
|
||||||
fn_sym := g.table.sym(node.call_expr.fn_var_type)
|
|
||||||
info := fn_sym.info as ast.FnType
|
|
||||||
fn_var = g.fn_var_signature(info.func.return_type, info.func.params.map(it.typ),
|
|
||||||
'fn')
|
|
||||||
} else if node.call_expr.left is ast.AnonFn {
|
|
||||||
f := node.call_expr.left.decl
|
|
||||||
fn_var = g.fn_var_signature(f.return_type, f.params.map(it.typ), 'fn')
|
|
||||||
} else {
|
|
||||||
if node.call_expr.is_method {
|
|
||||||
rec_sym := g.table.sym(g.unwrap_generic(node.call_expr.receiver_type))
|
|
||||||
if f := rec_sym.find_method_with_generic_parent(node.call_expr.name) {
|
|
||||||
mut muttable := unsafe { &ast.Table(g.table) }
|
|
||||||
return_type := muttable.resolve_generic_to_concrete(f.return_type,
|
|
||||||
f.generic_names, node.call_expr.concrete_types) or { f.return_type }
|
|
||||||
mut arg_types := f.params.map(it.typ)
|
|
||||||
arg_types = arg_types.map(muttable.resolve_generic_to_concrete(it,
|
|
||||||
f.generic_names, node.call_expr.concrete_types) or { it })
|
|
||||||
fn_var = g.fn_var_signature(return_type, arg_types, 'fn')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if f := g.table.find_fn(node.call_expr.name) {
|
|
||||||
mut muttable := unsafe { &ast.Table(g.table) }
|
|
||||||
return_type := muttable.resolve_generic_to_concrete(f.return_type,
|
|
||||||
f.generic_names, node.call_expr.concrete_types) or { f.return_type }
|
|
||||||
mut arg_types := f.params.map(it.typ)
|
|
||||||
arg_types = arg_types.map(muttable.resolve_generic_to_concrete(it,
|
|
||||||
f.generic_names, node.call_expr.concrete_types) or { it })
|
|
||||||
fn_var = g.fn_var_signature(return_type, arg_types, 'fn')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.type_definitions.writeln('\t${fn_var};')
|
|
||||||
if expr.is_method {
|
|
||||||
styp := g.typ(expr.receiver_type)
|
|
||||||
g.type_definitions.writeln('\t${styp} arg0;')
|
|
||||||
}
|
|
||||||
need_return_ptr := g.pref.os == .windows && node.call_expr.return_type != ast.void_type
|
|
||||||
for i, arg in expr.args {
|
|
||||||
arg_sym := g.table.sym(arg.typ)
|
|
||||||
if arg_sym.info is ast.FnType {
|
|
||||||
sig := g.fn_var_signature(arg_sym.info.func.return_type, arg_sym.info.func.params.map(it.typ),
|
|
||||||
'arg${i + 1}')
|
|
||||||
g.type_definitions.writeln('\t' + sig + ';')
|
|
||||||
} else {
|
|
||||||
styp := g.typ(arg.typ)
|
|
||||||
g.type_definitions.writeln('\t${styp} arg${i + 1};')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if need_return_ptr {
|
|
||||||
g.type_definitions.writeln('\tvoid* ret_ptr;')
|
|
||||||
}
|
|
||||||
g.type_definitions.writeln('} ${wrapper_struct_name};')
|
|
||||||
thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' }
|
|
||||||
g.type_definitions.writeln('${g.static_modifier} ${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg);')
|
|
||||||
g.gowrappers.writeln('${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg) {')
|
|
||||||
if node.call_expr.return_type != ast.void_type {
|
|
||||||
if g.pref.os == .windows {
|
|
||||||
g.gowrappers.write_string('\t*((${s_ret_typ}*)(arg->ret_ptr)) = ')
|
|
||||||
} else {
|
|
||||||
g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr = malloc(sizeof(${s_ret_typ}));')
|
|
||||||
g.gowrappers.write_string('\t*ret_ptr = ')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
g.gowrappers.write_string('\t')
|
|
||||||
}
|
|
||||||
if expr.is_method {
|
|
||||||
unwrapped_rec_type := g.unwrap_generic(expr.receiver_type)
|
|
||||||
typ_sym := g.table.sym(unwrapped_rec_type)
|
|
||||||
if typ_sym.kind == .interface_
|
|
||||||
&& (typ_sym.info as ast.Interface).defines_method(expr.name) {
|
|
||||||
rec_cc_type := g.cc_type(unwrapped_rec_type, false)
|
|
||||||
receiver_type_name := util.no_dots(rec_cc_type)
|
|
||||||
g.gowrappers.write_string('${c_name(receiver_type_name)}_name_table[')
|
|
||||||
g.gowrappers.write_string('arg->arg0')
|
|
||||||
dot := if expr.left_type.is_ptr() { '->' } else { '.' }
|
|
||||||
mname := c_name(expr.name)
|
|
||||||
g.gowrappers.write_string('${dot}_typ]._method_${mname}(')
|
|
||||||
g.gowrappers.write_string('arg->arg0')
|
|
||||||
g.gowrappers.write_string('${dot}_object')
|
|
||||||
} else {
|
|
||||||
g.gowrappers.write_string('arg->fn(')
|
|
||||||
g.gowrappers.write_string('arg->arg0')
|
|
||||||
}
|
|
||||||
if expr.args.len > 0 {
|
|
||||||
g.gowrappers.write_string(', ')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
g.gowrappers.write_string('arg->fn(')
|
|
||||||
}
|
|
||||||
if expr.args.len > 0 {
|
|
||||||
mut has_cast := false
|
|
||||||
for i in 0 .. expr.args.len {
|
|
||||||
if g.table.sym(expr.expected_arg_types[i]).kind == .interface_
|
|
||||||
&& g.table.sym(expr.args[i].typ).kind != .interface_ {
|
|
||||||
has_cast = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if has_cast {
|
|
||||||
pos := g.out.len
|
|
||||||
g.call_args(expr)
|
|
||||||
mut call_args_str := g.out.after(pos)
|
|
||||||
g.go_back(call_args_str.len)
|
|
||||||
mut rep_group := []string{cap: 2 * expr.args.len}
|
|
||||||
for i in 0 .. expr.args.len {
|
|
||||||
rep_group << g.expr_string(expr.args[i].expr)
|
|
||||||
rep_group << 'arg->arg${i + 1}'
|
|
||||||
}
|
|
||||||
call_args_str = call_args_str.replace_each(rep_group)
|
|
||||||
g.gowrappers.write_string(call_args_str)
|
|
||||||
} else if expr.name in ['print', 'println', 'eprint', 'eprintln', 'panic']
|
|
||||||
&& expr.args[0].typ != ast.string_type {
|
|
||||||
pos := g.out.len
|
|
||||||
g.gen_expr_to_string(expr.args[0].expr, expr.args[0].typ)
|
|
||||||
mut call_args_str := g.out.after(pos)
|
|
||||||
g.out.go_back(call_args_str.len)
|
|
||||||
mut rep_group := []string{cap: 2 * expr.args.len}
|
|
||||||
for i in 0 .. expr.args.len {
|
|
||||||
rep_group << g.expr_string(expr.args[i].expr)
|
|
||||||
rep_group << 'arg->arg${i + 1}'
|
|
||||||
}
|
|
||||||
call_args_str = call_args_str.replace_each(rep_group)
|
|
||||||
g.gowrappers.write_string(call_args_str)
|
|
||||||
} else {
|
|
||||||
for i in 0 .. expr.args.len {
|
|
||||||
expected_nr_muls := expr.expected_arg_types[i].nr_muls()
|
|
||||||
arg_nr_muls := expr.args[i].typ.nr_muls()
|
|
||||||
if arg_nr_muls > expected_nr_muls {
|
|
||||||
g.gowrappers.write_string('*'.repeat(arg_nr_muls - expected_nr_muls))
|
|
||||||
} else if arg_nr_muls < expected_nr_muls {
|
|
||||||
g.gowrappers.write_string('&'.repeat(expected_nr_muls - arg_nr_muls))
|
|
||||||
}
|
|
||||||
g.gowrappers.write_string('arg->arg${i + 1}')
|
|
||||||
if i != expr.args.len - 1 {
|
|
||||||
g.gowrappers.write_string(', ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.gowrappers.writeln(');')
|
|
||||||
g.gowrappers.writeln('\tfree(arg);')
|
|
||||||
if g.pref.os != .windows && node.call_expr.return_type != ast.void_type {
|
|
||||||
g.gowrappers.writeln('\treturn ret_ptr;')
|
|
||||||
} else {
|
|
||||||
g.gowrappers.writeln('\treturn 0;')
|
|
||||||
}
|
|
||||||
g.gowrappers.writeln('}')
|
|
||||||
}
|
|
||||||
if node.is_expr {
|
|
||||||
g.empty_line = false
|
|
||||||
g.write(line)
|
|
||||||
g.write(handle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get current thread size, if fn hasn't defined return default
|
|
||||||
[inline]
|
|
||||||
fn (mut g Gen) get_cur_thread_stack_size(name string) string {
|
|
||||||
ast_fn := g.table.fns[name] or { return '${g.pref.thread_stack_size}' }
|
|
||||||
attrs := ast_fn.attrs
|
|
||||||
if isnil(attrs) {
|
|
||||||
return '${g.pref.thread_stack_size}'
|
|
||||||
}
|
|
||||||
for attr in attrs {
|
|
||||||
if attr.name == 'spawn_stack' {
|
|
||||||
return attr.arg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '${g.pref.thread_stack_size}'
|
|
||||||
}
|
|
||||||
|
|
||||||
// similar to `autofree_call_pregen()` but only to to handle [keep_args_alive] for C functions
|
// similar to `autofree_call_pregen()` but only to to handle [keep_args_alive] for C functions
|
||||||
fn (mut g Gen) keep_alive_call_pregen(node ast.CallExpr) int {
|
fn (mut g Gen) keep_alive_call_pregen(node ast.CallExpr) int {
|
||||||
g.empty_line = true
|
g.empty_line = true
|
||||||
|
375
vlib/v/gen/c/spawn_and_go.v
Normal file
375
vlib/v/gen/c/spawn_and_go.v
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
// Copyright (c) 2019-2023 Alexander Medvednikov. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license
|
||||||
|
// that can be found in the LICENSE file.
|
||||||
|
module c
|
||||||
|
|
||||||
|
import v.ast
|
||||||
|
import v.util
|
||||||
|
|
||||||
|
fn (mut g Gen) spawn_expr(node ast.SpawnExpr) {
|
||||||
|
g.writeln('/*spawn (thread) */')
|
||||||
|
line := g.go_before_stmt(0)
|
||||||
|
mut handle := ''
|
||||||
|
tmp := g.new_tmp_var()
|
||||||
|
mut expr := node.call_expr
|
||||||
|
mut name := expr.name
|
||||||
|
mut use_tmp_fn_var := false
|
||||||
|
tmp_fn := g.new_tmp_var()
|
||||||
|
|
||||||
|
if expr.concrete_types.len > 0 {
|
||||||
|
name = g.generic_fn_name(expr.concrete_types, name)
|
||||||
|
} else if expr.is_fn_var && expr.fn_var_type.has_flag(.generic) {
|
||||||
|
fn_var_type := g.unwrap_generic(expr.fn_var_type)
|
||||||
|
name = g.typ(fn_var_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expr.is_method {
|
||||||
|
receiver_sym := g.table.sym(g.unwrap_generic(expr.receiver_type))
|
||||||
|
name = receiver_sym.cname + '_' + name
|
||||||
|
} else if mut expr.left is ast.AnonFn {
|
||||||
|
if expr.left.inherited_vars.len > 0 {
|
||||||
|
fn_var := g.fn_var_signature(expr.left.decl.return_type, expr.left.decl.params.map(it.typ),
|
||||||
|
tmp_fn)
|
||||||
|
g.write('\t${fn_var} = ')
|
||||||
|
g.gen_anon_fn(mut expr.left)
|
||||||
|
g.writeln(';')
|
||||||
|
use_tmp_fn_var = true
|
||||||
|
} else {
|
||||||
|
g.gen_anon_fn_decl(mut expr.left)
|
||||||
|
name = expr.left.decl.name
|
||||||
|
}
|
||||||
|
} else if expr.left is ast.IndexExpr {
|
||||||
|
if expr.is_fn_var {
|
||||||
|
fn_sym := g.table.sym(expr.fn_var_type)
|
||||||
|
func := (fn_sym.info as ast.FnType).func
|
||||||
|
fn_var := g.fn_var_signature(func.return_type, func.params.map(it.typ), tmp_fn)
|
||||||
|
g.write('\t${fn_var} = ')
|
||||||
|
g.expr(expr.left)
|
||||||
|
g.writeln(';')
|
||||||
|
name = fn_sym.cname
|
||||||
|
use_tmp_fn_var = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name = util.no_dots(name)
|
||||||
|
if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') {
|
||||||
|
mut key := expr.name
|
||||||
|
if expr.is_method {
|
||||||
|
sym := g.table.sym(expr.receiver_type)
|
||||||
|
key = sym.name + '.' + expr.name
|
||||||
|
}
|
||||||
|
g.write('/* obf go: ${key} */')
|
||||||
|
name = g.obf_table[key] or {
|
||||||
|
panic('cgen: obf name "${key}" not found, this should never happen')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.empty_line = true
|
||||||
|
g.writeln('// start go')
|
||||||
|
wrapper_struct_name := 'thread_arg_' + name
|
||||||
|
wrapper_fn_name := name + '_thread_wrapper'
|
||||||
|
arg_tmp_var := 'arg_' + tmp
|
||||||
|
g.writeln('${wrapper_struct_name} *${arg_tmp_var} = malloc(sizeof(thread_arg_${name}));')
|
||||||
|
fn_name := if use_tmp_fn_var {
|
||||||
|
tmp_fn
|
||||||
|
} else if expr.is_fn_var {
|
||||||
|
expr.name
|
||||||
|
} else {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
if !(expr.is_method && g.table.sym(expr.receiver_type).kind == .interface_) {
|
||||||
|
g.writeln('${arg_tmp_var}->fn = ${fn_name};')
|
||||||
|
}
|
||||||
|
if expr.is_method {
|
||||||
|
g.write('${arg_tmp_var}->arg0 = ')
|
||||||
|
g.expr(expr.left)
|
||||||
|
g.writeln(';')
|
||||||
|
}
|
||||||
|
for i, arg in expr.args {
|
||||||
|
g.write('${arg_tmp_var}->arg${i + 1} = ')
|
||||||
|
g.expr(arg.expr)
|
||||||
|
g.writeln(';')
|
||||||
|
}
|
||||||
|
s_ret_typ := g.typ(node.call_expr.return_type)
|
||||||
|
if g.pref.os == .windows && node.call_expr.return_type != ast.void_type {
|
||||||
|
g.writeln('${arg_tmp_var}->ret_ptr = malloc(sizeof(${s_ret_typ}));')
|
||||||
|
}
|
||||||
|
is_opt := node.call_expr.return_type.has_flag(.option)
|
||||||
|
is_res := node.call_expr.return_type.has_flag(.result)
|
||||||
|
mut gohandle_name := ''
|
||||||
|
if node.call_expr.return_type == ast.void_type {
|
||||||
|
if is_opt {
|
||||||
|
gohandle_name = '__v_thread_Option_void'
|
||||||
|
} else if is_res {
|
||||||
|
gohandle_name = '__v_thread_Result_void'
|
||||||
|
} else {
|
||||||
|
gohandle_name = '__v_thread'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opt := if is_opt { '${option_name}_' } else { '' }
|
||||||
|
res := if is_res { '${result_name}_' } else { '' }
|
||||||
|
gohandle_name = '__v_thread_${opt}${res}${g.table.sym(g.unwrap_generic(node.call_expr.return_type)).cname}'
|
||||||
|
}
|
||||||
|
if g.pref.os == .windows {
|
||||||
|
simple_handle := if node.is_expr && node.call_expr.return_type != ast.void_type {
|
||||||
|
'thread_handle_${tmp}'
|
||||||
|
} else {
|
||||||
|
'thread_${tmp}'
|
||||||
|
}
|
||||||
|
stack_size := g.get_cur_thread_stack_size(expr.name)
|
||||||
|
g.writeln('HANDLE ${simple_handle} = CreateThread(0, ${stack_size}, (LPTHREAD_START_ROUTINE)${wrapper_fn_name}, ${arg_tmp_var}, 0, 0); // fn: ${expr.name}')
|
||||||
|
g.writeln('if (!${simple_handle}) panic_lasterr(tos3("`go ${name}()`: "));')
|
||||||
|
if node.is_expr && node.call_expr.return_type != ast.void_type {
|
||||||
|
g.writeln('${gohandle_name} thread_${tmp} = {')
|
||||||
|
g.writeln('\t.ret_ptr = ${arg_tmp_var}->ret_ptr,')
|
||||||
|
g.writeln('\t.handle = thread_handle_${tmp}')
|
||||||
|
g.writeln('};')
|
||||||
|
}
|
||||||
|
if !node.is_expr {
|
||||||
|
g.writeln('CloseHandle(thread_${tmp});')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g.writeln('pthread_t thread_${tmp};')
|
||||||
|
mut sthread_attributes := 'NULL'
|
||||||
|
if g.pref.os != .vinix {
|
||||||
|
g.writeln('pthread_attr_t thread_${tmp}_attributes;')
|
||||||
|
g.writeln('pthread_attr_init(&thread_${tmp}_attributes);')
|
||||||
|
size := g.get_cur_thread_stack_size(expr.name)
|
||||||
|
g.writeln('pthread_attr_setstacksize(&thread_${tmp}_attributes, ${size}); // fn: ${expr.name}')
|
||||||
|
sthread_attributes = '&thread_${tmp}_attributes'
|
||||||
|
}
|
||||||
|
g.writeln('int ${tmp}_thr_res = pthread_create(&thread_${tmp}, ${sthread_attributes}, (void*)${wrapper_fn_name}, ${arg_tmp_var});')
|
||||||
|
g.writeln('if (${tmp}_thr_res) panic_error_number(tos3("`go ${name}()`: "), ${tmp}_thr_res);')
|
||||||
|
if !node.is_expr {
|
||||||
|
g.writeln('pthread_detach(thread_${tmp});')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.writeln('// end go')
|
||||||
|
if node.is_expr {
|
||||||
|
handle = 'thread_${tmp}'
|
||||||
|
// create wait handler for this return type if none exists
|
||||||
|
waiter_fn_name := gohandle_name + '_wait'
|
||||||
|
mut should_register := false
|
||||||
|
lock g.waiter_fns {
|
||||||
|
if waiter_fn_name !in g.waiter_fns {
|
||||||
|
g.waiter_fns << waiter_fn_name
|
||||||
|
should_register = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if should_register {
|
||||||
|
g.gowrappers.writeln('\n${s_ret_typ} ${waiter_fn_name}(${gohandle_name} thread) {')
|
||||||
|
mut c_ret_ptr_ptr := 'NULL'
|
||||||
|
if node.call_expr.return_type != ast.void_type {
|
||||||
|
g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr;')
|
||||||
|
c_ret_ptr_ptr = '&ret_ptr'
|
||||||
|
}
|
||||||
|
if g.pref.os == .windows {
|
||||||
|
if node.call_expr.return_type == ast.void_type {
|
||||||
|
g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);')
|
||||||
|
} else {
|
||||||
|
g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);')
|
||||||
|
g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)${c_ret_ptr_ptr});')
|
||||||
|
}
|
||||||
|
g.gowrappers.writeln('\tif (stat != 0) { _v_panic(_SLIT("unable to join thread")); }')
|
||||||
|
if g.pref.os == .windows {
|
||||||
|
if node.call_expr.return_type == ast.void_type {
|
||||||
|
g.gowrappers.writeln('\tCloseHandle(thread);')
|
||||||
|
} else {
|
||||||
|
g.gowrappers.writeln('\tCloseHandle(thread.handle);')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.call_expr.return_type != ast.void_type {
|
||||||
|
g.gowrappers.writeln('\t${s_ret_typ} ret = *ret_ptr;')
|
||||||
|
g.gowrappers.writeln('\tfree(ret_ptr);')
|
||||||
|
g.gowrappers.writeln('\treturn ret;')
|
||||||
|
}
|
||||||
|
g.gowrappers.writeln('}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Register the wrapper type and function
|
||||||
|
mut should_register := false
|
||||||
|
lock g.threaded_fns {
|
||||||
|
if name !in g.threaded_fns {
|
||||||
|
g.threaded_fns << name
|
||||||
|
should_register = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if should_register {
|
||||||
|
g.type_definitions.writeln('\ntypedef struct ${wrapper_struct_name} {')
|
||||||
|
mut fn_var := ''
|
||||||
|
if node.call_expr.is_fn_var {
|
||||||
|
fn_sym := g.table.sym(node.call_expr.fn_var_type)
|
||||||
|
info := fn_sym.info as ast.FnType
|
||||||
|
fn_var = g.fn_var_signature(info.func.return_type, info.func.params.map(it.typ),
|
||||||
|
'fn')
|
||||||
|
} else if node.call_expr.left is ast.AnonFn {
|
||||||
|
f := node.call_expr.left.decl
|
||||||
|
fn_var = g.fn_var_signature(f.return_type, f.params.map(it.typ), 'fn')
|
||||||
|
} else {
|
||||||
|
if node.call_expr.is_method {
|
||||||
|
rec_sym := g.table.sym(g.unwrap_generic(node.call_expr.receiver_type))
|
||||||
|
if f := rec_sym.find_method_with_generic_parent(node.call_expr.name) {
|
||||||
|
mut muttable := unsafe { &ast.Table(g.table) }
|
||||||
|
return_type := muttable.resolve_generic_to_concrete(f.return_type,
|
||||||
|
f.generic_names, node.call_expr.concrete_types) or { f.return_type }
|
||||||
|
mut arg_types := f.params.map(it.typ)
|
||||||
|
arg_types = arg_types.map(muttable.resolve_generic_to_concrete(it,
|
||||||
|
f.generic_names, node.call_expr.concrete_types) or { it })
|
||||||
|
fn_var = g.fn_var_signature(return_type, arg_types, 'fn')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if f := g.table.find_fn(node.call_expr.name) {
|
||||||
|
mut muttable := unsafe { &ast.Table(g.table) }
|
||||||
|
return_type := muttable.resolve_generic_to_concrete(f.return_type,
|
||||||
|
f.generic_names, node.call_expr.concrete_types) or { f.return_type }
|
||||||
|
mut arg_types := f.params.map(it.typ)
|
||||||
|
arg_types = arg_types.map(muttable.resolve_generic_to_concrete(it,
|
||||||
|
f.generic_names, node.call_expr.concrete_types) or { it })
|
||||||
|
fn_var = g.fn_var_signature(return_type, arg_types, 'fn')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.type_definitions.writeln('\t${fn_var};')
|
||||||
|
if expr.is_method {
|
||||||
|
styp := g.typ(expr.receiver_type)
|
||||||
|
g.type_definitions.writeln('\t${styp} arg0;')
|
||||||
|
}
|
||||||
|
need_return_ptr := g.pref.os == .windows && node.call_expr.return_type != ast.void_type
|
||||||
|
for i, arg in expr.args {
|
||||||
|
arg_sym := g.table.sym(arg.typ)
|
||||||
|
if arg_sym.info is ast.FnType {
|
||||||
|
sig := g.fn_var_signature(arg_sym.info.func.return_type, arg_sym.info.func.params.map(it.typ),
|
||||||
|
'arg${i + 1}')
|
||||||
|
g.type_definitions.writeln('\t' + sig + ';')
|
||||||
|
} else {
|
||||||
|
styp := g.typ(arg.typ)
|
||||||
|
g.type_definitions.writeln('\t${styp} arg${i + 1};')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if need_return_ptr {
|
||||||
|
g.type_definitions.writeln('\tvoid* ret_ptr;')
|
||||||
|
}
|
||||||
|
g.type_definitions.writeln('} ${wrapper_struct_name};')
|
||||||
|
thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' }
|
||||||
|
g.type_definitions.writeln('${g.static_modifier} ${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg);')
|
||||||
|
g.gowrappers.writeln('${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg) {')
|
||||||
|
if node.call_expr.return_type != ast.void_type {
|
||||||
|
if g.pref.os == .windows {
|
||||||
|
g.gowrappers.write_string('\t*((${s_ret_typ}*)(arg->ret_ptr)) = ')
|
||||||
|
} else {
|
||||||
|
g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr = malloc(sizeof(${s_ret_typ}));')
|
||||||
|
g.gowrappers.write_string('\t*ret_ptr = ')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g.gowrappers.write_string('\t')
|
||||||
|
}
|
||||||
|
if expr.is_method {
|
||||||
|
unwrapped_rec_type := g.unwrap_generic(expr.receiver_type)
|
||||||
|
typ_sym := g.table.sym(unwrapped_rec_type)
|
||||||
|
if typ_sym.kind == .interface_
|
||||||
|
&& (typ_sym.info as ast.Interface).defines_method(expr.name) {
|
||||||
|
rec_cc_type := g.cc_type(unwrapped_rec_type, false)
|
||||||
|
receiver_type_name := util.no_dots(rec_cc_type)
|
||||||
|
g.gowrappers.write_string('${c_name(receiver_type_name)}_name_table[')
|
||||||
|
g.gowrappers.write_string('arg->arg0')
|
||||||
|
dot := if expr.left_type.is_ptr() { '->' } else { '.' }
|
||||||
|
mname := c_name(expr.name)
|
||||||
|
g.gowrappers.write_string('${dot}_typ]._method_${mname}(')
|
||||||
|
g.gowrappers.write_string('arg->arg0')
|
||||||
|
g.gowrappers.write_string('${dot}_object')
|
||||||
|
} else {
|
||||||
|
g.gowrappers.write_string('arg->fn(')
|
||||||
|
g.gowrappers.write_string('arg->arg0')
|
||||||
|
}
|
||||||
|
if expr.args.len > 0 {
|
||||||
|
g.gowrappers.write_string(', ')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g.gowrappers.write_string('arg->fn(')
|
||||||
|
}
|
||||||
|
if expr.args.len > 0 {
|
||||||
|
mut has_cast := false
|
||||||
|
for i in 0 .. expr.args.len {
|
||||||
|
if g.table.sym(expr.expected_arg_types[i]).kind == .interface_
|
||||||
|
&& g.table.sym(expr.args[i].typ).kind != .interface_ {
|
||||||
|
has_cast = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if has_cast {
|
||||||
|
pos := g.out.len
|
||||||
|
g.call_args(expr)
|
||||||
|
mut call_args_str := g.out.after(pos)
|
||||||
|
g.go_back(call_args_str.len)
|
||||||
|
mut rep_group := []string{cap: 2 * expr.args.len}
|
||||||
|
for i in 0 .. expr.args.len {
|
||||||
|
rep_group << g.expr_string(expr.args[i].expr)
|
||||||
|
rep_group << 'arg->arg${i + 1}'
|
||||||
|
}
|
||||||
|
call_args_str = call_args_str.replace_each(rep_group)
|
||||||
|
g.gowrappers.write_string(call_args_str)
|
||||||
|
} else if expr.name in ['print', 'println', 'eprint', 'eprintln', 'panic']
|
||||||
|
&& expr.args[0].typ != ast.string_type {
|
||||||
|
pos := g.out.len
|
||||||
|
g.gen_expr_to_string(expr.args[0].expr, expr.args[0].typ)
|
||||||
|
mut call_args_str := g.out.after(pos)
|
||||||
|
g.out.go_back(call_args_str.len)
|
||||||
|
mut rep_group := []string{cap: 2 * expr.args.len}
|
||||||
|
for i in 0 .. expr.args.len {
|
||||||
|
rep_group << g.expr_string(expr.args[i].expr)
|
||||||
|
rep_group << 'arg->arg${i + 1}'
|
||||||
|
}
|
||||||
|
call_args_str = call_args_str.replace_each(rep_group)
|
||||||
|
g.gowrappers.write_string(call_args_str)
|
||||||
|
} else {
|
||||||
|
for i in 0 .. expr.args.len {
|
||||||
|
expected_nr_muls := expr.expected_arg_types[i].nr_muls()
|
||||||
|
arg_nr_muls := expr.args[i].typ.nr_muls()
|
||||||
|
if arg_nr_muls > expected_nr_muls {
|
||||||
|
g.gowrappers.write_string('*'.repeat(arg_nr_muls - expected_nr_muls))
|
||||||
|
} else if arg_nr_muls < expected_nr_muls {
|
||||||
|
g.gowrappers.write_string('&'.repeat(expected_nr_muls - arg_nr_muls))
|
||||||
|
}
|
||||||
|
g.gowrappers.write_string('arg->arg${i + 1}')
|
||||||
|
if i != expr.args.len - 1 {
|
||||||
|
g.gowrappers.write_string(', ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.gowrappers.writeln(');')
|
||||||
|
g.gowrappers.writeln('\tfree(arg);')
|
||||||
|
if g.pref.os != .windows && node.call_expr.return_type != ast.void_type {
|
||||||
|
g.gowrappers.writeln('\treturn ret_ptr;')
|
||||||
|
} else {
|
||||||
|
g.gowrappers.writeln('\treturn 0;')
|
||||||
|
}
|
||||||
|
g.gowrappers.writeln('}')
|
||||||
|
}
|
||||||
|
if node.is_expr {
|
||||||
|
g.empty_line = false
|
||||||
|
g.write(line)
|
||||||
|
g.write(handle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) go_expr(node ast.GoExpr) {
|
||||||
|
g.writeln('/*go (coroutine) */')
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current thread size, if fn hasn't defined return default
|
||||||
|
[inline]
|
||||||
|
fn (mut g Gen) get_cur_thread_stack_size(name string) string {
|
||||||
|
ast_fn := g.table.fns[name] or { return '${g.pref.thread_stack_size}' }
|
||||||
|
attrs := ast_fn.attrs
|
||||||
|
if isnil(attrs) {
|
||||||
|
return '${g.pref.thread_stack_size}'
|
||||||
|
}
|
||||||
|
for attr in attrs {
|
||||||
|
if attr.name == 'spawn_stack' {
|
||||||
|
return attr.arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '${g.pref.thread_stack_size}'
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user