js: support JS.await (#12726)

pull/12732/head
playX 2021-12-05 14:33:53 +03:00 committed by GitHub
parent e4850a007c
commit 105d7fcf75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 66 additions and 18 deletions

View File

@ -114,6 +114,9 @@ fn should_test(path string, backend string) ShouldTestStatus {
if path.ends_with('_test.v') { if path.ends_with('_test.v') {
return .test return .test
} }
if path.ends_with('_test.js.v') {
return .test
}
if path.ends_with('.v') && path.count('.') == 2 { if path.ends_with('.v') && path.count('.') == 2 {
if !path.all_before_last('.v').all_before_last('.').ends_with('_test') { if !path.all_before_last('.v').all_before_last('.').ends_with('_test') {
return .ignore return .ignore

View File

@ -485,8 +485,9 @@ pub mut:
return_type_pos token.Position // `string` in `fn (u User) name() string` position return_type_pos token.Position // `string` in `fn (u User) name() string` position
has_return bool has_return bool
should_be_skipped 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 next_comments []Comment // coments that are one line after the decl; used for InterfaceDecl
// //
source_file &File = 0 source_file &File = 0

View File

@ -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 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' { if fn_name == 'json.encode' {
} else if fn_name == 'json.decode' && node.args.len > 0 { } else if fn_name == 'json.decode' && node.args.len > 0 {
if node.args.len != 2 { if node.args.len != 2 {

View File

@ -66,23 +66,35 @@ fn (mut g JsGen) js_call(node ast.CallExpr) {
g.call_stack << node g.call_stack << node
it := node it := node
call_return_is_optional := it.return_type.has_flag(.optional) call_return_is_optional := it.return_type.has_flag(.optional)
is_await := node.name == 'JS.await'
if call_return_is_optional { 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('try {')
g.writeln('let tmp = ') g.writeln('let tmp = ')
} }
if it.is_ctor_new { if it.is_ctor_new {
g.write('new ') g.write('new ')
} }
g.write('${g.js_mname(it.name)}(') if is_await {
for i, arg in it.args { g.write('await (')
g.expr(arg.expr)
if i != it.args.len - 1 { g.expr(it.args[0].expr)
g.write(', ') 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 { if call_return_is_optional {
g.write(';\n') g.write(';\n')
g.writeln('if (tmp === null) throw "none";') 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 = ') 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' is_main := it.name == 'main.main'
g.gen_attrs(it.attrs) g.gen_attrs(it.attrs)
if is_main { if is_main {
// there is no concept of main in JS but we do have iife // there is no concept of main in JS but we do have iife
g.writeln('/* program entry point */') g.writeln('/* program entry point */')
// main function is always async
// g.write('(') g.write('async ')
if has_go {
g.write('async ')
}
g.write('function js_main(') g.write('function js_main(')
} else if it.is_anon { } else if it.is_anon {
g.write('function (') g.write('function (')

View File

@ -321,6 +321,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
if g.pref.sourcemap { if g.pref.sourcemap {
out += g.create_sourcemap() out += g.create_sourcemap()
} }
return out return out
} }
@ -335,7 +336,7 @@ fn (g JsGen) create_sourcemap() string {
pub fn (mut g JsGen) gen_js_main_for_tests() { pub fn (mut g JsGen) gen_js_main_for_tests() {
g.enter_namespace('main') g.enter_namespace('main')
g.writeln('function js_main() { ') g.writeln('async function js_main() { ')
g.inc_indent() g.inc_indent()
all_tfuncs := g.get_all_test_function_names() 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('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 { if g.pref.is_stats {
g.writeln('main__BenchedTests_testing_step_end(bt);') g.writeln('main__BenchedTests_testing_step_end(bt);')
} }