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