parser, checker, cgen: minor cleanup of go_expr() (#13875)
parent
db3bbb58cf
commit
bc98c11d9d
|
@ -2111,32 +2111,6 @@ fn (mut c Checker) global_decl(mut node ast.GlobalDecl) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut c Checker) go_expr(mut node ast.GoExpr) ast.Type {
|
|
||||||
ret_type := c.call_expr(mut node.call_expr)
|
|
||||||
if node.call_expr.or_block.kind != .absent {
|
|
||||||
c.error('optional handling cannot be done in `go` call. Do it when calling `.wait()`',
|
|
||||||
node.call_expr.or_block.pos)
|
|
||||||
}
|
|
||||||
// Make sure there are no mutable arguments
|
|
||||||
for arg in node.call_expr.args {
|
|
||||||
if arg.is_mut && !arg.typ.is_ptr() {
|
|
||||||
c.error('function in `go` statement cannot contain mutable non-reference arguments',
|
|
||||||
arg.expr.pos())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr()
|
|
||||||
&& !node.call_expr.left_type.is_ptr() {
|
|
||||||
c.error('method in `go` statement cannot have non-reference mutable receiver',
|
|
||||||
node.call_expr.left.pos())
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.pref.backend.is_js() {
|
|
||||||
return c.table.find_or_register_promise(ret_type)
|
|
||||||
} else {
|
|
||||||
return c.table.find_or_register_thread(ret_type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) {
|
fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) {
|
||||||
if stmt.is_goto {
|
if stmt.is_goto {
|
||||||
c.warn('inline assembly goto is not supported, it will most likely not work',
|
c.warn('inline assembly goto is not supported, it will most likely not work',
|
||||||
|
|
|
@ -1484,6 +1484,32 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
return ast.void_type
|
return ast.void_type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) go_expr(mut node ast.GoExpr) ast.Type {
|
||||||
|
ret_type := c.call_expr(mut node.call_expr)
|
||||||
|
if node.call_expr.or_block.kind != .absent {
|
||||||
|
c.error('optional handling cannot be done in `go` call. Do it when calling `.wait()`',
|
||||||
|
node.call_expr.or_block.pos)
|
||||||
|
}
|
||||||
|
// Make sure there are no mutable arguments
|
||||||
|
for arg in node.call_expr.args {
|
||||||
|
if arg.is_mut && !arg.typ.is_ptr() {
|
||||||
|
c.error('function in `go` statement cannot contain mutable non-reference arguments',
|
||||||
|
arg.expr.pos())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr()
|
||||||
|
&& !node.call_expr.left_type.is_ptr() {
|
||||||
|
c.error('method in `go` statement cannot have non-reference mutable receiver',
|
||||||
|
node.call_expr.left.pos())
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.pref.backend.is_js() {
|
||||||
|
return c.table.find_or_register_promise(ret_type)
|
||||||
|
} else {
|
||||||
|
return c.table.find_or_register_thread(ret_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut c Checker) deprecate_fnmethod(kind string, name string, the_fn ast.Fn, node ast.CallExpr) {
|
fn (mut c Checker) deprecate_fnmethod(kind string, name string, the_fn ast.Fn, node ast.CallExpr) {
|
||||||
mut deprecation_message := ''
|
mut deprecation_message := ''
|
||||||
now := time.now()
|
now := time.now()
|
||||||
|
|
|
@ -5146,258 +5146,6 @@ fn (mut g Gen) enum_val(node ast.EnumVal) {
|
||||||
g.write('${styp}__$node.val')
|
g.write('${styp}__$node.val')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) go_expr(node ast.GoExpr) {
|
|
||||||
line := g.go_before_stmt(0)
|
|
||||||
mut handle := ''
|
|
||||||
tmp := g.new_tmp_var()
|
|
||||||
mut expr := node.call_expr
|
|
||||||
mut name := expr.name // util.no_dots(expr.name)
|
|
||||||
// TODO: fn call is duplicated. merge with fn_call().
|
|
||||||
for i, concrete_type in expr.concrete_types {
|
|
||||||
if concrete_type != ast.void_type && concrete_type != 0 {
|
|
||||||
// Using _T_ to differentiate between get<string> and get_string
|
|
||||||
// `foo<int>()` => `foo_T_int()`
|
|
||||||
if i == 0 {
|
|
||||||
name += '_T'
|
|
||||||
}
|
|
||||||
name += '_' + g.typ(concrete_type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if expr.is_method {
|
|
||||||
receiver_sym := g.table.sym(expr.receiver_type)
|
|
||||||
name = receiver_sym.name + '_' + name
|
|
||||||
} else if mut expr.left is ast.AnonFn {
|
|
||||||
g.gen_anon_fn_decl(mut expr.left)
|
|
||||||
name = expr.left.decl.name
|
|
||||||
} else if expr.is_fn_var {
|
|
||||||
name = g.table.sym(expr.fn_var_type).name
|
|
||||||
}
|
|
||||||
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));')
|
|
||||||
if expr.is_method {
|
|
||||||
g.write('$arg_tmp_var->arg0 = ')
|
|
||||||
// TODO is this needed?
|
|
||||||
/*
|
|
||||||
if false && !expr.return_type.is_ptr() {
|
|
||||||
g.write('&')
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
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(.optional)
|
|
||||||
mut gohandle_name := ''
|
|
||||||
if node.call_expr.return_type == ast.void_type {
|
|
||||||
gohandle_name = if is_opt { '__v_thread_Option_void' } else { '__v_thread' }
|
|
||||||
} else {
|
|
||||||
opt := if is_opt { 'Option_' } else { '' }
|
|
||||||
gohandle_name = '__v_thread_$opt${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'
|
|
||||||
}
|
|
||||||
g.writeln('HANDLE $simple_handle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0, 0);')
|
|
||||||
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;')
|
|
||||||
g.writeln('int ${tmp}_thr_res = pthread_create(&thread_$tmp, NULL, (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 {')
|
|
||||||
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
|
|
||||||
if expr.args.len == 0 && !need_return_ptr {
|
|
||||||
g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;')
|
|
||||||
} else {
|
|
||||||
for i, arg in expr.args {
|
|
||||||
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('$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('${name}(')
|
|
||||||
g.gowrappers.write_string('arg->arg0')
|
|
||||||
}
|
|
||||||
if expr.args.len > 0 {
|
|
||||||
g.gowrappers.write_string(', ')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
g.gowrappers.write_string('${name}(')
|
|
||||||
}
|
|
||||||
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.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 {
|
|
||||||
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) as_cast(node ast.AsCast) {
|
fn (mut g Gen) as_cast(node ast.AsCast) {
|
||||||
// Make sure the sum type can be cast to this type (the types
|
// Make sure the sum type can be cast to this type (the types
|
||||||
// are the same), otherwise panic.
|
// are the same), otherwise panic.
|
||||||
|
|
|
@ -1667,6 +1667,258 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) go_expr(node ast.GoExpr) {
|
||||||
|
line := g.go_before_stmt(0)
|
||||||
|
mut handle := ''
|
||||||
|
tmp := g.new_tmp_var()
|
||||||
|
mut expr := node.call_expr
|
||||||
|
mut name := expr.name // util.no_dots(expr.name)
|
||||||
|
// TODO: fn call is duplicated. merge with fn_call().
|
||||||
|
for i, concrete_type in expr.concrete_types {
|
||||||
|
if concrete_type != ast.void_type && concrete_type != 0 {
|
||||||
|
// Using _T_ to differentiate between get<string> and get_string
|
||||||
|
// `foo<int>()` => `foo_T_int()`
|
||||||
|
if i == 0 {
|
||||||
|
name += '_T'
|
||||||
|
}
|
||||||
|
name += '_' + g.typ(concrete_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if expr.is_method {
|
||||||
|
receiver_sym := g.table.sym(expr.receiver_type)
|
||||||
|
name = receiver_sym.name + '_' + name
|
||||||
|
} else if mut expr.left is ast.AnonFn {
|
||||||
|
g.gen_anon_fn_decl(mut expr.left)
|
||||||
|
name = expr.left.decl.name
|
||||||
|
} else if expr.is_fn_var {
|
||||||
|
name = g.table.sym(expr.fn_var_type).name
|
||||||
|
}
|
||||||
|
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));')
|
||||||
|
if expr.is_method {
|
||||||
|
g.write('$arg_tmp_var->arg0 = ')
|
||||||
|
// TODO is this needed?
|
||||||
|
/*
|
||||||
|
if false && !expr.return_type.is_ptr() {
|
||||||
|
g.write('&')
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
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(.optional)
|
||||||
|
mut gohandle_name := ''
|
||||||
|
if node.call_expr.return_type == ast.void_type {
|
||||||
|
gohandle_name = if is_opt { '__v_thread_Option_void' } else { '__v_thread' }
|
||||||
|
} else {
|
||||||
|
opt := if is_opt { 'Option_' } else { '' }
|
||||||
|
gohandle_name = '__v_thread_$opt${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'
|
||||||
|
}
|
||||||
|
g.writeln('HANDLE $simple_handle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0, 0);')
|
||||||
|
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;')
|
||||||
|
g.writeln('int ${tmp}_thr_res = pthread_create(&thread_$tmp, NULL, (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 {')
|
||||||
|
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
|
||||||
|
if expr.args.len == 0 && !need_return_ptr {
|
||||||
|
g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;')
|
||||||
|
} else {
|
||||||
|
for i, arg in expr.args {
|
||||||
|
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('$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('${name}(')
|
||||||
|
g.gowrappers.write_string('arg->arg0')
|
||||||
|
}
|
||||||
|
if expr.args.len > 0 {
|
||||||
|
g.gowrappers.write_string(', ')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g.gowrappers.write_string('${name}(')
|
||||||
|
}
|
||||||
|
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.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 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
@ -545,27 +545,6 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut p Parser) go_expr() ast.GoExpr {
|
|
||||||
p.next()
|
|
||||||
spos := p.tok.pos()
|
|
||||||
expr := p.expr(0)
|
|
||||||
call_expr := if expr is ast.CallExpr {
|
|
||||||
expr
|
|
||||||
} else {
|
|
||||||
p.error_with_pos('expression in `go` must be a function call', expr.pos())
|
|
||||||
ast.CallExpr{
|
|
||||||
scope: p.scope
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos := spos.extend(p.prev_tok.pos())
|
|
||||||
p.register_auto_import('sync.threads')
|
|
||||||
p.table.gostmts++
|
|
||||||
return ast.GoExpr{
|
|
||||||
call_expr: call_expr
|
|
||||||
pos: pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (p &Parser) fileis(s string) bool {
|
fn (p &Parser) fileis(s string) bool {
|
||||||
return p.file_name.contains(s)
|
return p.file_name.contains(s)
|
||||||
}
|
}
|
||||||
|
|
|
@ -928,6 +928,27 @@ fn (mut p Parser) fn_args() ([]ast.Param, bool, bool) {
|
||||||
return args, types_only, is_variadic
|
return args, types_only, is_variadic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut p Parser) go_expr() ast.GoExpr {
|
||||||
|
p.next()
|
||||||
|
spos := p.tok.pos()
|
||||||
|
expr := p.expr(0)
|
||||||
|
call_expr := if expr is ast.CallExpr {
|
||||||
|
expr
|
||||||
|
} else {
|
||||||
|
p.error_with_pos('expression in `go` must be a function call', expr.pos())
|
||||||
|
ast.CallExpr{
|
||||||
|
scope: p.scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos := spos.extend(p.prev_tok.pos())
|
||||||
|
p.register_auto_import('sync.threads')
|
||||||
|
p.table.gostmts++
|
||||||
|
return ast.GoExpr{
|
||||||
|
call_expr: call_expr
|
||||||
|
pos: pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut p Parser) closure_vars() []ast.Param {
|
fn (mut p Parser) closure_vars() []ast.Param {
|
||||||
p.check(.lsbr)
|
p.check(.lsbr)
|
||||||
mut vars := []ast.Param{cap: 5}
|
mut vars := []ast.Param{cap: 5}
|
||||||
|
|
Loading…
Reference in New Issue