js: support JS.await (#12726)
parent
e4850a007c
commit
105d7fcf75
|
@ -114,6 +114,9 @@ fn should_test(path string, backend string) ShouldTestStatus {
|
|||
if path.ends_with('_test.v') {
|
||||
return .test
|
||||
}
|
||||
if path.ends_with('_test.js.v') {
|
||||
return .test
|
||||
}
|
||||
if path.ends_with('.v') && path.count('.') == 2 {
|
||||
if !path.all_before_last('.v').all_before_last('.').ends_with('_test') {
|
||||
return .ignore
|
||||
|
|
|
@ -485,8 +485,9 @@ pub mut:
|
|||
return_type_pos token.Position // `string` in `fn (u User) name() string` position
|
||||
has_return bool
|
||||
should_be_skipped bool
|
||||
has_await bool // 'true' if this function uses JS.await
|
||||
//
|
||||
comments []Comment // comments *after* the header, but *before* `{`; used for InterfaceDecl
|
||||
comments []Comment // comments *after* the header, but *before* `{`; used for InterfaceDecl
|
||||
next_comments []Comment // coments that are one line after the decl; used for InterfaceDecl
|
||||
//
|
||||
source_file &File = 0
|
||||
|
|
|
@ -2550,6 +2550,35 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
|
|||
c.need_recheck_generic_fns = true
|
||||
}
|
||||
}
|
||||
if fn_name == 'JS.await' {
|
||||
if node.args.len > 1 {
|
||||
c.error('JS.await expects 1 argument, a promise value (e.g `JS.await(fs.read())`',
|
||||
node.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
|
||||
typ := c.expr(node.args[0].expr)
|
||||
tsym := c.table.get_type_symbol(typ)
|
||||
|
||||
if !tsym.name.starts_with('js.promise.Promise<') {
|
||||
c.error('JS.await: first argument must be a promise, got `$tsym.name`', node.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
c.table.cur_fn.has_await = true
|
||||
match tsym.info {
|
||||
ast.Struct {
|
||||
mut ret_type := tsym.info.concrete_types[0]
|
||||
ret_type = ret_type.set_flag(.optional)
|
||||
node.return_type = ret_type
|
||||
return ret_type
|
||||
}
|
||||
else {
|
||||
c.error('JS.await: Promise must be a struct type', node.pos)
|
||||
return ast.void_type
|
||||
}
|
||||
}
|
||||
panic('unreachable')
|
||||
}
|
||||
if fn_name == 'json.encode' {
|
||||
} else if fn_name == 'json.decode' && node.args.len > 0 {
|
||||
if node.args.len != 2 {
|
||||
|
|
|
@ -66,23 +66,35 @@ fn (mut g JsGen) js_call(node ast.CallExpr) {
|
|||
g.call_stack << node
|
||||
it := node
|
||||
call_return_is_optional := it.return_type.has_flag(.optional)
|
||||
is_await := node.name == 'JS.await'
|
||||
if call_return_is_optional {
|
||||
g.writeln('(function () {')
|
||||
if is_await {
|
||||
g.writeln('await (async function () {')
|
||||
} else {
|
||||
g.writeln('(function () {')
|
||||
}
|
||||
g.writeln('try {')
|
||||
g.writeln('let tmp = ')
|
||||
}
|
||||
if it.is_ctor_new {
|
||||
g.write('new ')
|
||||
}
|
||||
g.write('${g.js_mname(it.name)}(')
|
||||
for i, arg in it.args {
|
||||
g.expr(arg.expr)
|
||||
if i != it.args.len - 1 {
|
||||
g.write(', ')
|
||||
if is_await {
|
||||
g.write('await (')
|
||||
|
||||
g.expr(it.args[0].expr)
|
||||
g.write(').promise')
|
||||
} else {
|
||||
g.write('${g.js_mname(it.name)}(')
|
||||
for i, arg in it.args {
|
||||
g.expr(arg.expr)
|
||||
if i != it.args.len - 1 {
|
||||
g.write(', ')
|
||||
}
|
||||
}
|
||||
// end call
|
||||
g.write(')')
|
||||
}
|
||||
// end call
|
||||
g.write(')')
|
||||
if call_return_is_optional {
|
||||
g.write(';\n')
|
||||
g.writeln('if (tmp === null) throw "none";')
|
||||
|
@ -588,18 +600,20 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
|
|||
g.writeln('${g.typ(it.receiver.typ)}.prototype.$it.name = ')
|
||||
}
|
||||
|
||||
has_go := fn_has_go(it)
|
||||
|
||||
mut has_go := fn_has_go(it) || it.has_await
|
||||
for attr in it.attrs {
|
||||
if attr.name == 'async' {
|
||||
has_go = true
|
||||
break
|
||||
}
|
||||
}
|
||||
is_main := it.name == 'main.main'
|
||||
g.gen_attrs(it.attrs)
|
||||
if is_main {
|
||||
// there is no concept of main in JS but we do have iife
|
||||
g.writeln('/* program entry point */')
|
||||
|
||||
// g.write('(')
|
||||
if has_go {
|
||||
g.write('async ')
|
||||
}
|
||||
// main function is always async
|
||||
g.write('async ')
|
||||
g.write('function js_main(')
|
||||
} else if it.is_anon {
|
||||
g.write('function (')
|
||||
|
|
|
@ -321,6 +321,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
|
|||
if g.pref.sourcemap {
|
||||
out += g.create_sourcemap()
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
|
@ -335,7 +336,7 @@ fn (g JsGen) create_sourcemap() string {
|
|||
|
||||
pub fn (mut g JsGen) gen_js_main_for_tests() {
|
||||
g.enter_namespace('main')
|
||||
g.writeln('function js_main() { ')
|
||||
g.writeln('async function js_main() { ')
|
||||
g.inc_indent()
|
||||
all_tfuncs := g.get_all_test_function_names()
|
||||
|
||||
|
@ -351,7 +352,7 @@ pub fn (mut g JsGen) gen_js_main_for_tests() {
|
|||
g.writeln('main__BenchedTests_testing_step_start(bt,new string("$tcname"))')
|
||||
}
|
||||
|
||||
g.writeln('try { ${tcname}(); } catch (_e) {} ')
|
||||
g.writeln('try { let res = ${tcname}(); if (res instanceof Promise) { await res; } } catch (_e) {} ')
|
||||
if g.pref.is_stats {
|
||||
g.writeln('main__BenchedTests_testing_step_end(bt);')
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue