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') {
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 (')
|
||||||
|
|
|
@ -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);')
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue