| 1 | module js |
| 2 | |
| 3 | import v.ast |
| 4 | import v.util |
| 5 | import strings |
| 6 | |
| 7 | pub const builtin_functions = ['print', 'println', 'eprint', 'eprintln', 'isnil', 'panic', 'exit'] |
| 8 | |
| 9 | fn (mut g JsGen) js_mname(name_ string) string { |
| 10 | mut is_js := false |
| 11 | is_overload := ['+', '-', '*', '/', '==', '<', '>'] |
| 12 | mut name := name_ |
| 13 | if name.starts_with('JS.') { |
| 14 | name = name[3..] |
| 15 | is_js = true |
| 16 | } |
| 17 | ns := get_ns(name) |
| 18 | name = if name in is_overload { |
| 19 | match name { |
| 20 | '+' { |
| 21 | '\$add' |
| 22 | } |
| 23 | '-' { |
| 24 | '\$sub' |
| 25 | } |
| 26 | '/' { |
| 27 | '\$div' |
| 28 | } |
| 29 | '*' { |
| 30 | '\$mul' |
| 31 | } |
| 32 | '%' { |
| 33 | '\$mod' |
| 34 | } |
| 35 | '==' { |
| 36 | 'eq' |
| 37 | } |
| 38 | '>' { |
| 39 | '\$gt' |
| 40 | } |
| 41 | '<' { |
| 42 | '\$lt' |
| 43 | } |
| 44 | else { |
| 45 | '' |
| 46 | } |
| 47 | } |
| 48 | } else if unsafe { g.ns == 0 } { |
| 49 | name |
| 50 | } else if ns == g.ns.name { |
| 51 | name.split('.').last() |
| 52 | } else { |
| 53 | g.get_alias(name) |
| 54 | } |
| 55 | mut parts := name.split('.') |
| 56 | if !is_js { |
| 57 | for i, p in parts { |
| 58 | if p in js_reserved { |
| 59 | parts[i] = 'v_${p}' |
| 60 | } |
| 61 | } |
| 62 | } |
| 63 | return parts.join('.') |
| 64 | } |
| 65 | |
| 66 | fn (mut g JsGen) js_call(node ast.CallExpr) { |
| 67 | g.call_stack << node |
| 68 | it := node |
| 69 | call_return_is_option := it.return_type.has_flag(.option) |
| 70 | is_await := node.name == 'JS.await' |
| 71 | if call_return_is_option { |
| 72 | if is_await { |
| 73 | g.writeln('await (async function () {') |
| 74 | } else { |
| 75 | g.writeln('(function () {') |
| 76 | } |
| 77 | g.writeln('try {') |
| 78 | g.writeln('let tmp = ') |
| 79 | } |
| 80 | if it.is_ctor_new { |
| 81 | g.write('new ') |
| 82 | } |
| 83 | if is_await { |
| 84 | g.write('await (') |
| 85 | |
| 86 | g.expr(it.args[0].expr) |
| 87 | g.write(').promise') |
| 88 | } else { |
| 89 | g.write('${g.js_mname(it.name)}(') |
| 90 | for i, arg in it.args { |
| 91 | g.expr(arg.expr) |
| 92 | if i != it.args.len - 1 { |
| 93 | g.write(', ') |
| 94 | } |
| 95 | } |
| 96 | // end call |
| 97 | g.write(')') |
| 98 | } |
| 99 | if call_return_is_option { |
| 100 | g.write(';\n') |
| 101 | prev_inside_or := g.inside_or |
| 102 | g.inside_or = true |
| 103 | defer { |
| 104 | g.inside_or = prev_inside_or |
| 105 | } |
| 106 | g.writeln('if (tmp === null) throw "none";') |
| 107 | g.writeln('return tmp;') |
| 108 | g.writeln('} catch(err) {') |
| 109 | g.inc_indent() |
| 110 | // gen or block contents |
| 111 | match it.or_block.kind { |
| 112 | .block { |
| 113 | if it.or_block.stmts.len > 1 { |
| 114 | g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1]) |
| 115 | } |
| 116 | // g.write('return ') |
| 117 | g.stmt(it.or_block.stmts.last()) |
| 118 | } |
| 119 | .propagate_option { |
| 120 | panicstr := '`option not set (\${err + ""})`' |
| 121 | if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' { |
| 122 | g.writeln('return builtin__panic(${panicstr})') |
| 123 | } else { |
| 124 | g.writeln('throw new Option({ state: new u8(2), err: error(new string(${panicstr})) });') |
| 125 | } |
| 126 | } |
| 127 | else {} |
| 128 | } |
| 129 | |
| 130 | // end catch |
| 131 | g.dec_indent() |
| 132 | g.writeln('}') |
| 133 | g.writeln('})()') |
| 134 | } |
| 135 | g.call_stack.delete_last() |
| 136 | } |
| 137 | |
| 138 | fn (mut g JsGen) js_method_call(node ast.CallExpr) { |
| 139 | g.call_stack << node |
| 140 | it := node |
| 141 | call_return_is_option := it.return_type.has_flag(.option) |
| 142 | if call_return_is_option { |
| 143 | g.writeln('(function () {') |
| 144 | g.writeln('try {') |
| 145 | g.writeln('let tmp = ') |
| 146 | } |
| 147 | if it.is_ctor_new { |
| 148 | g.write('new ') |
| 149 | } |
| 150 | g.expr(it.left) |
| 151 | g.write('.${g.js_mname(it.name)}(') |
| 152 | for i, arg in it.args { |
| 153 | g.expr(arg.expr) |
| 154 | if i != it.args.len - 1 { |
| 155 | g.write(', ') |
| 156 | } |
| 157 | } |
| 158 | // end method call |
| 159 | g.write(')') |
| 160 | if call_return_is_option { |
| 161 | prev_inside_or := g.inside_or |
| 162 | g.inside_or = true |
| 163 | defer { |
| 164 | g.inside_or = prev_inside_or |
| 165 | } |
| 166 | g.write(';\n') |
| 167 | g.writeln('if (tmp === null) throw "none";') |
| 168 | g.writeln('return tmp;') |
| 169 | g.writeln('} catch(err) {') |
| 170 | g.inc_indent() |
| 171 | // gen or block contents |
| 172 | match it.or_block.kind { |
| 173 | .block { |
| 174 | if it.or_block.stmts.len > 1 { |
| 175 | g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1]) |
| 176 | } |
| 177 | // g.write('return ') |
| 178 | g.stmt(it.or_block.stmts.last()) |
| 179 | } |
| 180 | .propagate_option { |
| 181 | panicstr := '`option not set (\${err + ""})`' |
| 182 | if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' { |
| 183 | g.writeln('return builtin__panic(${panicstr})') |
| 184 | } else { |
| 185 | g.writeln('throw new option({ state: new u8(2), err: error(new string(${panicstr})) });') |
| 186 | } |
| 187 | } |
| 188 | else {} |
| 189 | } |
| 190 | |
| 191 | // end catch |
| 192 | g.dec_indent() |
| 193 | g.writeln('}') |
| 194 | g.writeln('})()') |
| 195 | } |
| 196 | g.call_stack.delete_last() |
| 197 | } |
| 198 | |
| 199 | fn (mut g JsGen) method_call(node ast.CallExpr) { |
| 200 | g.call_stack << node |
| 201 | it := node |
| 202 | if it.name == 'str' { |
| 203 | g.gen_expr_to_string(node.left, node.left_type) |
| 204 | return |
| 205 | } |
| 206 | is_async := node.name == 'wait' && g.table.sym(node.receiver_type).name.starts_with('Promise<') |
| 207 | call_return_is_option := it.return_type.has_flag(.option) |
| 208 | if call_return_is_option { |
| 209 | if is_async { |
| 210 | g.writeln('(async function (){') |
| 211 | } else { |
| 212 | g.writeln('(function(){') |
| 213 | } |
| 214 | g.inc_indent() |
| 215 | g.writeln('try {') |
| 216 | g.inc_indent() |
| 217 | g.write('return unwrap(') |
| 218 | } |
| 219 | if node.name == 'str' { |
| 220 | mut rec_type := node.receiver_type |
| 221 | if rec_type.has_flag(.shared_f) { |
| 222 | rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0) |
| 223 | } |
| 224 | g.get_str_fn(rec_type) |
| 225 | } |
| 226 | mut unwrapped_rec_type := node.receiver_type |
| 227 | if unsafe { g.fn_decl != 0 } && g.fn_decl.generic_names.len > 0 { // in generic fn |
| 228 | unwrapped_rec_type = g.unwrap_generic(node.receiver_type) |
| 229 | } else { // in non-generic fn |
| 230 | sym := g.table.sym(node.receiver_type) |
| 231 | match sym.info { |
| 232 | ast.Struct, ast.Interface, ast.SumType { |
| 233 | generic_names := sym.info.generic_types.map(g.table.sym(it).name) |
| 234 | // see comment at top of vlib/v/gen/c/utils.v |
| 235 | mut muttable := unsafe { &ast.Table(g.table) } |
| 236 | if utyp := muttable.convert_generic_type(node.receiver_type, generic_names, |
| 237 | sym.info.concrete_types) |
| 238 | { |
| 239 | unwrapped_rec_type = utyp |
| 240 | } |
| 241 | } |
| 242 | else {} |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | mut typ_sym := g.table.sym(unwrapped_rec_type) |
| 247 | rec_cc_type := g.cc_type(unwrapped_rec_type, false) |
| 248 | mut receiver_type_name := util.no_dots(rec_cc_type) |
| 249 | // alias type that undefined this method (not include `str`) need to use parent type |
| 250 | if typ_sym.kind == .alias && node.name != 'str' && !typ_sym.has_method(node.name) { |
| 251 | unwrapped_rec_type = (typ_sym.info as ast.Alias).parent_type |
| 252 | typ_sym = g.table.sym(unwrapped_rec_type) |
| 253 | } |
| 254 | |
| 255 | if typ_sym.kind == .interface && (typ_sym.info as ast.Interface).defines_method(node.name) { |
| 256 | g.expr(it.left) |
| 257 | g.gen_deref_ptr(it.left_type) |
| 258 | g.write('.${it.name}(') |
| 259 | for i, arg in it.args { |
| 260 | g.expr(arg.expr) |
| 261 | if i != it.args.len - 1 { |
| 262 | g.write(', ') |
| 263 | } |
| 264 | } |
| 265 | g.write(')') |
| 266 | return |
| 267 | } |
| 268 | |
| 269 | left_sym := g.table.sym(node.left_type) |
| 270 | final_left_sym := g.table.final_sym(node.left_type) |
| 271 | |
| 272 | if final_left_sym.kind == .map && it.name in special_map_methods { |
| 273 | g.gen_map_method_call(it) |
| 274 | return |
| 275 | } else if final_left_sym.kind == .array { |
| 276 | if it.name in ['map', 'filter'] { |
| 277 | g.expr(it.left) |
| 278 | mut ltyp := it.left_type |
| 279 | for ltyp.is_ptr() { |
| 280 | g.write('.valueOf()') |
| 281 | ltyp = ltyp.deref() |
| 282 | } |
| 283 | g.write('.') |
| 284 | // Prevent 'it' from getting shadowed inside the match |
| 285 | |
| 286 | g.write(it.name) |
| 287 | g.write('(') |
| 288 | expr := node.args[0].expr |
| 289 | match expr { |
| 290 | ast.AnonFn { |
| 291 | g.gen_fn_decl(expr.decl) |
| 292 | g.write(')') |
| 293 | return |
| 294 | } |
| 295 | ast.Ident { |
| 296 | if expr.kind == .function { |
| 297 | g.write(g.js_name(expr.name)) |
| 298 | g.write(')') |
| 299 | return |
| 300 | } else if expr.kind == .variable && expr.info is ast.IdentVar { |
| 301 | v_sym := g.table.sym(expr.var_info().typ) |
| 302 | if v_sym.kind == .function { |
| 303 | g.write(g.js_name(expr.name)) |
| 304 | g.write(')') |
| 305 | return |
| 306 | } |
| 307 | } |
| 308 | } |
| 309 | else {} |
| 310 | } |
| 311 | |
| 312 | g.write('it => ') |
| 313 | g.expr(node.args[0].expr) |
| 314 | g.write(')') |
| 315 | return |
| 316 | } |
| 317 | |
| 318 | if it.name in special_array_methods { |
| 319 | g.gen_array_method_call(it) |
| 320 | return |
| 321 | } |
| 322 | |
| 323 | if node.name in ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', 'first', 'last', |
| 324 | 'pop_left', 'pop', 'clone', 'reverse', 'slice', 'pointers'] { |
| 325 | if !(left_sym.info is ast.Alias && typ_sym.has_method(node.name)) { |
| 326 | // `array_Xyz_clone` => `array_clone` |
| 327 | receiver_type_name = 'array' |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | if is_async { |
| 333 | g.write('await ') |
| 334 | g.expr(it.left) |
| 335 | g.write('.promise') |
| 336 | } else { |
| 337 | mut name := util.no_dots('${receiver_type_name}_${node.name}') |
| 338 | |
| 339 | name = g.generic_fn_name(node.concrete_types, name) |
| 340 | g.write('${name}(') |
| 341 | g.expr_with_expected_type(it.left, unwrapped_rec_type) |
| 342 | g.gen_deref_ptr(it.left_type) |
| 343 | g.write(',') |
| 344 | for i, arg in it.args { |
| 345 | expected_arg_type := if i < it.expected_arg_types.len { |
| 346 | it.expected_arg_types[i] |
| 347 | } else { |
| 348 | arg.typ |
| 349 | } |
| 350 | g.expr_with_expected_type(arg.expr, expected_arg_type) |
| 351 | if i != it.args.len - 1 { |
| 352 | g.write(', ') |
| 353 | } |
| 354 | } |
| 355 | g.write(')') |
| 356 | } |
| 357 | |
| 358 | if call_return_is_option { |
| 359 | // end unwrap |
| 360 | g.writeln(')') |
| 361 | g.dec_indent() |
| 362 | prev_inside_or := g.inside_or |
| 363 | g.inside_or = true |
| 364 | defer { |
| 365 | g.inside_or = prev_inside_or |
| 366 | } |
| 367 | // begin catch block |
| 368 | g.writeln('} catch(err) {') |
| 369 | g.inc_indent() |
| 370 | // gen or block contents |
| 371 | match it.or_block.kind { |
| 372 | .block { |
| 373 | if it.or_block.stmts.len > 1 { |
| 374 | g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1]) |
| 375 | } |
| 376 | // g.write('return ') |
| 377 | g.stmt(it.or_block.stmts.last()) |
| 378 | } |
| 379 | .propagate_option { |
| 380 | panicstr := '`option not set (\${err.valueOf().msg})`' |
| 381 | if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' { |
| 382 | g.writeln('return builtin__panic(${panicstr})') |
| 383 | } else { |
| 384 | g.writeln('js_throw(err)') |
| 385 | } |
| 386 | } |
| 387 | else {} |
| 388 | } |
| 389 | |
| 390 | // end catch |
| 391 | g.dec_indent() |
| 392 | g.writeln('}') |
| 393 | // end anon fn |
| 394 | g.dec_indent() |
| 395 | g.write('})()') |
| 396 | } |
| 397 | g.call_stack.delete_last() |
| 398 | } |
| 399 | |
| 400 | fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { |
| 401 | if it.should_be_skipped { |
| 402 | return |
| 403 | } |
| 404 | if it.is_method && (it.is_field || g.table.sym(it.receiver_type).name.starts_with('JS.')) { |
| 405 | g.js_method_call(it) |
| 406 | return |
| 407 | } else if it.name.starts_with('JS.') { |
| 408 | g.js_call(it) |
| 409 | return |
| 410 | } |
| 411 | if it.is_method { |
| 412 | g.method_call(it) |
| 413 | return |
| 414 | } |
| 415 | node := it |
| 416 | g.call_stack << it |
| 417 | mut name := g.js_name(it.name) |
| 418 | |
| 419 | is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic'] |
| 420 | if name in builtin_functions { |
| 421 | name = 'builtin__${name}' |
| 422 | } |
| 423 | print_method := name |
| 424 | ret_sym := g.table.sym(it.return_type) |
| 425 | if it.language == .js && ret_sym.name in v_types && ret_sym.name != 'void' { |
| 426 | g.write('new ') |
| 427 | g.write(ret_sym.name) |
| 428 | g.write('(') |
| 429 | } |
| 430 | call_return_is_option := it.return_type.has_flag(.option) |
| 431 | if call_return_is_option { |
| 432 | g.writeln('(function(){') |
| 433 | g.inc_indent() |
| 434 | g.writeln('try {') |
| 435 | g.inc_indent() |
| 436 | g.write('return unwrap(') |
| 437 | } |
| 438 | if is_print { |
| 439 | mut typ := node.args[0].typ |
| 440 | |
| 441 | expr := node.args[0].expr |
| 442 | g.write('${print_method} (') |
| 443 | g.gen_expr_to_string(expr, typ) |
| 444 | g.write(')') |
| 445 | return |
| 446 | } |
| 447 | name = g.generic_fn_name(node.concrete_types, name) |
| 448 | g.expr(it.left) |
| 449 | |
| 450 | g.write('${name}(') |
| 451 | for i, arg in it.args { |
| 452 | expected_arg_type := if i < it.expected_arg_types.len { |
| 453 | it.expected_arg_types[i] |
| 454 | } else { |
| 455 | arg.typ |
| 456 | } |
| 457 | g.expr_with_expected_type(arg.expr, expected_arg_type) |
| 458 | if i != it.args.len - 1 { |
| 459 | g.write(', ') |
| 460 | } |
| 461 | } |
| 462 | // end method call |
| 463 | g.write(')') |
| 464 | if call_return_is_option { |
| 465 | // end unwrap |
| 466 | prev_inside_or := g.inside_or |
| 467 | g.inside_or = true |
| 468 | defer { |
| 469 | g.inside_or = prev_inside_or |
| 470 | } |
| 471 | g.writeln(')') |
| 472 | g.dec_indent() |
| 473 | // begin catch block |
| 474 | g.writeln('} catch(err) {') |
| 475 | g.inc_indent() |
| 476 | // gen or block contents |
| 477 | match it.or_block.kind { |
| 478 | .block { |
| 479 | if it.or_block.stmts.len > 1 { |
| 480 | g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1]) |
| 481 | } |
| 482 | |
| 483 | // g.write('return ') |
| 484 | g.stmt(it.or_block.stmts.last()) |
| 485 | } |
| 486 | .propagate_option { |
| 487 | panicstr := '`option not set (\${err.valueOf().msg})`' |
| 488 | if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' { |
| 489 | g.writeln('return builtin__panic(${panicstr})') |
| 490 | } else { |
| 491 | g.writeln('js_throw(err)') |
| 492 | } |
| 493 | } |
| 494 | else {} |
| 495 | } |
| 496 | |
| 497 | // end catch |
| 498 | g.dec_indent() |
| 499 | g.writeln('}') |
| 500 | // end anon fn |
| 501 | g.dec_indent() |
| 502 | g.write('})()') |
| 503 | } |
| 504 | if it.language == .js && ret_sym.name in v_types && ret_sym.name != 'void' { |
| 505 | g.write(')') |
| 506 | } |
| 507 | g.call_stack.delete_last() |
| 508 | } |
| 509 | |
| 510 | enum FnGenType { |
| 511 | function |
| 512 | struct_method |
| 513 | alias_method |
| 514 | iface_method |
| 515 | } |
| 516 | |
| 517 | fn (g &JsGen) fn_gen_type(it &ast.FnDecl) FnGenType { |
| 518 | if it.is_method && g.table.sym(it.params[0].typ).kind == .alias { |
| 519 | return .alias_method |
| 520 | } else if it.is_method && g.table.sym(it.params[0].typ).kind == .interface { |
| 521 | return .iface_method |
| 522 | } else if it.is_method || it.no_body { |
| 523 | return .struct_method |
| 524 | } else { |
| 525 | return .function |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | fn (mut g JsGen) is_used_by_main(node ast.FnDecl) bool { |
| 530 | mut is_used_by_main := true |
| 531 | if g.pref.skip_unused { |
| 532 | fkey := node.fkey() |
| 533 | is_used_by_main = g.table.used_features.used_fns[fkey] |
| 534 | $if trace_skip_unused_fns ? { |
| 535 | println('> is_used_by_main: ${is_used_by_main} | node.name: ${node.name} | fkey: ${fkey} | node.is_method: ${node.is_method}') |
| 536 | } |
| 537 | if !is_used_by_main { |
| 538 | $if trace_skip_unused_fns_in_js_code ? { |
| 539 | g.writeln('// trace_skip_unused_fns_in_js_code, ${node.name}, fkey: ${fkey}') |
| 540 | } |
| 541 | } |
| 542 | } else { |
| 543 | $if trace_skip_unused_fns_in_js_code ? { |
| 544 | g.writeln('// trace_skip_unused_fns_in_js_code, ${node.name}, fkey: ${node.fkey()}') |
| 545 | } |
| 546 | } |
| 547 | return is_used_by_main |
| 548 | } |
| 549 | |
| 550 | fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) { |
| 551 | res := g.fn_gen_type(it) |
| 552 | if it.language == .js && it.no_body { |
| 553 | for attr in it.attrs { |
| 554 | match attr.name { |
| 555 | 'wasm_import' { |
| 556 | mut x := g.wasm_export[attr.arg] or { []string{} } |
| 557 | x << it.name |
| 558 | g.wasm_import[attr.arg] = x |
| 559 | } |
| 560 | else {} |
| 561 | } |
| 562 | } |
| 563 | return |
| 564 | } |
| 565 | if g.inside_builtin { |
| 566 | g.builtin_fns << it.name |
| 567 | } |
| 568 | if !g.is_used_by_main(it) { |
| 569 | return |
| 570 | } |
| 571 | if it.should_be_skipped { |
| 572 | return |
| 573 | } |
| 574 | cur_fn_decl := g.fn_decl |
| 575 | g.fn_decl = unsafe { &it } |
| 576 | g.gen_method_decl(it, res) |
| 577 | g.fn_decl = cur_fn_decl |
| 578 | } |
| 579 | |
| 580 | fn fn_has_go(node ast.FnDecl) bool { |
| 581 | mut has_go := false |
| 582 | for stmt in node.stmts { |
| 583 | if stmt is ast.ExprStmt { |
| 584 | if stmt.expr is ast.GoExpr { |
| 585 | has_go = true |
| 586 | break |
| 587 | } |
| 588 | } |
| 589 | } |
| 590 | return has_go |
| 591 | } |
| 592 | |
| 593 | fn (mut g JsGen) generic_fn_name(types []ast.Type, before string) string { |
| 594 | if types.len == 0 { |
| 595 | return before |
| 596 | } |
| 597 | |
| 598 | mut name := before + '_T' |
| 599 | for typ in types { |
| 600 | name += '_' + strings.repeat_string('__ptr__', typ.nr_muls()) + g.styp(typ.set_nr_muls(0)) |
| 601 | } |
| 602 | return name |
| 603 | } |
| 604 | |
| 605 | fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) { |
| 606 | node := it |
| 607 | if node.generic_names.len > 0 && g.cur_concrete_types.len == 0 { // need the cur_concrete_type check to avoid inf. recursion |
| 608 | // loop thru each generic type and generate a function |
| 609 | for concrete_types in g.table.fn_generic_types[node.fkey()] { |
| 610 | if g.pref.is_verbose { |
| 611 | syms := concrete_types.map(g.table.sym(it)) |
| 612 | the_type := syms.map(it.name).join(', ') |
| 613 | println('gen fn `${node.name}` for type `${the_type}`') |
| 614 | } |
| 615 | g.cur_concrete_types = concrete_types |
| 616 | g.gen_method_decl(node, typ) |
| 617 | } |
| 618 | g.cur_concrete_types = [] |
| 619 | return |
| 620 | } |
| 621 | cur_fn_decl := g.fn_decl |
| 622 | unsafe { |
| 623 | g.fn_decl = &it |
| 624 | } |
| 625 | cur_fn_save := g.table.cur_fn |
| 626 | defer { |
| 627 | g.table.cur_fn = cur_fn_save |
| 628 | } |
| 629 | unsafe { |
| 630 | g.table.cur_fn = &it |
| 631 | } |
| 632 | mut name := it.name |
| 633 | if name in ['+', '-', '*', '**', '/', '%', '<', '==', '[]', '[]='] { |
| 634 | name = util.replace_op(name) |
| 635 | } |
| 636 | |
| 637 | if node.is_method { |
| 638 | name = g.cc_type(node.receiver.typ, false) + '_' + name |
| 639 | } |
| 640 | name = g.js_name(name) |
| 641 | |
| 642 | name = g.generic_fn_name(g.cur_concrete_types, name) |
| 643 | if name in builtin_functions { |
| 644 | name = 'builtin__${name}' |
| 645 | } |
| 646 | if it.is_pub && !it.is_method { |
| 647 | g.push_pub_var(name) |
| 648 | } |
| 649 | if it.language == .js && it.is_method { |
| 650 | g.writeln('${g.styp(it.receiver.typ)}.prototype.${it.name} = ') |
| 651 | } |
| 652 | |
| 653 | mut has_go := fn_has_go(it) || it.has_await |
| 654 | for attr in it.attrs { |
| 655 | if attr.name == 'async' { |
| 656 | if g.pref.output_es5 { |
| 657 | verror('cannot use @[async] attribute when outputting ES5 source code') |
| 658 | } |
| 659 | has_go = true |
| 660 | break |
| 661 | } |
| 662 | } |
| 663 | is_main := it.name == 'main.main' |
| 664 | g.gen_attrs(it.attrs) |
| 665 | if is_main { |
| 666 | // there is no concept of main in JS but we do have iife |
| 667 | g.writeln('/* program entry point */') |
| 668 | if !g.pref.output_es5 { |
| 669 | // main function is always async |
| 670 | g.write('async ') |
| 671 | } |
| 672 | g.write('function js_main(') |
| 673 | } else if it.is_anon { |
| 674 | g.write('function (') |
| 675 | } else { |
| 676 | c := name[0] |
| 677 | if c in [`+`, `-`, `*`, `/`] { |
| 678 | name = util.replace_op(name) |
| 679 | } |
| 680 | // type_name := g.styp(it.return_type) |
| 681 | // generate jsdoc for the function |
| 682 | g.doc.gen_fn(it) |
| 683 | if has_go && !g.pref.output_es5 { |
| 684 | g.write('async ') |
| 685 | } |
| 686 | |
| 687 | g.write('function ') |
| 688 | |
| 689 | g.write('${name}(') |
| 690 | if it.is_pub && !it.is_method { |
| 691 | g.push_pub_var(name) |
| 692 | } |
| 693 | } |
| 694 | args := it.params |
| 695 | |
| 696 | g.fn_args(args, it.is_variadic) |
| 697 | g.writeln(') {') |
| 698 | for i, arg in args { |
| 699 | is_varg := i == args.len - 1 && it.is_variadic |
| 700 | arg_name := g.js_name(arg.name) |
| 701 | if is_varg { |
| 702 | g.writeln('${arg_name} = new array(new array_buffer({arr: ${arg_name},len: new int(${arg_name}.length),index_start: new int(0)}));') |
| 703 | } else { |
| 704 | asym := g.table.sym(arg.typ) |
| 705 | if asym.kind != .interface && asym.language != .js { |
| 706 | if arg.typ.is_ptr() || arg.is_mut { |
| 707 | g.writeln('${arg_name} = new \$ref(${arg_name})') |
| 708 | } |
| 709 | } |
| 710 | } |
| 711 | } |
| 712 | g.inc_indent() |
| 713 | g.writeln('try {') |
| 714 | g.inc_indent() |
| 715 | g.stmts(it.stmts) |
| 716 | g.dec_indent() |
| 717 | g.writeln('} catch (e) { ') |
| 718 | g.writeln('\tif (e instanceof ReturnException) { return e.val; } ') |
| 719 | g.writeln('\tthrow e;') |
| 720 | g.writeln('}') |
| 721 | g.dec_indent() |
| 722 | g.writeln('}') |
| 723 | |
| 724 | if is_main { |
| 725 | // g.write(')') |
| 726 | } |
| 727 | g.writeln('') |
| 728 | for attr in it.attrs { |
| 729 | match attr.name { |
| 730 | 'export' { |
| 731 | g.writeln('globalThis.${attr.arg} = ${g.js_name(it.name)};') |
| 732 | } |
| 733 | 'wasm_export' { |
| 734 | mut x := g.wasm_export[attr.arg] or { []string{} } |
| 735 | g.write('function \$wasm${g.js_name(it.name)}(') |
| 736 | g.fn_args(args, it.is_variadic) |
| 737 | g.writeln(') {') |
| 738 | g.write('\treturn ${name} (') |
| 739 | for i, arg in args { |
| 740 | is_varg := i == args.len - 1 && it.is_variadic |
| 741 | arg_name := g.js_name(arg.name) |
| 742 | if is_varg { |
| 743 | g.write('...${arg_name}') |
| 744 | } else { |
| 745 | g.gen_cast_tmp(arg_name, arg.typ) |
| 746 | } |
| 747 | if i != args.len - 1 { |
| 748 | g.write(',') |
| 749 | } |
| 750 | } |
| 751 | g.writeln(').valueOf();') |
| 752 | g.writeln('}') |
| 753 | x << it.name |
| 754 | g.wasm_export[attr.arg] = x |
| 755 | } |
| 756 | 'wasm_import' { |
| 757 | mut x := g.wasm_export[attr.arg] or { []string{} } |
| 758 | x << name |
| 759 | g.wasm_import[attr.arg] = x |
| 760 | } |
| 761 | else {} |
| 762 | } |
| 763 | } |
| 764 | |
| 765 | g.fn_decl = cur_fn_decl |
| 766 | } |
| 767 | |
| 768 | fn (mut g JsGen) fn_args(args []ast.Param, is_variadic bool) { |
| 769 | for i, arg in args { |
| 770 | name := g.js_name(arg.name) |
| 771 | is_varg := i == args.len - 1 && is_variadic |
| 772 | if is_varg { |
| 773 | g.write('...${name}') |
| 774 | } else { |
| 775 | g.write(name) |
| 776 | } |
| 777 | // if its not the last argument |
| 778 | if i < args.len - 1 { |
| 779 | g.write(', ') |
| 780 | } |
| 781 | } |
| 782 | } |
| 783 | |
| 784 | fn (mut g JsGen) gen_anon_fn(mut fun ast.AnonFn) { |
| 785 | mut fn_name := fun.decl.name |
| 786 | if fun.decl.generic_names.len > 0 { |
| 787 | fn_name = g.generic_fn_name(g.cur_concrete_types, fn_name) |
| 788 | } |
| 789 | if fun.has_gen[fn_name] { |
| 790 | return |
| 791 | } |
| 792 | fun.has_gen[fn_name] = true |
| 793 | it := fun.decl |
| 794 | cur_fn_decl := g.fn_decl |
| 795 | unsafe { |
| 796 | g.fn_decl = &it |
| 797 | } |
| 798 | cur_fn_save := g.table.cur_fn |
| 799 | defer { |
| 800 | g.table.cur_fn = cur_fn_save |
| 801 | } |
| 802 | unsafe { |
| 803 | g.table.cur_fn = &it |
| 804 | } |
| 805 | mut name := it.name |
| 806 | if name in ['+', '-', '*', '**', '/', '%', '<', '==', '[]', '[]='] { |
| 807 | name = util.replace_op(name) |
| 808 | } |
| 809 | g.writeln('(function () { ') |
| 810 | mut inherited2copy := map[string]string{} |
| 811 | for inherited in fun.inherited_vars { |
| 812 | if !inherited.is_mut { |
| 813 | copy := g.copy_val(inherited.typ, inherited.name) |
| 814 | inherited2copy[inherited.name] = copy |
| 815 | } |
| 816 | } |
| 817 | |
| 818 | name = g.js_name(name) |
| 819 | |
| 820 | name = g.generic_fn_name(g.table.cur_concrete_types, name) |
| 821 | if name in builtin_functions { |
| 822 | name = 'builtin__${name}' |
| 823 | } |
| 824 | if it.is_pub && !it.is_method { |
| 825 | g.push_pub_var(name) |
| 826 | } |
| 827 | g.gen_attrs(it.attrs) |
| 828 | |
| 829 | g.write('return function (') |
| 830 | |
| 831 | args := it.params |
| 832 | |
| 833 | g.fn_args(args, it.is_variadic) |
| 834 | g.writeln(') {') |
| 835 | |
| 836 | g.inc_indent() |
| 837 | for i, arg in args { |
| 838 | is_varg := i == args.len - 1 && it.is_variadic |
| 839 | arg_name := g.js_name(arg.name) |
| 840 | if is_varg { |
| 841 | g.writeln('${arg_name} = new array(new array_buffer({arr: ${arg_name},len: new int(${arg_name}.length),index_start: new int(0)}));') |
| 842 | } else { |
| 843 | asym := g.table.sym(arg.typ) |
| 844 | |
| 845 | if arg.typ.is_ptr() || (arg.is_mut && asym.kind != .interface && asym.language != .js) { |
| 846 | g.writeln('${arg_name} = new \$ref(${arg_name})') |
| 847 | } |
| 848 | } |
| 849 | } |
| 850 | |
| 851 | for inherited in fun.inherited_vars { |
| 852 | if !inherited.is_mut { |
| 853 | g.writeln('let ${inherited.name} = ${inherited2copy[inherited.name]};') |
| 854 | } |
| 855 | } |
| 856 | g.stmts(it.stmts) |
| 857 | g.dec_indent() |
| 858 | g.writeln('}})()') |
| 859 | |
| 860 | g.fn_decl = cur_fn_decl |
| 861 | } |
| 862 | |