v.gen.js: Interfaces support, fix for error propagation & panic stacktraces (#11471)

pull/11475/head
playX 2021-09-11 14:24:47 +03:00 committed by GitHub
parent 90e04d03b2
commit 5b619b99c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 13 deletions

View File

@ -79,3 +79,12 @@ pub fn (r rune) str() string {
return sb.str()
}
fn js_stacktrace() string {
stacktrace := ''
#let err = new TypeError();
#err.name = 'stacktrace: '
#stacktrace.str = err.stack
return stacktrace
}

View File

@ -7,7 +7,7 @@ module builtin
fn (a any) toString()
pub fn panic(s string) {
eprintln('V panic: $s')
eprintln('V panic: $s\n$js_stacktrace()')
exit(1)
}

View File

@ -130,9 +130,16 @@ fn (mut g JsGen) method_call(node ast.CallExpr) {
}
if typ_sym.kind == .interface_ && (typ_sym.info as ast.Interface).defines_method(node.name) {
// g.write('${g.js_name(receiver_type_name)}_name_table')
// g.expr(node.left)
g.writeln('/* TODO: Interface call */')
g.expr(it.left)
g.gen_deref_ptr(it.left_type)
g.write('.${it.name}(')
for i, arg in it.args {
g.expr(arg.expr)
if i != it.args.len - 1 {
g.write(', ')
}
}
g.write(')')
return
}
@ -229,9 +236,9 @@ fn (mut g JsGen) method_call(node ast.CallExpr) {
.propagate {
panicstr := '`optional not set (\${err})`'
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
g.writeln('return builtin.panic($panicstr)')
g.writeln('return panic($panicstr)')
} else {
g.writeln('builtin.js_throw(err)')
g.writeln('js_throw(err)')
}
}
else {}
@ -315,7 +322,7 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
g.stmt(it.or_block.stmts.last())
}
.propagate {
panicstr := '`optional not set (\${err})`'
panicstr := '`optional not set (\${err.val.msg})`'
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
g.writeln('return panic($panicstr)')
} else {

View File

@ -156,6 +156,45 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
if g.pref.is_test {
g.gen_js_main_for_tests()
}
g.enter_namespace('main')
// generate JS methods for interface methods
for _, iface_types in g.table.iface_types {
for ty in iface_types {
sym := g.table.get_type_symbol(ty)
for method in sym.methods {
p_sym := g.table.get_type_symbol(method.params[0].typ)
mname := g.js_name(p_sym.name) + '_' + method.name
g.write('${g.js_name(sym.name)}.prototype.$method.name = function(')
for i, param in method.params {
if i == 0 {
continue
}
g.write('${g.js_name(param.name)}')
if i != method.params.len - 1 {
g.write(',')
}
}
g.writeln(') {')
g.inc_indent()
g.write('return ${mname}(')
for i, param in method.params {
if i == 0 {
g.write('this')
} else {
g.write('${g.js_name(param.name)}')
}
if i != method.params.len - 1 {
g.write(',')
}
}
g.writeln(')')
g.dec_indent()
g.writeln('}')
}
}
}
g.write('js_main();')
g.escape_namespace()
// resolve imports
deps_resolved := graph.resolve()
nodes := deps_resolved.nodes
@ -178,6 +217,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
out += g.out.str()
/*
TODO(playX): Again add support for these doc comments
for node in nodes {
name := g.js_name(node.name).replace('.', '_')
if g.enable_doc {
@ -212,6 +252,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
// public scope
out += '\n'
}*/
if g.pref.sourcemap {
out += g.create_sourcemap()
}
@ -229,7 +270,7 @@ fn (g JsGen) create_sourcemap() string {
pub fn (mut g JsGen) gen_js_main_for_tests() {
g.enter_namespace('main')
g.writeln('(function() { ')
g.writeln('function js_main() { ')
g.inc_indent()
all_tfuncs := g.get_all_test_function_names()
@ -256,7 +297,7 @@ pub fn (mut g JsGen) gen_js_main_for_tests() {
g.writeln('bt.end_testing();')
}
g.dec_indent()
g.writeln('})();')
g.writeln('}')
g.escape_namespace()
}
@ -888,9 +929,13 @@ fn (mut g JsGen) expr(node ast.Expr) {
if node.op == .amp {
// if !node.right_type.is_pointer() {
// kind of weird way to handle references but it allows us to access type methods easily.
/*
g.write('(function(x) {')
g.write(' return { val: x, __proto__: Object.getPrototypeOf(x), valueOf: function() { return this.val; } }})( ')
g.expr(node.right)
g.write(')')*/
g.write('new \$ref(')
g.expr(node.right)
g.write(')')
//} else {
// g.expr(node.right)
@ -1406,11 +1451,11 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
// there is no concept of main in JS but we do have iife
g.writeln('/* program entry point */')
g.write('(')
// g.write('(')
if has_go {
g.write('async ')
}
g.write('function(')
g.write('function js_main(')
} else if it.is_anon {
g.write('function (')
} else {
@ -1451,9 +1496,18 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
g.writeln('}')
if is_main {
g.write(')();')
// g.write(')')
}
g.writeln('')
for attr in it.attrs {
match attr.name {
'export' {
g.writeln('globalThis.$attr.arg = ${g.js_name(it.name)};')
}
else {}
}
}
/*
if typ == .alias_method || typ == .iface_method {
sym := g.table.get_final_type_symbol(it.params[0].typ.set_nr_muls(0))
@ -1733,7 +1787,7 @@ fn (mut g JsGen) gen_return_stmt(it ast.Return) {
fn_return_is_optional := g.fn_decl.return_type.has_flag(.optional)
if node.exprs.len == 0 {
if fn_return_is_optional {
g.writeln('return {}')
g.writeln('return {state: new int(0)}')
} else {
g.writeln('return;')
}