@@ -5,7 +5,7 @@ use heck::ToUpperCamelCase;
55use std:: fmt:: Write ;
66use std:: mem;
77use std:: ops:: Deref ;
8- use wit_bindgen_core:: abi:: { Bindgen , Bitcast , Instruction } ;
8+ use wit_bindgen_core:: abi:: { self , Bindgen , Bitcast , Instruction } ;
99use wit_bindgen_core:: { uwrite, uwriteln, Direction , Ns } ;
1010use wit_parser:: abi:: WasmType ;
1111use wit_parser:: {
@@ -32,6 +32,7 @@ pub(crate) struct FunctionBindgen<'a, 'b> {
3232 is_block : bool ,
3333 fixed_statments : Vec < Fixed > ,
3434 parameter_type : ParameterType ,
35+ result_type : Option < Type > ,
3536}
3637
3738impl < ' a , ' b > FunctionBindgen < ' a , ' b > {
@@ -42,6 +43,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
4243 params : Box < [ String ] > ,
4344 results : Vec < TypeId > ,
4445 parameter_type : ParameterType ,
46+ result_type : Option < Type > ,
4547 ) -> FunctionBindgen < ' a , ' b > {
4648 let mut locals = Ns :: default ( ) ;
4749 // Ensure temporary variable names don't clash with parameter names:
@@ -67,6 +69,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
6769 is_block : false ,
6870 fixed_statments : Vec :: new ( ) ,
6971 parameter_type : parameter_type,
72+ result_type : result_type,
7073 }
7174 }
7275
@@ -304,7 +307,13 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
304307 let ty = self
305308 . interface_gen
306309 . type_name_with_qualifier ( & func. result . unwrap ( ) , true ) ;
307- uwriteln ! ( self . src, "{ty} {ret};" ) ;
310+ let is_async = InterfaceGenerator :: is_async ( & func. kind ) ;
311+
312+ if is_async {
313+ uwriteln ! ( self . src, "Task<{ty}> {ret};" ) ;
314+ } else {
315+ uwriteln ! ( self . src, "{ty} {ret};" ) ;
316+ }
308317 let mut cases = Vec :: with_capacity ( self . results . len ( ) ) ;
309318 let mut oks = Vec :: with_capacity ( self . results . len ( ) ) ;
310319 let mut payload_is_void = false ;
@@ -371,6 +380,44 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
371380 }
372381 ret
373382 }
383+
384+ fn emit_allocation_for_type ( & mut self , results : & [ WasmType ] ) -> String {
385+ let address = self . locals . tmp ( "address" ) ;
386+ let buffer_size = self . get_size_for_type ( results) ;
387+ let align = self . get_align_for_type ( results) ;
388+ uwriteln ! ( self . src, "void* {address} = global::System.Runtime.InteropServices.NativeMemory.AlignedAlloc({buffer_size}, {align});" ) ;
389+
390+ // TODO: Store the address somewhere so we can free it when the task completes.
391+ address
392+ }
393+
394+ fn get_size_for_type ( & self , results : & [ WasmType ] ) -> usize {
395+ match results {
396+ [ WasmType :: I32 ] => 4 ,
397+ [ WasmType :: I64 ] => 8 ,
398+ [ WasmType :: F32 ] => 4 ,
399+ [ WasmType :: F64 ] => 8 ,
400+ [ WasmType :: Pointer , WasmType :: Length ] => 4 , // TODO: Wasm64
401+ [ WasmType :: PointerOrI64 , WasmType :: Length ] => 8 ,
402+ _ => {
403+ todo ! ( "other types not yet supported" ) ;
404+ }
405+ }
406+ }
407+
408+ fn get_align_for_type ( & self , results : & [ WasmType ] ) -> usize {
409+ match results {
410+ [ WasmType :: I32 ] => 4 ,
411+ [ WasmType :: I64 ] => 8 ,
412+ [ WasmType :: F32 ] => 4 ,
413+ [ WasmType :: F64 ] => 8 ,
414+ [ WasmType :: Pointer , WasmType :: Length ] => 4 , // TODO: Wasm64
415+ [ WasmType :: PointerOrI64 , WasmType :: Length ] => 8 ,
416+ _ => {
417+ todo ! ( "other types not yet supported" ) ;
418+ }
419+ }
420+ }
374421}
375422
376423impl Bindgen for FunctionBindgen < ' _ , ' _ > {
@@ -400,22 +447,22 @@ impl Bindgen for FunctionBindgen<'_, '_> {
400447 } ) ) ,
401448 Instruction :: I32Load { offset }
402449 | Instruction :: PointerLoad { offset }
403- | Instruction :: LengthLoad { offset } => results. push ( format ! ( "global::System.BitConverter.ToInt32(new global::System.Span<byte>((void*)( {} + {offset}) , 4))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
404- Instruction :: I32Load8U { offset } => results. push ( format ! ( "new global::System.Span<byte>((void*)( {} + {offset}) , 1)[0]" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
405- Instruction :: I32Load8S { offset } => results. push ( format ! ( "(sbyte)new global::System.Span<byte>((void*)( {} + {offset}) , 1)[0]" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
406- Instruction :: I32Load16U { offset } => results. push ( format ! ( "global::System.BitConverter.ToUInt16(new global::System.Span<byte>((void*)( {} + {offset}) , 2))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
407- Instruction :: I32Load16S { offset } => results. push ( format ! ( "global::System.BitConverter.ToInt16(new global::System.Span<byte>((void*)( {} + {offset}) , 2))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
408- Instruction :: I64Load { offset } => results. push ( format ! ( "global::System.BitConverter.ToInt64(new global::System.Span<byte>((void*)( {} + {offset}) , 8))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
409- Instruction :: F32Load { offset } => results. push ( format ! ( "global::System.BitConverter.ToSingle(new global::System.Span<byte>((void*)( {} + {offset}) , 4))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
410- Instruction :: F64Load { offset } => results. push ( format ! ( "global::System.BitConverter.ToDouble(new global::System.Span<byte>((void*)( {} + {offset}) , 8))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
450+ | Instruction :: LengthLoad { offset } => results. push ( format ! ( "global::System.BitConverter.ToInt32(new global::System.Span<byte>((byte*) {} + {offset}, 4))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
451+ Instruction :: I32Load8U { offset } => results. push ( format ! ( "new global::System.Span<byte>((byte*) {} + {offset}, 1)[0]" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
452+ Instruction :: I32Load8S { offset } => results. push ( format ! ( "(sbyte)new global::System.Span<byte>((byte*) {} + {offset}, 1)[0]" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
453+ Instruction :: I32Load16U { offset } => results. push ( format ! ( "global::System.BitConverter.ToUInt16(new global::System.Span<byte>((byte*) {} + {offset}, 2))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
454+ Instruction :: I32Load16S { offset } => results. push ( format ! ( "global::System.BitConverter.ToInt16(new global::System.Span<byte>((byte*) {} + {offset}, 2))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
455+ Instruction :: I64Load { offset } => results. push ( format ! ( "global::System.BitConverter.ToInt64(new global::System.Span<byte>((byte*) {} + {offset}, 8))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
456+ Instruction :: F32Load { offset } => results. push ( format ! ( "global::System.BitConverter.ToSingle(new global::System.Span<byte>((byte*) {} + {offset}, 4))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
457+ Instruction :: F64Load { offset } => results. push ( format ! ( "global::System.BitConverter.ToDouble(new global::System.Span<byte>((byte*) {} + {offset}, 8))" , operands[ 0 ] , offset = offset. size_wasm32( ) ) ) ,
411458 Instruction :: I32Store { offset }
412459 | Instruction :: PointerStore { offset }
413- | Instruction :: LengthStore { offset } => uwriteln ! ( self . src, "global::System.BitConverter.TryWriteBytes(new global::System.Span<byte>((void*)( {} + {offset}) , 4), {});" , operands[ 1 ] , operands[ 0 ] , offset = offset. size_wasm32( ) ) ,
460+ | Instruction :: LengthStore { offset } => uwriteln ! ( self . src, "global::System.BitConverter.TryWriteBytes(new global::System.Span<byte>((byte*) {} + {offset}, 4), {});" , operands[ 1 ] , operands[ 0 ] , offset = offset. size_wasm32( ) ) ,
414461 Instruction :: I32Store8 { offset } => uwriteln ! ( self . src, "*(byte*)({} + {offset}) = (byte){};" , operands[ 1 ] , operands[ 0 ] , offset = offset. size_wasm32( ) ) ,
415- Instruction :: I32Store16 { offset } => uwriteln ! ( self . src, "global::System.BitConverter.TryWriteBytes(new global::System.Span<byte>((void*)( {} + {offset}) , 2), (short){});" , operands[ 1 ] , operands[ 0 ] , offset = offset. size_wasm32( ) ) ,
416- Instruction :: I64Store { offset } => uwriteln ! ( self . src, "global::System.BitConverter.TryWriteBytes(new global::System.Span<byte>((void*)( {} + {offset}) , 8), unchecked((long){}));" , operands[ 1 ] , operands[ 0 ] , offset = offset. size_wasm32( ) ) ,
417- Instruction :: F32Store { offset } => uwriteln ! ( self . src, "global::System.BitConverter.TryWriteBytes(new global::System.Span<byte>((void*)( {} + {offset}) , 4), unchecked((float){}));" , operands[ 1 ] , operands[ 0 ] , offset = offset. size_wasm32( ) ) ,
418- Instruction :: F64Store { offset } => uwriteln ! ( self . src, "global::System.BitConverter.TryWriteBytes(new global::System.Span<byte>((void*)( {} + {offset}) , 8), unchecked((double){}));" , operands[ 1 ] , operands[ 0 ] , offset = offset. size_wasm32( ) ) ,
462+ Instruction :: I32Store16 { offset } => uwriteln ! ( self . src, "global::System.BitConverter.TryWriteBytes(new global::System.Span<byte>((byte*) {} + {offset}, 2), (short){});" , operands[ 1 ] , operands[ 0 ] , offset = offset. size_wasm32( ) ) ,
463+ Instruction :: I64Store { offset } => uwriteln ! ( self . src, "global::System.BitConverter.TryWriteBytes(new global::System.Span<byte>((byte*) {} + {offset}, 8), unchecked((long){}));" , operands[ 1 ] , operands[ 0 ] , offset = offset. size_wasm32( ) ) ,
464+ Instruction :: F32Store { offset } => uwriteln ! ( self . src, "global::System.BitConverter.TryWriteBytes(new global::System.Span<byte>((byte*) {} + {offset}, 4), unchecked((float){}));" , operands[ 1 ] , operands[ 0 ] , offset = offset. size_wasm32( ) ) ,
465+ Instruction :: F64Store { offset } => uwriteln ! ( self . src, "global::System.BitConverter.TryWriteBytes(new global::System.Span<byte>((byte*) {} + {offset}, 8), unchecked((double){}));" , operands[ 1 ] , operands[ 0 ] , offset = offset. size_wasm32( ) ) ,
419466
420467 Instruction :: I64FromU64 => results. push ( format ! ( "unchecked((long)({}))" , operands[ 0 ] ) ) ,
421468 Instruction :: I32FromChar => results. push ( format ! ( "((int){})" , operands[ 0 ] ) ) ,
@@ -980,11 +1027,24 @@ impl Bindgen for FunctionBindgen<'_, '_> {
9801027 }
9811028
9821029 Instruction :: CallWasm { sig, .. } => {
1030+ let is_async = InterfaceGenerator :: is_async ( self . kind ) ;
1031+
1032+ let requires_async_return_buffer_param = is_async && sig. results . len ( ) >= 1 ;
1033+ let async_return_buffer = if requires_async_return_buffer_param {
1034+ let buffer = self . emit_allocation_for_type ( & sig. results ) ;
1035+ uwriteln ! ( self . src, "//TODO: store somewhere with the TaskCompletionSource, possibly in the state, using Task.AsyncState to retrieve it later." ) ;
1036+ Some ( buffer)
1037+ } else {
1038+ None
1039+ } ;
1040+
9831041 let assignment = match & sig. results [ ..] {
9841042 [ _] => {
9851043 let result = self . locals . tmp ( "result" ) ;
9861044 let assignment = format ! ( "var {result} = " ) ;
987- results. push ( result) ;
1045+ if !requires_async_return_buffer_param {
1046+ results. push ( result) ;
1047+ }
9881048 assignment
9891049 }
9901050
@@ -995,12 +1055,32 @@ impl Bindgen for FunctionBindgen<'_, '_> {
9951055
9961056 let func_name = self . func_name . to_upper_camel_case ( ) ;
9971057
998- let operands = operands. join ( ", " ) ;
1058+ // Async functions that return a result need to pass a buffer where the result will later be written.
1059+ let operands = match async_return_buffer {
1060+ Some ( ref buffer) if operands. is_empty ( ) => buffer. clone ( ) ,
1061+ Some ( ref buffer) => format ! ( "{}, {}" , operands. join( ", " ) , buffer) ,
1062+ None => operands. join ( ", " ) ,
1063+ } ;
9991064
10001065 uwriteln ! (
10011066 self . src,
10021067 "{assignment} {func_name}WasmInterop.wasmImport{func_name}({operands});"
10031068 ) ;
1069+
1070+ if let Some ( buffer) = async_return_buffer {
1071+ let result = abi:: lift_from_memory (
1072+ self . interface_gen . resolve ,
1073+ self ,
1074+ buffer. clone ( ) ,
1075+ & self . result_type . unwrap ( ) ,
1076+ ) ;
1077+ uwriteln ! (
1078+ self . src,
1079+ "global::System.Runtime.InteropServices.NativeMemory.Free({});" ,
1080+ buffer
1081+ ) ;
1082+ results. push ( result) ;
1083+ }
10041084 }
10051085
10061086 Instruction :: CallInterface { func, .. } => {
@@ -1027,6 +1107,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
10271107 }
10281108 }
10291109
1110+ let is_async = InterfaceGenerator :: is_async ( self . kind ) ;
10301111 match self . kind {
10311112 FunctionKind :: Constructor ( id) => {
10321113 let target = self . interface_gen . csharp_gen . all_resources [ id] . export_impl_name ( ) ;
@@ -1042,7 +1123,13 @@ impl Bindgen for FunctionBindgen<'_, '_> {
10421123 } ;
10431124
10441125 match func. result {
1045- None => uwriteln ! ( self . src, "{target}.{func_name}({oper});" ) ,
1126+ None => {
1127+ if is_async{
1128+ uwriteln ! ( self . src, "var ret = {target}.{func_name}({oper});" ) ;
1129+ } else {
1130+ uwriteln ! ( self . src, "{target}.{func_name}({oper});" ) ;
1131+ }
1132+ }
10461133 Some ( _ty) => {
10471134 let ret = self . handle_result_call ( func, target, func_name, oper) ;
10481135 results. push ( ret) ;
@@ -1051,6 +1138,25 @@ impl Bindgen for FunctionBindgen<'_, '_> {
10511138 }
10521139 }
10531140
1141+ if is_async {
1142+ self . interface_gen . csharp_gen . needs_async_support = true ;
1143+ let name = self . func_name . to_upper_camel_case ( ) ;
1144+ let ret_param = match func. result {
1145+ None => "" ,
1146+ Some ( _ty) => "ret.Result"
1147+ } ;
1148+
1149+ uwriteln ! ( self . src, r#"if (ret.IsCompletedSuccessfully)
1150+ {{
1151+ {name}TaskReturn({ret_param});
1152+ return (uint)CallbackCode.Exit;
1153+ }}
1154+
1155+ // TODO: Defer dropping borrowed resources until a result is returned.
1156+ return (uint)CallbackCode.Yield;
1157+ "# ) ;
1158+ }
1159+
10541160 for ( _, drop) in & self . resource_drops {
10551161 uwriteln ! ( self . src, "{drop}?.Dispose();" ) ;
10561162 }
@@ -1267,15 +1373,25 @@ impl Bindgen for FunctionBindgen<'_, '_> {
12671373 results. extend ( operands. iter ( ) . take ( * amt) . map ( |v| v. clone ( ) ) ) ;
12681374 }
12691375
1270- Instruction :: AsyncTaskReturn { .. }
1271- | Instruction :: FutureLower { .. }
1272- | Instruction :: FutureLift { .. }
1376+ Instruction :: FutureLower { .. } => {
1377+ let op = & operands[ 0 ] ;
1378+ results. push ( format ! ( "{op}.Handle" ) ) ;
1379+ }
1380+
1381+ Instruction :: AsyncTaskReturn { name : _, params : _ } => {
1382+ uwriteln ! ( self . src, "// TODO_task_cancel.forget();" ) ;
1383+ }
1384+
1385+ Instruction :: FutureLift { .. }
12731386 | Instruction :: StreamLower { .. }
12741387 | Instruction :: StreamLift { .. }
12751388 | Instruction :: ErrorContextLower { .. }
12761389 | Instruction :: ErrorContextLift { .. }
12771390 | Instruction :: DropHandle { .. }
1278- => todo ! ( ) ,
1391+ => {
1392+ dbg ! ( inst) ;
1393+ todo ! ( )
1394+ }
12791395 }
12801396 }
12811397
0 commit comments