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