| 1 | // Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | module c |
| 5 | |
| 6 | import v.ast |
| 7 | import v.util |
| 8 | |
| 9 | enum SpawnGoMode { |
| 10 | spawn_ |
| 11 | go_ |
| 12 | } |
| 13 | |
| 14 | fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { |
| 15 | if node.call_expr.should_be_skipped { |
| 16 | return |
| 17 | } |
| 18 | is_spawn := mode == .spawn_ |
| 19 | is_go := mode == .go_ |
| 20 | if is_spawn { |
| 21 | g.writeln('/*spawn (thread) */') |
| 22 | } else { |
| 23 | g.writeln('/*go (coroutine) */') |
| 24 | } |
| 25 | line := g.go_before_last_stmt() |
| 26 | mut handle := '' |
| 27 | tmp := g.new_tmp_var() |
| 28 | mut expr := node.call_expr |
| 29 | mut name := expr.name |
| 30 | mut use_tmp_fn_var := false |
| 31 | tmp_fn := g.new_tmp_var() |
| 32 | |
| 33 | if expr.is_fn_var { |
| 34 | // generate a name different for same var fn name declared in another scope |
| 35 | name = '${name}_${node.pos.pos}' |
| 36 | } |
| 37 | |
| 38 | if expr.concrete_types.len > 0 { |
| 39 | name = g.generic_fn_name(expr.concrete_types, name) |
| 40 | } else if expr.is_fn_var && expr.fn_var_type.has_flag(.generic) { |
| 41 | fn_var_type := g.unwrap_generic(expr.fn_var_type) |
| 42 | name = g.styp(fn_var_type) |
| 43 | } |
| 44 | |
| 45 | if expr.is_method { |
| 46 | receiver_sym := g.table.sym(g.unwrap_generic(expr.receiver_type)) |
| 47 | name = receiver_sym.cname + '_' + name |
| 48 | if receiver_sym.is_builtin() && !name.starts_with('_') { |
| 49 | name = 'builtin__${name}' |
| 50 | } |
| 51 | } else if mut expr.left is ast.AnonFn { |
| 52 | if expr.left.inherited_vars.len > 0 { |
| 53 | fn_var := g.fn_var_signature(ast.void_type, expr.left.decl.return_type, |
| 54 | expr.left.decl.params.map(it.typ), tmp_fn) |
| 55 | g.write('\t${fn_var} = ') |
| 56 | g.gen_anon_fn(mut expr.left) |
| 57 | g.writeln(';') |
| 58 | use_tmp_fn_var = true |
| 59 | name = g.anon_fn_cname(expr.left.decl.return_type, expr.left.decl.params.map(it.typ)) |
| 60 | } else { |
| 61 | g.gen_anon_fn_decl(mut expr.left) |
| 62 | name = expr.left.decl.name |
| 63 | } |
| 64 | } else if expr.left is ast.IndexExpr { |
| 65 | if expr.is_fn_var { |
| 66 | fn_sym := g.table.sym(expr.fn_var_type) |
| 67 | func := (fn_sym.info as ast.FnType).func |
| 68 | fn_var := g.fn_var_signature(ast.void_type, func.return_type, func.params.map(it.typ), |
| 69 | tmp_fn) |
| 70 | g.write('\t${fn_var} = ') |
| 71 | g.expr(expr.left) |
| 72 | g.writeln(';') |
| 73 | name = fn_sym.cname |
| 74 | use_tmp_fn_var = true |
| 75 | } |
| 76 | } |
| 77 | // When inside a generic function, differentiate wrapper struct names |
| 78 | // per concrete type instantiation so each gets its own typed struct/wrapper. |
| 79 | if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 { |
| 80 | generic_suffix := g.generic_fn_name(g.cur_concrete_types, '') |
| 81 | if generic_suffix != '' && !name.ends_with(generic_suffix) { |
| 82 | name = g.generic_fn_name(g.cur_concrete_types, name) |
| 83 | } |
| 84 | } |
| 85 | name = util.no_dots(name) |
| 86 | g.empty_line = true |
| 87 | g.writeln('// start go') |
| 88 | wrapper_struct_name := 'thread_arg_' + name |
| 89 | wrapper_fn_name := name + '_thread_wrapper' |
| 90 | arg_tmp_var := 'arg_' + tmp |
| 91 | if is_spawn { |
| 92 | if g.pref.prealloc { |
| 93 | g.writeln('${wrapper_struct_name} *${arg_tmp_var} = (${wrapper_struct_name} *) malloc(sizeof(thread_arg_${name}));') |
| 94 | g.writeln('if (${arg_tmp_var} == NULL) builtin___v_panic(_S("thread argument allocation failed"));') |
| 95 | } else { |
| 96 | g.writeln('${wrapper_struct_name} *${arg_tmp_var} = (${wrapper_struct_name} *) builtin___v_malloc(sizeof(thread_arg_${name}));') |
| 97 | } |
| 98 | } else if is_go { |
| 99 | g.writeln('${wrapper_struct_name} ${arg_tmp_var};') |
| 100 | } |
| 101 | dot := if is_spawn { '->' } else { '.' } |
| 102 | fn_name := if use_tmp_fn_var { |
| 103 | tmp_fn |
| 104 | } else if expr.is_fn_var { |
| 105 | expr.name |
| 106 | } else { |
| 107 | if func := g.table.find_fn(expr.name) { |
| 108 | if func.mod == 'builtin' && !name.starts_with('builtin__') && func.language != .c { |
| 109 | 'builtin__${name}' |
| 110 | } else { |
| 111 | name |
| 112 | } |
| 113 | } else { |
| 114 | name |
| 115 | } |
| 116 | } |
| 117 | if !(expr.is_method && (g.table.sym(expr.receiver_type).kind == .interface |
| 118 | || (g.table.sym(expr.receiver_type).kind == .struct && expr.is_field))) { |
| 119 | g.writeln('${arg_tmp_var}${dot}fn = ${fn_name};') |
| 120 | } |
| 121 | if expr.is_method { |
| 122 | g.write('${arg_tmp_var}${dot}arg0 = ') |
| 123 | if mut expr.left is ast.Ident && expr.left.obj.typ.is_ptr() |
| 124 | && !node.call_expr.receiver_type.is_ptr() { |
| 125 | g.write('*'.repeat(expr.left.obj.typ.nr_muls())) |
| 126 | } |
| 127 | g.expr(expr.left) |
| 128 | g.writeln(';') |
| 129 | } |
| 130 | for i, arg in expr.args { |
| 131 | g.write('${arg_tmp_var}${dot}arg${i + 1} = ') |
| 132 | g.expr(arg.expr) |
| 133 | g.writeln(';') |
| 134 | } |
| 135 | if is_spawn && g.pref.prealloc { |
| 136 | g.writeln('${arg_tmp_var}->prealloc_scope = builtin__prealloc_scope_retain_current();') |
| 137 | } |
| 138 | call_ret_type := if expr.is_fn_var && g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 |
| 139 | && g.cur_fn.generic_names.len > 0 { |
| 140 | // In generic contexts, node.call_expr.return_type may be stale from |
| 141 | // a previous instantiation. Look up the original fn param from the table |
| 142 | // and resolve through convert_generic_type. |
| 143 | mut resolved_ret := g.unwrap_generic(node.call_expr.return_type) |
| 144 | cur_fn_name := g.cur_fn.fkey() |
| 145 | orig_fn := g.table.find_fn(cur_fn_name) or { ast.Fn{} } |
| 146 | for param in orig_fn.params { |
| 147 | if param.name == expr.name { |
| 148 | if param.typ.has_flag(.generic) || g.type_has_unresolved_generic_parts(param.typ) { |
| 149 | mut muttable := unsafe { &ast.Table(g.table) } |
| 150 | if resolved_type := muttable.convert_generic_type(param.typ, |
| 151 | orig_fn.generic_names, g.cur_concrete_types) |
| 152 | { |
| 153 | fn_sym := g.table.sym(resolved_type) |
| 154 | if fn_sym.info is ast.FnType { |
| 155 | resolved_ret = fn_sym.info.func.return_type |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | break |
| 160 | } |
| 161 | } |
| 162 | resolved_ret |
| 163 | } else { |
| 164 | g.unwrap_generic(node.call_expr.return_type) |
| 165 | } |
| 166 | s_ret_typ := g.styp(g.unwrap_generic(call_ret_type)) |
| 167 | if g.pref.os == .windows && call_ret_type != ast.void_type { |
| 168 | if g.pref.prealloc { |
| 169 | g.writeln('${arg_tmp_var}->ret_ptr = (void *) malloc(sizeof(${s_ret_typ}));') |
| 170 | g.writeln('if (${arg_tmp_var}->ret_ptr == NULL) builtin___v_panic(_S("thread return allocation failed"));') |
| 171 | } else { |
| 172 | g.writeln('${arg_tmp_var}->ret_ptr = (void *) builtin___v_malloc(sizeof(${s_ret_typ}));') |
| 173 | } |
| 174 | } |
| 175 | gohandle_name := g.gen_gohandle_name(call_ret_type) |
| 176 | if is_spawn { |
| 177 | if g.pref.os == .windows { |
| 178 | simple_handle := if node.is_expr && call_ret_type != ast.void_type { |
| 179 | 'thread_handle_${tmp}' |
| 180 | } else { |
| 181 | 'thread_${tmp}' |
| 182 | } |
| 183 | stack_size := g.get_cur_thread_stack_size(expr.name) |
| 184 | g.writeln('HANDLE ${simple_handle} = CreateThread(0, ${stack_size}, (LPTHREAD_START_ROUTINE)${wrapper_fn_name}, ${arg_tmp_var}, 0, 0); // fn: ${expr.name}') |
| 185 | g.writeln('if (!${simple_handle}) builtin__panic_lasterr(builtin__tos3("`go ${name}()`: "));') |
| 186 | if node.is_expr && call_ret_type != ast.void_type { |
| 187 | g.writeln('${gohandle_name} thread_${tmp} = {') |
| 188 | g.writeln('\t.ret_ptr = ${arg_tmp_var}->ret_ptr,') |
| 189 | g.writeln2('\t.handle = thread_handle_${tmp}', '};') |
| 190 | } |
| 191 | if !node.is_expr { |
| 192 | g.writeln('CloseHandle(thread_${tmp});') |
| 193 | } |
| 194 | } else { |
| 195 | g.writeln('pthread_t thread_${tmp};') |
| 196 | mut sthread_attributes := 'NULL' |
| 197 | if g.pref.os != .vinix { |
| 198 | g.writeln('pthread_attr_t thread_${tmp}_attributes;') |
| 199 | g.writeln('pthread_attr_init(&thread_${tmp}_attributes);') |
| 200 | size := g.get_cur_thread_stack_size(expr.name) |
| 201 | g.writeln('pthread_attr_setstacksize(&thread_${tmp}_attributes, ${size}); // fn: ${expr.name}') |
| 202 | sthread_attributes = '&thread_${tmp}_attributes' |
| 203 | } |
| 204 | g.writeln('int ${tmp}_thr_res = pthread_create(&thread_${tmp}, ${sthread_attributes}, (void*)${wrapper_fn_name}, ${arg_tmp_var});') |
| 205 | g.writeln('if (${tmp}_thr_res) builtin__panic_error_number(builtin__tos3("`go ${name}()`: "), ${tmp}_thr_res);') |
| 206 | if !node.is_expr { |
| 207 | g.writeln('pthread_detach(thread_${tmp});') |
| 208 | } |
| 209 | } |
| 210 | } else if is_go { |
| 211 | if util.nr_jobs > 1 { |
| 212 | g.writeln('photon_thread_create_and_migrate_to_work_pool((void*)${wrapper_fn_name}, &${arg_tmp_var});') |
| 213 | } else { |
| 214 | g.writeln('photon_thread_create((void*)${wrapper_fn_name}, &${arg_tmp_var}, 8 * 1024);') |
| 215 | } |
| 216 | } |
| 217 | g.writeln('// end go') |
| 218 | if node.is_expr { |
| 219 | handle = 'thread_${tmp}' |
| 220 | g.create_waiter_handler(call_ret_type, s_ret_typ, gohandle_name) |
| 221 | } |
| 222 | // Register the wrapper type and function |
| 223 | mut should_register := false |
| 224 | lock g.threaded_fns { |
| 225 | if name !in g.threaded_fns { |
| 226 | g.threaded_fns << name |
| 227 | should_register = true |
| 228 | } |
| 229 | } |
| 230 | if should_register { |
| 231 | g.type_definitions.writeln('\ntypedef struct ${wrapper_struct_name} {') |
| 232 | mut fn_var := '' |
| 233 | mut wrapper_return_type := call_ret_type |
| 234 | mut resolved_fn_params := []ast.Param{} |
| 235 | if node.call_expr.is_fn_var { |
| 236 | mut fn_var_type := node.call_expr.fn_var_type |
| 237 | // In generic contexts, fn_var_type may be stale from the last checker pass. |
| 238 | // Look up the original fn parameter from the table to get the generic fn type, |
| 239 | // then resolve it through convert_generic_type. |
| 240 | if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 |
| 241 | && g.cur_fn.generic_names.len > 0 { |
| 242 | cur_fn_name := g.cur_fn.fkey() |
| 243 | orig_fn := g.table.find_fn(cur_fn_name) or { ast.Fn{} } |
| 244 | for param in orig_fn.params { |
| 245 | if param.name == expr.name { |
| 246 | if param.typ.has_flag(.generic) |
| 247 | || g.type_has_unresolved_generic_parts(param.typ) { |
| 248 | mut muttable := unsafe { &ast.Table(g.table) } |
| 249 | if resolved := muttable.convert_generic_type(param.typ, |
| 250 | orig_fn.generic_names, g.cur_concrete_types) |
| 251 | { |
| 252 | fn_var_type = resolved |
| 253 | } |
| 254 | } |
| 255 | break |
| 256 | } |
| 257 | } |
| 258 | } |
| 259 | fn_sym := g.table.sym(fn_var_type) |
| 260 | info := fn_sym.info as ast.FnType |
| 261 | resolved_fn_params = info.func.params.clone() |
| 262 | wrapper_return_type = info.func.return_type |
| 263 | fn_var = g.fn_var_signature(ast.void_type, wrapper_return_type, |
| 264 | info.func.params.map(it.typ), 'fn') |
| 265 | } else if node.call_expr.left is ast.AnonFn { |
| 266 | f := node.call_expr.left.decl |
| 267 | wrapper_return_type = f.return_type |
| 268 | fn_var = |
| 269 | g.fn_var_signature(ast.void_type, wrapper_return_type, f.params.map(it.typ), 'fn') |
| 270 | } else { |
| 271 | if node.call_expr.is_method { |
| 272 | rec_sym := g.table.sym(g.unwrap_generic(node.call_expr.receiver_type)) |
| 273 | if f := rec_sym.find_method_with_generic_parent(node.call_expr.name) { |
| 274 | mut muttable := unsafe { &ast.Table(g.table) } |
| 275 | return_type := muttable.convert_generic_type(f.return_type, f.generic_names, |
| 276 | node.call_expr.concrete_types) or { f.return_type } |
| 277 | wrapper_return_type = return_type |
| 278 | mut arg_types := f.params.map(it.typ) |
| 279 | arg_types = arg_types.map(muttable.convert_generic_type(it, f.generic_names, |
| 280 | node.call_expr.concrete_types) or { it }) |
| 281 | fn_var = g.fn_var_signature(ast.void_type, return_type, arg_types, 'fn') |
| 282 | } |
| 283 | } else { |
| 284 | if f := g.table.find_fn(node.call_expr.name) { |
| 285 | concrete_types := node.call_expr.concrete_types.map(g.unwrap_generic(it)) |
| 286 | return_type := g.table.convert_generic_type(f.return_type, f.generic_names, |
| 287 | concrete_types) or { f.return_type } |
| 288 | wrapper_return_type = return_type |
| 289 | mut arg_types := f.params.map(it.typ) |
| 290 | arg_types = arg_types.map(g.table.convert_generic_type(it, f.generic_names, |
| 291 | concrete_types) or { it }) |
| 292 | for i, typ in arg_types { |
| 293 | mut typ_sym := g.table.sym(typ) |
| 294 | for { |
| 295 | if mut typ_sym.info is ast.Array { |
| 296 | typ_sym = g.table.sym(typ_sym.info.elem_type) |
| 297 | } else { |
| 298 | if typ_sym.info is ast.FnType { |
| 299 | arg_types[i] = expr.args[i].typ |
| 300 | } |
| 301 | break |
| 302 | } |
| 303 | } |
| 304 | } |
| 305 | fn_var = g.fn_var_signature(ast.void_type, return_type, arg_types, 'fn') |
| 306 | } |
| 307 | } |
| 308 | } |
| 309 | wrapper_return_type = g.unwrap_generic(wrapper_return_type) |
| 310 | wrapper_s_ret_typ := g.styp(wrapper_return_type) |
| 311 | if fn_var != '' { |
| 312 | g.type_definitions.writeln('\t${fn_var};') |
| 313 | } |
| 314 | if expr.is_method { |
| 315 | styp := g.styp(expr.receiver_type) |
| 316 | g.type_definitions.writeln('\t${styp} arg0;') |
| 317 | } |
| 318 | need_return_ptr := g.pref.os == .windows && wrapper_return_type != ast.void_type |
| 319 | for i, arg in expr.args { |
| 320 | mut arg_typ := arg.typ |
| 321 | // For fn var and AnonFn calls in generic contexts, use the declared |
| 322 | // parameter types instead of argument expression types, since the |
| 323 | // AST arg types may be stale from a previous generic instantiation. |
| 324 | if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 { |
| 325 | if expr.left is ast.AnonFn { |
| 326 | anon := expr.left as ast.AnonFn |
| 327 | f := anon.decl |
| 328 | if i < f.params.len { |
| 329 | arg_typ = g.unwrap_generic(f.params[i].typ) |
| 330 | } |
| 331 | } else if expr.is_fn_var && resolved_fn_params.len > 0 { |
| 332 | if i < resolved_fn_params.len { |
| 333 | arg_typ = resolved_fn_params[i].typ |
| 334 | } |
| 335 | } else { |
| 336 | resolved_arg_typ := g.unwrap_generic(arg_typ) |
| 337 | if resolved_arg_typ != 0 { |
| 338 | arg_typ = resolved_arg_typ |
| 339 | } |
| 340 | } |
| 341 | } |
| 342 | arg_sym := g.table.sym(arg_typ) |
| 343 | if arg_sym.info is ast.FnType { |
| 344 | sig := g.fn_var_signature(arg_typ, arg_sym.info.func.return_type, |
| 345 | arg_sym.info.func.params.map(it.typ), 'arg${i + 1}') |
| 346 | g.type_definitions.writeln('\t' + sig + ';') |
| 347 | } else { |
| 348 | styp := g.styp(arg_typ) |
| 349 | g.type_definitions.writeln('\t${styp} arg${i + 1};') |
| 350 | } |
| 351 | } |
| 352 | if is_spawn && g.pref.prealloc { |
| 353 | g.type_definitions.writeln('\tvoid* prealloc_scope;') |
| 354 | } |
| 355 | if need_return_ptr { |
| 356 | g.type_definitions.writeln('\tvoid* ret_ptr;') |
| 357 | } |
| 358 | g.type_definitions.writeln('} ${wrapper_struct_name};') |
| 359 | thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' } |
| 360 | g.waiter_fn_definitions.writeln('${g.static_non_parallel}${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg);') |
| 361 | g.gowrappers.writeln('${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg) {') |
| 362 | if is_spawn && g.pref.prealloc && wrapper_return_type == ast.void_type { |
| 363 | g.gowrappers.writeln('\tvoid* thread_prealloc_scope = builtin__prealloc_scope_begin();') |
| 364 | } |
| 365 | if wrapper_return_type != ast.void_type { |
| 366 | if g.pref.os == .windows { |
| 367 | g.gowrappers.write_string('\t*((${wrapper_s_ret_typ}*)(arg->ret_ptr)) = ') |
| 368 | } else { |
| 369 | if g.pref.prealloc { |
| 370 | g.gowrappers.writeln('\t${wrapper_s_ret_typ}* ret_ptr = (${wrapper_s_ret_typ}*) malloc(sizeof(${wrapper_s_ret_typ}));') |
| 371 | g.gowrappers.writeln('\tif (ret_ptr == NULL) builtin___v_panic(_S("thread return allocation failed"));') |
| 372 | } else { |
| 373 | g.gowrappers.writeln('\t${wrapper_s_ret_typ}* ret_ptr = (${wrapper_s_ret_typ}*) builtin___v_malloc(sizeof(${wrapper_s_ret_typ}));') |
| 374 | } |
| 375 | $if tinyc && arm64 { |
| 376 | g.gowrappers.write_string('\t${wrapper_s_ret_typ} tcc_bug_tmp_var = ') |
| 377 | } $else { |
| 378 | g.gowrappers.write_string('\t*ret_ptr = ') |
| 379 | } |
| 380 | } |
| 381 | } else { |
| 382 | g.gowrappers.write_string('\t') |
| 383 | } |
| 384 | if expr.is_method { |
| 385 | unwrapped_rec_type := g.unwrap_generic(expr.receiver_type) |
| 386 | typ_sym := g.table.sym(unwrapped_rec_type) |
| 387 | if typ_sym.kind == .interface |
| 388 | && (typ_sym.info as ast.Interface).defines_method(expr.name) { |
| 389 | rec_cc_type := g.cc_type(unwrapped_rec_type, false) |
| 390 | receiver_type_name := util.no_dots(rec_cc_type) |
| 391 | g.gowrappers.write_string2('${c_name(receiver_type_name)}_name_table[', 'arg->arg0') |
| 392 | dot_or_ptr := g.dot_or_ptr(unwrapped_rec_type) |
| 393 | mname := c_name(expr.name) |
| 394 | g.gowrappers.write_string2('${dot_or_ptr}_typ]._method_${mname}(', 'arg->arg0') |
| 395 | g.gowrappers.write_string('${dot_or_ptr}_object') |
| 396 | } else if typ_sym.kind == .struct && expr.is_field { |
| 397 | g.gowrappers.write_string('arg->arg0') |
| 398 | idot := if expr.left_type.is_ptr() { '->' } else { '.' } |
| 399 | mname := c_name(expr.name) |
| 400 | g.gowrappers.write_string('${idot}${mname}(') |
| 401 | } else { |
| 402 | g.gowrappers.write_string2('arg->fn(', 'arg->arg0') |
| 403 | } |
| 404 | if expr.args.len > 0 && (typ_sym.kind != .struct || !expr.is_field) { |
| 405 | g.gowrappers.write_string(', ') |
| 406 | } |
| 407 | } else { |
| 408 | g.gowrappers.write_string('arg->fn(') |
| 409 | } |
| 410 | if expr.args.len > 0 { |
| 411 | mut has_cast := false |
| 412 | for i in 0 .. expr.args.len { |
| 413 | if g.table.sym(expr.expected_arg_types[i]).kind == .interface |
| 414 | && g.table.sym(expr.args[i].typ).kind != .interface { |
| 415 | has_cast = true |
| 416 | break |
| 417 | } |
| 418 | } |
| 419 | if has_cast { |
| 420 | pos := g.out.len |
| 421 | g.call_args(expr) |
| 422 | mut call_args_str := g.out.after(pos).trim_space() |
| 423 | g.go_back(call_args_str.len) |
| 424 | mut rep_group := []string{cap: 2 * expr.args.len} |
| 425 | for i in 0 .. expr.args.len { |
| 426 | rep_group << g.expr_string(expr.args[i].expr) |
| 427 | rep_group << 'arg->arg${i + 1}' |
| 428 | } |
| 429 | if call_args_str.starts_with('I_') { |
| 430 | g.gowrappers.write_string(call_args_str.all_before('(')) |
| 431 | g.gowrappers.write_string('(') |
| 432 | g.gowrappers.write_string(call_args_str.all_after('(').replace_each(rep_group)) |
| 433 | } else { |
| 434 | call_args_str = call_args_str.replace_each(rep_group) |
| 435 | g.gowrappers.write_string(call_args_str) |
| 436 | } |
| 437 | } else if expr.name in ['print', 'println', 'eprint', 'eprintln', 'panic'] |
| 438 | && expr.args[0].typ != ast.string_type { |
| 439 | pos := g.out.len |
| 440 | g.gen_expr_to_string(expr.args[0].expr, expr.args[0].typ) |
| 441 | mut call_args_str := g.out.after(pos) |
| 442 | g.out.go_back(call_args_str.len) |
| 443 | mut rep_group := []string{cap: 2 * expr.args.len} |
| 444 | for i in 0 .. expr.args.len { |
| 445 | rep_group << g.expr_string(expr.args[i].expr) |
| 446 | rep_group << 'arg->arg${i + 1}' |
| 447 | } |
| 448 | call_args_str = call_args_str.replace_each(rep_group) |
| 449 | g.gowrappers.write_string(call_args_str) |
| 450 | } else { |
| 451 | for i in 0 .. expr.args.len { |
| 452 | expected_nr_muls := expr.expected_arg_types[i].nr_muls() |
| 453 | arg_nr_muls := expr.args[i].typ.nr_muls() |
| 454 | if arg_nr_muls > expected_nr_muls { |
| 455 | g.gowrappers.write_string('*'.repeat(arg_nr_muls - expected_nr_muls)) |
| 456 | } else if arg_nr_muls < expected_nr_muls { |
| 457 | g.gowrappers.write_string('&'.repeat(expected_nr_muls - arg_nr_muls)) |
| 458 | } |
| 459 | g.gowrappers.write_string('arg->arg${i + 1}') |
| 460 | if i != expr.args.len - 1 { |
| 461 | g.gowrappers.write_string(', ') |
| 462 | } |
| 463 | } |
| 464 | } |
| 465 | } |
| 466 | g.gowrappers.writeln(');') |
| 467 | $if tinyc && arm64 { |
| 468 | if g.pref.os != .windows && wrapper_return_type != ast.void_type { |
| 469 | g.gowrappers.writeln('\t*ret_ptr = tcc_bug_tmp_var;') |
| 470 | } |
| 471 | } |
| 472 | if is_spawn && g.pref.prealloc { |
| 473 | if wrapper_return_type == ast.void_type { |
| 474 | g.gowrappers.writeln('\tbuiltin__prealloc_scope_end(thread_prealloc_scope);') |
| 475 | } |
| 476 | g.gowrappers.writeln('\tbuiltin__prealloc_scope_release(arg->prealloc_scope);') |
| 477 | } |
| 478 | if is_spawn { |
| 479 | if g.pref.prealloc { |
| 480 | g.gowrappers.writeln('\tfree(arg);') |
| 481 | } else { |
| 482 | g.gowrappers.writeln('\tbuiltin___v_free(arg);') |
| 483 | } |
| 484 | } |
| 485 | if g.pref.os != .windows && wrapper_return_type != ast.void_type { |
| 486 | g.gowrappers.writeln('\treturn ret_ptr;') |
| 487 | } else { |
| 488 | g.gowrappers.writeln('\treturn 0;') |
| 489 | } |
| 490 | g.gowrappers.writeln('}') |
| 491 | } |
| 492 | if node.is_expr { |
| 493 | g.empty_line = false |
| 494 | g.write2(line, handle) |
| 495 | } |
| 496 | } |
| 497 | |
| 498 | // get current thread size, if fn hasn't defined return default |
| 499 | @[inline] |
| 500 | fn (mut g Gen) get_cur_thread_stack_size(name string) string { |
| 501 | ast_fn := g.table.fns[name] or { return '${g.pref.thread_stack_size}' } |
| 502 | attrs := ast_fn.attrs |
| 503 | if isnil(attrs) { |
| 504 | return '${g.pref.thread_stack_size}' |
| 505 | } |
| 506 | for attr in attrs { |
| 507 | if attr.name == 'spawn_stack' { |
| 508 | return attr.arg |
| 509 | } |
| 510 | } |
| 511 | return '${g.pref.thread_stack_size}' |
| 512 | } |
| 513 | |
| 514 | fn (mut g Gen) gen_gohandle_name(typ ast.Type) string { |
| 515 | mut gohandle_name := '' |
| 516 | if typ == ast.void_type { |
| 517 | if typ.has_flag(.option) { |
| 518 | gohandle_name = '__v_thread_Option_void' |
| 519 | } else if typ.has_flag(.result) { |
| 520 | gohandle_name = '__v_thread_Result_void' |
| 521 | } else { |
| 522 | gohandle_name = '__v_thread' |
| 523 | } |
| 524 | } else { |
| 525 | ret_styp := g.styp(g.unwrap_generic(typ)).replace('*', '_ptr') |
| 526 | gohandle_name = '__v_thread_${ret_styp}' |
| 527 | } |
| 528 | return gohandle_name |
| 529 | } |
| 530 | |
| 531 | fn (mut g Gen) create_waiter_handler(call_ret_type ast.Type, s_ret_typ string, gohandle_name string) { |
| 532 | // create wait handler for this return type if none exists |
| 533 | waiter_fn_name := gohandle_name + '_wait' |
| 534 | mut should_register := false |
| 535 | lock g.waiter_fns { |
| 536 | if waiter_fn_name !in g.waiter_fns { |
| 537 | g.waiter_fns << waiter_fn_name |
| 538 | should_register = true |
| 539 | } |
| 540 | } |
| 541 | if !should_register { |
| 542 | return |
| 543 | } |
| 544 | g.waiter_fn_definitions.writeln('${s_ret_typ} ${waiter_fn_name}(${gohandle_name} thread);') |
| 545 | g.gowrappers.writeln('\n${s_ret_typ} ${waiter_fn_name}(${gohandle_name} thread) {') |
| 546 | mut c_ret_ptr_ptr := 'NULL' |
| 547 | if call_ret_type != ast.void_type { |
| 548 | g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr;') |
| 549 | c_ret_ptr_ptr = '&ret_ptr' |
| 550 | } |
| 551 | if g.pref.os == .windows { |
| 552 | if call_ret_type == ast.void_type { |
| 553 | g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);') |
| 554 | } else { |
| 555 | g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);') |
| 556 | g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;') |
| 557 | } |
| 558 | } else { |
| 559 | g.gowrappers.writeln('\tif ((unsigned long int)thread == 0) { builtin___v_panic(_S("unable to join thread")); }') |
| 560 | g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)${c_ret_ptr_ptr});') |
| 561 | } |
| 562 | g.gowrappers.writeln('\tif (stat != 0) { builtin___v_panic(_S("unable to join thread")); }') |
| 563 | if g.pref.os == .windows { |
| 564 | if call_ret_type == ast.void_type { |
| 565 | g.gowrappers.writeln('\tCloseHandle(thread);') |
| 566 | } else { |
| 567 | g.gowrappers.writeln('\tCloseHandle(thread.handle);') |
| 568 | } |
| 569 | } |
| 570 | if call_ret_type != ast.void_type { |
| 571 | g.gowrappers.writeln('\t${s_ret_typ} ret = *ret_ptr;') |
| 572 | if g.pref.prealloc { |
| 573 | g.gowrappers.writeln('\tfree(ret_ptr);') |
| 574 | } else { |
| 575 | g.gowrappers.writeln('\tbuiltin___v_free(ret_ptr);') |
| 576 | } |
| 577 | g.gowrappers.writeln('\treturn ret;') |
| 578 | } |
| 579 | g.gowrappers.writeln('}') |
| 580 | } |
| 581 | |