From c3fce6104513ee24b42f10cffb8d9280acbf513c Mon Sep 17 00:00:00 2001 From: yowl Date: Sun, 30 Nov 2025 11:05:05 -0500 Subject: [PATCH 1/2] C# Move export and import types to respective classes. Capitilase import and export Add initial future support --- crates/csharp/src/FutureCommonSupport.cs | 59 +++ crates/csharp/src/FutureReaderSupport.cs | 49 +++ crates/csharp/src/FutureWriterSupport.cs | 44 +++ crates/csharp/src/csproj.rs | 14 +- crates/csharp/src/function.rs | 48 ++- crates/csharp/src/interface.rs | 336 ++++++++++++++++-- crates/csharp/src/world_generator.rs | 256 ++++++++++--- crates/test/src/lib.rs | 7 +- .../async/simple-future/runner.cs | 57 +++ .../runtime-async/async/simple-future/test.cs | 38 ++ .../simple-import-params-results/runner.cs | 12 +- .../simple-import-params-results/test.cs | 31 +- tests/runtime/demo/runner.cs | 7 +- tests/runtime/demo/test.cs | 8 +- tests/runtime/lists/runner.cs | 46 ++- tests/runtime/lists/test.cs | 4 +- tests/runtime/many-arguments/runner.cs | 4 +- tests/runtime/many-arguments/test.cs | 4 +- tests/runtime/numbers/runner.cs | 96 +++-- tests/runtime/numbers/test.cs | 8 +- tests/runtime/options/runner.cs | 18 +- tests/runtime/options/test.cs | 4 +- tests/runtime/records/runner.cs | 44 +-- tests/runtime/records/test.cs | 28 +- tests/runtime/resource-borrow/runner.cs | 4 +- tests/runtime/resource-borrow/test.cs | 8 +- .../intermediate.cs | 17 +- tests/runtime/resource_aggregates/runner.cs | 38 +- tests/runtime/resource_aggregates/test.cs | 32 +- tests/runtime/resource_alias/test.cs | 16 +- .../resource_alias_redux/_test_disabled.cs | 41 --- tests/runtime/resource_alias_redux/runner.cs | 20 +- tests/runtime/resource_alias_redux/test.cs | 45 +++ .../resource_borrow_in_record/runner.cs | 14 +- .../runtime/resource_borrow_in_record/test.cs | 10 +- tests/runtime/resource_floats/intermediate.cs | 20 +- .../resource-with-lists.cs | 9 +- tests/runtime/resources/resources.cs | 32 +- tests/runtime/results/intermediate.cs | 34 +- .../results/intermediate_with_wit_results.cs | 55 +-- .../runtime/rust/resource_into_inner/test.cs | 9 +- tests/runtime/strings/runner.cs | 10 +- tests/runtime/strings/test.cs | 6 +- tests/runtime/variants/runner.cs | 37 +- tests/runtime/variants/test.cs | 16 +- tests/runtime/versions/runner.cs | 14 +- tests/runtime/versions/test.cs | 8 +- 47 files changed, 1231 insertions(+), 486 deletions(-) create mode 100644 crates/csharp/src/FutureCommonSupport.cs create mode 100644 crates/csharp/src/FutureReaderSupport.cs create mode 100644 crates/csharp/src/FutureWriterSupport.cs create mode 100644 tests/runtime-async/async/simple-future/runner.cs create mode 100644 tests/runtime-async/async/simple-future/test.cs delete mode 100644 tests/runtime/resource_alias_redux/_test_disabled.cs create mode 100644 tests/runtime/resource_alias_redux/test.cs diff --git a/crates/csharp/src/FutureCommonSupport.cs b/crates/csharp/src/FutureCommonSupport.cs new file mode 100644 index 000000000..275dc53ca --- /dev/null +++ b/crates/csharp/src/FutureCommonSupport.cs @@ -0,0 +1,59 @@ +/** + * Helpers for future support. + */ + +public readonly struct WaitableStatus (int status) +{ + public int State => status & 0xf; + public int Count => (int)(status >> 4); + public bool IsBlocked => status == -1; + public bool IsCompleted => State == 0; + public bool IsDropped => State == 1; +} + +public enum EventCode +{ + None, + Subtask, + StreamRead, + StreamWrite, + FutureRead, + FutureWrite, + Cancel, +} + +public readonly struct EventWaitable +{ + public EventWaitable(EventCode eventCode, int waitable, int code) + { + Event = eventCode; + Waitable = waitable; + Status = new WaitableStatus(code); + } + public readonly EventCode Event; + public readonly int Waitable; + public readonly int Code; + + public readonly WaitableStatus Status; +} + +public partial class WaitableSet(int handle) : IDisposable +{ + public int Handle { get; } = handle; + + void Dispose(bool _disposing) + { + {{interop_name}}.WaitableSetDrop(Handle); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~WaitableSet() + { + Dispose(false); + } +} \ No newline at end of file diff --git a/crates/csharp/src/FutureReaderSupport.cs b/crates/csharp/src/FutureReaderSupport.cs new file mode 100644 index 000000000..8def9ad1f --- /dev/null +++ b/crates/csharp/src/FutureReaderSupport.cs @@ -0,0 +1,49 @@ +/** + * Helpers for future reader support. + */ + +public abstract class FutureReader(int handle) : IDisposable // : TODO Waitable +{ + public int Handle { get; } = handle; + + // TODO: Generate per type for this instrinsic. + public Task Read() + { + // TODO: Generate for the interop name and the namespace. + + var status = new WaitableStatus(ReadInternal()); + if (status.IsBlocked) + { + //TODO: store somewhere so we can complete it later. + var tcs = new TaskCompletionSource(); + + return tcs.Task; + } + if (status.IsCompleted) + { + return Task.CompletedTask; + } + + throw new NotImplementedException(); + } + + void Dispose(bool _disposing) + { + // Free unmanaged resources if any. + Drop(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~FutureReader() + { + Dispose(false); + } + + protected abstract int ReadInternal(); + protected abstract void Drop(); +} \ No newline at end of file diff --git a/crates/csharp/src/FutureWriterSupport.cs b/crates/csharp/src/FutureWriterSupport.cs new file mode 100644 index 000000000..2e81fedcb --- /dev/null +++ b/crates/csharp/src/FutureWriterSupport.cs @@ -0,0 +1,44 @@ +/** + * Helpers for future writer support. + */ + +public abstract class FutureWriter(int handle) // : TODO Waitable +{ + public int Handle { get; } = handle; + + // TODO: Generate per type for this instrinsic. + public Task Write() + { + // TODO: Generate for the interop name. + var status = new WaitableStatus(Write(Handle, IntPtr.Zero)); + if (status.IsBlocked) + { + //TODO: store somewhere so we can complete it later. + var tcs = new TaskCompletionSource(); + return tcs.Task; + } + + throw new NotImplementedException(); + } + + protected abstract void Drop(); + + void Dispose(bool _disposing) + { + // Free unmanaged resources if any. + Drop(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~FutureWriter() + { + Dispose(false); + } + + protected abstract int Write(int handle, IntPtr buffer); +} diff --git a/crates/csharp/src/csproj.rs b/crates/csharp/src/csproj.rs index ebd6c4276..52661d877 100644 --- a/crates/csharp/src/csproj.rs +++ b/crates/csharp/src/csproj.rs @@ -109,11 +109,10 @@ impl CSProjectLLVMBuilder { csproj.push_str( &format!( r#" - - - - - "#), + + + + "#), ); fs::write( @@ -153,8 +152,9 @@ impl CSProjectLLVMBuilder { } csproj.push_str( - r#" - "#, + r#" + +"#, ); fs::write(self.dir.join(format!("{camel}.csproj")), csproj)?; diff --git a/crates/csharp/src/function.rs b/crates/csharp/src/function.rs index 32d5a8128..8c33d1125 100644 --- a/crates/csharp/src/function.rs +++ b/crates/csharp/src/function.rs @@ -33,6 +33,7 @@ pub(crate) struct FunctionBindgen<'a, 'b> { fixed_statments: Vec, parameter_type: ParameterType, result_type: Option, + pub(crate) resource_type_name: Option, } impl<'a, 'b> FunctionBindgen<'a, 'b> { @@ -70,6 +71,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { fixed_statments: Vec::new(), parameter_type: parameter_type, result_type: result_type, + resource_type_name: None, } } @@ -1062,9 +1064,32 @@ impl Bindgen for FunctionBindgen<'_, '_> { None => operands.join(", "), }; + let (_namespace, interface_name) = + &CSharp::get_class_name_from_qualified_name(self.interface_gen.name); + let mut interop_name = format!("{}ImportsInterop", interface_name.strip_prefix("I").unwrap() + .strip_suffix(if self.interface_gen.direction == Direction::Import { "Imports" } else { "Exports" }).unwrap().to_upper_camel_case()); + + if self.interface_gen.is_world && self.interface_gen.direction == Direction::Import { + interop_name = format!("Imports.{interop_name}"); + } + + let resource_type_name = match self.kind { + FunctionKind::Method(resource_type_id) | + FunctionKind::Static(resource_type_id) | + FunctionKind::Constructor(resource_type_id) => { + format!( + ".{}", + self.interface_gen.csharp_gen.all_resources[resource_type_id] + .name + .to_upper_camel_case() + ) + } + _ => String::new(), + }; + uwriteln!( self.src, - "{assignment} {func_name}WasmInterop.wasmImport{func_name}({operands});" + "{assignment} {interop_name}{resource_type_name}.{func_name}WasmInterop.wasmImport{func_name}({operands});" ); if let Some(buffer) = async_return_buffer { @@ -1364,6 +1389,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } else { uwriteln!(self.src, "var {resource} = ({export_name}) {export_name}.repTable.Get({op});"); } + self.resource_type_name = Some(export_name); } } results.push(resource); @@ -1375,6 +1401,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::FutureLower { .. } => { let op = &operands[0]; + self.interface_gen.add_future(self.func_name); + results.push(format!("{op}.Handle")); } @@ -1382,8 +1410,18 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "// TODO_task_cancel.forget();"); } - Instruction::FutureLift { .. } - | Instruction::StreamLower { .. } + Instruction::FutureLift { payload: _, ty: _ } => { + // TODO get the prefix for the type + let sig_type_name = "Void"; + uwriteln!(self.src, "var reader = new {}.FutureReader{}({});", self.interface_gen.name, sig_type_name, operands[0]); + self.interface_gen.csharp_gen.needs_future_reader_support = true; + results.push("reader".to_string()); + + self.interface_gen.add_future(self.func_name); + self.interface_gen.csharp_gen.needs_future_reader_support = true; + } + + Instruction::StreamLower { .. } | Instruction::StreamLift { .. } | Instruction::ErrorContextLower { .. } | Instruction::ErrorContextLift { .. } @@ -1418,7 +1456,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwrite!( self.src, " - var {ret_area} = stackalloc {element_type}[{array_size}+1]; + var {ret_area} = stackalloc {element_type}[{array_size} + 1]; var {ptr} = ((int){ret_area}) + ({align} - 1) & -{align}; ", align = align.align_wasm32() @@ -1487,7 +1525,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } /// Dereference any number `TypeDefKind::Type` aliases to retrieve the target type. -fn dealias(resolve: &Resolve, mut id: TypeId) -> TypeId { +pub fn dealias(resolve: &Resolve, mut id: TypeId) -> TypeId { loop { match &resolve.types[id].kind { TypeDefKind::Type(Type::Id(that_id)) => id = *that_id, diff --git a/crates/csharp/src/interface.rs b/crates/csharp/src/interface.rs index 7ed582e30..572b3faec 100644 --- a/crates/csharp/src/interface.rs +++ b/crates/csharp/src/interface.rs @@ -20,6 +20,7 @@ pub(crate) struct InterfaceFragment { pub(crate) csharp_src: String, pub(crate) csharp_interop_src: String, pub(crate) stub: String, + pub(crate) direction: Option, // Types do not have a direction. } pub(crate) struct InterfaceTypeAndFragments { @@ -47,6 +48,8 @@ pub(crate) struct InterfaceGenerator<'a> { pub(crate) resolve: &'a Resolve, pub(crate) name: &'a str, pub(crate) direction: Direction, + pub(crate) futures: Vec, + pub(crate) is_world: bool, } impl InterfaceGenerator<'_> { @@ -132,11 +135,12 @@ impl InterfaceGenerator<'_> { } pub(crate) fn qualifier(&self, when: bool, ty: &TypeId) -> String { + let type_def = &self.resolve.types[*ty]; + // anonymous types dont get an owner from wit-parser, so assume they are part of an interface here. let owner = if let Some(owner_type) = self.csharp_gen.anonymous_type_owners.get(ty) { *owner_type } else { - let type_def = &self.resolve.types[*ty]; type_def.owner }; @@ -151,13 +155,37 @@ impl InterfaceGenerator<'_> { } if when { - let name = self.name; + let mut name = self.name; + let tmp_name: String; + if self.is_world { + if CSharp::type_is_bidirectional(self.resolve(), ty) { + name = name.rsplitn(2, ".").nth(1).unwrap(); + } + // World level types, except resources are always imports. + else if name.ends_with("Exports") && type_def.kind != TypeDefKind::Resource { + tmp_name = Self::replace_last(&name, "Exports", "Imports"); + name = &tmp_name; + } + } format!("{global_prefix}{name}.") } else { String::new() } } + fn replace_last(haystack: &str, needle: &str, replacement: &str) -> String { + if let Some(pos) = haystack.rfind(needle) { + let mut result = + String::with_capacity(haystack.len() - needle.len() + replacement.len()); + result.push_str(&haystack[..pos]); + result.push_str(replacement); + result.push_str(&haystack[pos + needle.len()..]); + result + } else { + haystack.to_string() + } + } + pub(crate) fn add_interface_fragment(self, is_export: bool) { self.csharp_gen .interface_fragments @@ -168,14 +196,211 @@ impl InterfaceGenerator<'_> { csharp_src: self.src, csharp_interop_src: self.csharp_interop_src, stub: self.stub, + direction: Some(self.direction), + }); + } + + pub(crate) fn add_futures(&mut self, import_module_name: &str) { + if self.futures.is_empty() { + return; + } + + let (_namespace, interface_name) = &CSharp::get_class_name_from_qualified_name(self.name); + let interop_name = format!("{}Interop", interface_name.strip_prefix("I").unwrap()); + + for future in &self.futures { + let camel_name = future.to_upper_camel_case(); + + //TODO: we need these per future type. + // Create a hash map.. + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-new-0][async]{future}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern ulong {camel_name}VoidNew(); + "# + ); + + // TODO: Move this and other type dependent functions out to another function. + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-cancel-read-0][async]{future}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern uint {camel_name}CancelRead(int readable); + "# + ); + + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-cancel-write-0][async]{future}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern uint {camel_name}CancelWrite(int writeable); + "# + ); + + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-drop-writeable-0][async]{future}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern void {camel_name}DropWriteable(int writeable); + "# + ); + + self.csharp_gen + .interface_fragments + .entry(self.name.to_string()) + .or_insert_with(|| InterfaceTypeAndFragments::new(false)) + .interface_fragments + .push(InterfaceFragment { + csharp_src: format!(r#" + public static (FutureReader, FutureWriter) {camel_name}VoidNew() + {{ + var packed = {interop_name}.{camel_name}VoidNew(); + var readerHandle = (int)(packed & 0xFFFFFFFF); + var writerHandle = (int)(packed >> 32); + + return (new FutureReaderVoid(readerHandle), new FutureWriterVoid(writerHandle)); + }} + "#).to_string(), + csharp_interop_src: "".to_string(), + stub: "".to_string(), + direction: Some(self.direction), + }); + + self.csharp_gen.needs_future_reader_support = true; + self.csharp_gen.needs_future_writer_support = true; + } + + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-set-new]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern int WaitableSetNew(); + "# + ); + + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-join]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern void WaitableJoin(int waitable, int set); + "# + ); + + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-set-wait]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static unsafe extern int WaitableSetWait(int waitable, int* waitableHandlePtr); + "# + ); + + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-set-drop]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static unsafe extern void WaitableSetDrop(int waitable); + "# + ); + + // TODO: for each type: + let future_type_name = "Void"; + + self.csharp_gen + .interface_fragments + .entry(self.name.to_string()) + .or_insert_with(|| InterfaceTypeAndFragments::new(false)) + .interface_fragments + .push(InterfaceFragment { + csharp_src: format!(r#" + public class FutureReader{future_type_name} : FutureReader + {{ + public FutureReader{future_type_name}(int handle) : base(handle) {{ }} + + protected override int ReadInternal() + {{ + return {interop_name}.FutureRead{future_type_name}(Handle, IntPtr.Zero); + }} + + protected override void Drop() + {{ + {interop_name}.FutureDropReader{future_type_name}(Handle); + }} + }} + + public class FutureWriter{future_type_name} : FutureWriter + {{ + public FutureWriter{future_type_name}(int handle) : base(handle) {{ }} + + protected override int Write(int handle, IntPtr buffer) + {{ + return {interop_name}.FutureWrite{future_type_name}(handle, buffer); + }} + + protected override void Drop() + {{ + {interop_name}.FutureDropWriter{future_type_name}(Handle); + }} + }} + "#).to_string(), + csharp_interop_src: format!(r#" + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[async-lower][future-read-0][async]read-future"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static unsafe extern int FutureRead{future_type_name}(int readable, IntPtr ptr); + + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[async-lower][future-write-0][async]read-future"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static unsafe extern int FutureWrite{future_type_name}(int writeable, IntPtr buffer); + + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-drop-readable-0][async]read-future"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern void FutureDropReader{future_type_name}(int readable); + + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-drop-writable-0][async]read-future"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern void FutureDropWriter{future_type_name}(int readable); + "#).to_string(), + stub: "".to_string(), + direction: Some(self.direction), + }); + + self.csharp_gen + .interface_fragments + .entry(self.name.to_string()) + .or_insert_with(|| InterfaceTypeAndFragments::new(false)) + .interface_fragments + .push(InterfaceFragment { + csharp_src: format!( + r#" + public static WaitableSet WaitableSetNew() + {{ + var waitable = {interop_name}.WaitableSetNew(); + return new WaitableSet(waitable); + }} + + public static void Join(FutureWriter writer, WaitableSet set) + {{ + {interop_name}.WaitableJoin(writer.Handle, set.Handle); + }} + + public unsafe static EventWaitable WaitableSetWait(WaitableSet set) + {{ + int* buffer = stackalloc int[2]; + var eventCode = (EventCode){interop_name}.WaitableSetWait(set.Handle, buffer); + return new EventWaitable(eventCode, buffer[0], buffer[1]); + }} + + "# + ) + .to_string(), + csharp_interop_src: "".to_string(), + stub: "".to_string(), + direction: Some(self.direction), }); } - pub(crate) fn add_world_fragment(self) { + pub(crate) fn add_world_fragment(self, direction: Option) { self.csharp_gen.world_fragments.push(InterfaceFragment { csharp_src: self.src, csharp_interop_src: self.csharp_interop_src, stub: self.stub, + direction, }); } @@ -279,14 +504,8 @@ impl InterfaceGenerator<'_> { func.name.to_string() }; - let target = if let FunctionKind::Freestanding = &func.kind { - &mut self.csharp_interop_src - } else { - &mut self.src - }; - uwrite!( - target, + self.csharp_interop_src, r#" internal static class {interop_camel_name}WasmInterop {{ @@ -298,7 +517,7 @@ impl InterfaceGenerator<'_> { for (src, params) in funcs { uwrite!( - target, + self.src, r#" {access} {modifiers} unsafe {result_type} {camel_name}({params}) {{ @@ -480,6 +699,7 @@ impl InterfaceGenerator<'_> { ); let src = bindgen.src; + let resource_type_name = bindgen.resource_type_name; let vars = bindgen .resource_drops @@ -518,7 +738,7 @@ impl InterfaceGenerator<'_> { 0 }) .map(|(name, ty)| { - let ty = self.type_name(ty); + let ty = self.type_name_with_qualifier(ty, true); let name = name.to_csharp_ident(); format!("{ty} {name}") }) @@ -588,19 +808,33 @@ impl InterfaceGenerator<'_> { if async_ { let import_module_name = &self.resolve.name_world_key(interface_name.unwrap()); + let (_namespace, interface_name) = + &CSharp::get_class_name_from_qualified_name(self.name); + let impl_name = if resource_type_name.is_some() { + resource_type_name.unwrap() + } else { + format!("{}Impl", interface_name.strip_prefix("I").unwrap()) + }; uwriteln!( self.csharp_interop_src, r#" [global::System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute(EntryPoint = "[callback][async-lift]{import_module_name}#{wasm_func_name}")] - public static uint {camel_name}Callback(uint eventRaw, uint waitable, uint code) + public static int {camel_name}Callback(uint eventRaw, uint waitable, uint code) {{ // TODO: decode the parameters - return (uint)CallbackCode.Exit; + return {impl_name}.{camel_name}Callback(); }} "# ); + uwriteln!( + self.src, + r#" + public static abstract int {camel_name}Callback(); + "# + ); + // TODO: The task return function can take up to 16 core parameters. let task_return_param = match &sig.results[..] { [] => "", @@ -628,12 +862,19 @@ impl InterfaceGenerator<'_> { } if self.csharp_gen.opts.generate_stub { - let sig = self.sig_string(func, true); + let sig: String = self.sig_string(func, true); + let func_name = func.item_name().to_upper_camel_case(); uwrite!( self.stub, r#" - {sig} {{ + {sig} + {{ + throw new global::System.NotImplementedException(); + }} + + public static int {func_name}Callback(/* TODO: event arg */) + {{ throw new global::System.NotImplementedException(); }} "# @@ -787,9 +1028,9 @@ impl InterfaceGenerator<'_> { .map(|ty| self.type_name_with_qualifier(ty, qualifier)) .unwrap_or_else(|| "".to_owned()); if name.is_empty() { - return "Task".to_owned(); + return "FutureReader".to_owned(); } else { - return format!("Task<{name}>"); + return format!("FutureReader<{name}>"); } } _ => { @@ -972,6 +1213,7 @@ impl InterfaceGenerator<'_> { {access} interface I{upper_camel} {{ "# ); + self.csharp_gen.needs_rep_table = true; if self.csharp_gen.opts.generate_stub { let super_ = self.type_name_with_qualifier(&Type::Id(id), true); @@ -995,6 +1237,14 @@ impl InterfaceGenerator<'_> { } } }; + + uwrite!( + self.csharp_interop_src, + r#" + internal static class {upper_camel} + {{ + "# + ); } pub(crate) fn end_resource(&mut self) { @@ -1013,6 +1263,13 @@ impl InterfaceGenerator<'_> { }} " ); + + uwrite!( + self.csharp_interop_src, + " + }} + " + ); } fn sig_string(&mut self, func: &Function, qualifier: bool) -> String { @@ -1052,6 +1309,10 @@ impl InterfaceGenerator<'_> { format!("{access} {modifiers} {result_type} {camel_name}({params})") } + + pub(crate) fn add_future(&mut self, func_name: &str) { + self.futures.push(func_name.to_string()); + } } impl<'a> CoreInterfaceGenerator<'a> for InterfaceGenerator<'a> { @@ -1265,7 +1526,7 @@ impl<'a> CoreInterfaceGenerator<'a> for InterfaceGenerator<'a> { self.type_name(&Type::Id(id)); } - fn type_enum(&mut self, _id: TypeId, name: &str, enum_: &Enum, docs: &Docs) { + fn type_enum(&mut self, ty: TypeId, name: &str, enum_: &Enum, docs: &Docs) { self.print_docs(docs); let name = name.to_upper_camel_case(); @@ -1279,14 +1540,28 @@ impl<'a> CoreInterfaceGenerator<'a> for InterfaceGenerator<'a> { let access = self.csharp_gen.access_modifier(); - uwrite!( - self.src, - " - {access} enum {name} {{ - {cases} - }} - " - ); + // Write the top level bidirectional types to csharp_gen. Could also create a class to gather the interface level bidrectional types. + match &self.resolve.types[ty].owner { + TypeOwner::World(_id) => { + self.csharp_gen.bidirectional_types_src.insert(format!( + " + {access} enum {name} {{ + {cases} + }} + " + )); + } + _ => { + uwrite!( + self.src, + " + {access} enum {name} {{ + {cases} + }} + " + ); + } + }; } fn type_alias(&mut self, id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { @@ -1380,10 +1655,9 @@ fn modifiers(func: &Function, name: &str, direction: Direction) -> String { _ => "", }; - let abstract_modifier = if direction == Direction::Export { - " abstract" - } else { - "" + let abstract_modifier = match (direction, &func.kind) { + (Direction::Export, _) => " abstract", + _ => "", }; let async_modifier = match &func.kind { diff --git a/crates/csharp/src/world_generator.rs b/crates/csharp/src/world_generator.rs index 69c130cb0..ac8b8abea 100644 --- a/crates/csharp/src/world_generator.rs +++ b/crates/csharp/src/world_generator.rs @@ -12,7 +12,8 @@ use wit_bindgen_core::{uwrite, Direction, Files, InterfaceGenerator as _, WorldG use wit_component::WitPrinter; use wit_parser::abi::WasmType; use wit_parser::{ - Function, InterfaceId, Resolve, SizeAlign, Type, TypeId, TypeOwner, WorldId, WorldKey, + Function, InterfaceId, Resolve, SizeAlign, Type, TypeDefKind, TypeId, TypeOwner, WorldId, + WorldItem, WorldKey, }; /// CSharp is the world generator for wit files. It coordinates all the generated code. @@ -33,6 +34,8 @@ pub struct CSharp { pub(crate) needs_rep_table: bool, pub(crate) needs_wit_exception: bool, pub(crate) needs_async_support: bool, + pub(crate) needs_future_reader_support: bool, + pub(crate) needs_future_writer_support: bool, pub(crate) interface_fragments: HashMap, pub(crate) world_fragments: Vec, pub(crate) sizes: SizeAlign, @@ -41,6 +44,9 @@ pub struct CSharp { pub(crate) all_resources: HashMap, pub(crate) world_resources: HashMap, pub(crate) import_funcs_called: bool, + + // Top level types that are bidirectional like enums, to save code size and not duplicate whene unnecessary. + pub(crate) bidirectional_types_src: HashSet, } impl CSharp { @@ -62,6 +68,7 @@ impl CSharp { resolve: &'a Resolve, name: &'a str, direction: Direction, + is_world: bool, ) -> InterfaceGenerator<'a> { InterfaceGenerator { src: String::new(), @@ -71,6 +78,8 @@ impl CSharp { resolve, name, direction, + futures: Vec::new(), + is_world, } } @@ -87,6 +96,50 @@ impl CSharp { (String::new(), String::new()) } } + + // We can share some types to save some code size and allow a more intuitive user experience. + pub(crate) fn type_is_bidirectional(resolve: &Resolve, ty: &TypeId) -> bool { + let type_def = &resolve.types[*ty]; + let kind = &type_def.kind; + + return match kind { + TypeDefKind::Flags(_) => true, + TypeDefKind::Enum(_) => true, + _ => false, + }; + } + + fn write_world_fragments( + &mut self, + direction: Direction, + direction_name: &str, + src: &mut String, + access: &str, + name: &String, + ) { + if self + .world_fragments + .iter() + .any(|f| f.direction == Some(direction)) + { + uwrite!( + src, + " + {access} interface I{name}World{direction_name}{{ + " + ); + src.push_str( + &self + .world_fragments + .iter() + .filter(|f| f.direction == Some(direction)) + .map(|f| f.csharp_src.deref()) + .collect::>() + .join("\n"), + ); + src.push_str("}\n"); + } + } } impl WorldGenerator for CSharp { @@ -105,7 +158,7 @@ impl WorldGenerator for CSharp { ) -> anyhow::Result<()> { let name = interface_name(self, resolve, key, Direction::Import); self.interface_names.insert(id, name.clone()); - let mut gen = self.interface(resolve, &name, Direction::Import); + let mut gen = self.interface(resolve, &name, Direction::Import, false); let mut old_resources = mem::take(&mut gen.csharp_gen.all_resources); gen.types(id); @@ -113,6 +166,7 @@ impl WorldGenerator for CSharp { old_resources.extend(new_resources.clone()); gen.csharp_gen.all_resources = old_resources; + let import_module_name = &resolve.name_world_key(key); for (resource, funcs) in by_resource( resolve.interfaces[id] .functions @@ -124,7 +178,6 @@ impl WorldGenerator for CSharp { gen.start_resource(resource, Some(key)); } - let import_module_name = &resolve.name_world_key(key); for func in funcs { gen.import(import_module_name, func); } @@ -137,6 +190,8 @@ impl WorldGenerator for CSharp { // for anonymous types gen.define_interface_types(id); + gen.add_futures(import_module_name); + gen.add_interface_fragment(false); Ok(()) @@ -152,9 +207,10 @@ impl WorldGenerator for CSharp { self.import_funcs_called = true; let name = &format!("{}-world", resolve.worlds[world].name).to_upper_camel_case(); - let name = &format!("{name}.I{name}"); - let mut gen = self.interface(resolve, name, Direction::Import); + let name = &format!("{name}.I{name}Imports"); + let mut gen = self.interface(resolve, name, Direction::Import, true); + //TODO: This generates resource types for imports even when not used, i.e. no imported functions. for (resource, funcs) in by_resource( funcs.iter().copied(), gen.csharp_gen.world_resources.keys().copied(), @@ -172,7 +228,7 @@ impl WorldGenerator for CSharp { } } - gen.add_world_fragment(); + gen.add_world_fragment(Some(Direction::Import)); } fn export_interface( @@ -184,7 +240,7 @@ impl WorldGenerator for CSharp { ) -> anyhow::Result<()> { let name = interface_name(self, resolve, key, Direction::Export); self.interface_names.insert(id, name.clone()); - let mut gen = self.interface(resolve, &name, Direction::Export); + let mut gen = self.interface(resolve, &name, Direction::Export, false); let mut old_resources = mem::take(&mut gen.csharp_gen.all_resources); gen.types(id); @@ -215,6 +271,9 @@ impl WorldGenerator for CSharp { // for anonymous types gen.define_interface_types(id); + let import_module_name = &resolve.name_world_key(key); + gen.add_futures(&format!("[export]{import_module_name}")); + gen.add_interface_fragment(true); Ok(()) } @@ -222,15 +281,39 @@ impl WorldGenerator for CSharp { fn export_funcs( &mut self, resolve: &Resolve, - world: WorldId, + world_id: WorldId, funcs: &[(&str, &Function)], _files: &mut Files, ) -> anyhow::Result<()> { - let name = &format!("{}-world", resolve.worlds[world].name).to_upper_camel_case(); - let name = &format!("{name}.I{name}"); - let mut gen = self.interface(resolve, name, Direction::Export); + let name = &format!("{}-world", resolve.worlds[world_id].name).to_upper_camel_case(); + let name = &format!("{name}.I{name}Exports"); + let mut gen = self.interface(resolve, name, Direction::Export, true); + + // Write the export types used by the functions. + let world = &resolve.worlds[world_id]; + let mut types = Vec::new(); + for (name, export) in world.exports.iter() { + match name { + WorldKey::Name(name) => match export { + WorldItem::Type(id) => { + if !CSharp::type_is_bidirectional(resolve, &id) { + types.push((name.as_str(), *id)) + } + } + _ => {} + }, + WorldKey::Interface(_) => {} + } + } + + if !types.is_empty() { + export_types(&mut gen, &types); + } - for (resource, funcs) in by_resource(funcs.iter().copied(), iter::empty()) { + for (resource, funcs) in by_resource( + funcs.iter().copied(), + gen.csharp_gen.world_resources.keys().copied(), + ) { if let Some(resource) = resource { gen.start_resource(resource, None); } @@ -244,7 +327,7 @@ impl WorldGenerator for CSharp { } } - gen.add_world_fragment(); + gen.add_world_fragment(Some(Direction::Export)); Ok(()) } @@ -256,8 +339,8 @@ impl WorldGenerator for CSharp { _files: &mut Files, ) { let name = &format!("{}-world", resolve.worlds[world].name).to_upper_camel_case(); - let name = &format!("{name}.I{name}"); - let mut gen = self.interface(resolve, name, Direction::Import); + let name = &format!("{name}.I{name}Imports"); + let mut gen = self.interface(resolve, name, Direction::Import, false); let mut old_resources = mem::take(&mut gen.csharp_gen.all_resources); for (ty_name, ty) in types { @@ -268,7 +351,7 @@ impl WorldGenerator for CSharp { gen.csharp_gen.all_resources = old_resources; gen.csharp_gen.world_resources = new_resources; - gen.add_world_fragment(); + gen.add_world_fragment(Some(Direction::Import)); } fn finish(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) -> anyhow::Result<()> { @@ -294,6 +377,13 @@ impl WorldGenerator for CSharp { let mut src = String::new(); src.push_str(&header); + let mut producers = wasm_metadata::Producers::empty(); + producers.add( + "processed-by", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + ); + let access = self.access_modifier(); uwrite!( @@ -301,7 +391,6 @@ impl WorldGenerator for CSharp { " namespace {world_namespace} {{ - {access} interface I{name}World {{ " ); @@ -309,19 +398,22 @@ impl WorldGenerator for CSharp { &self .world_fragments .iter() + .filter(|f| f.direction.is_none()) .map(|f| f.csharp_src.deref()) .collect::>() .join("\n"), ); - let mut producers = wasm_metadata::Producers::empty(); - producers.add( - "processed-by", - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_VERSION"), + src.push_str( + self.bidirectional_types_src + .iter() + .cloned() + .collect::>() + .join("\n") + .as_str(), ); - - src.push_str("}\n"); + self.write_world_fragments(Direction::Import, "Imports", &mut src, access, &name); + self.write_world_fragments(Direction::Export, "Exports", &mut src, access, &name); if self.needs_result { uwrite!( @@ -494,18 +586,57 @@ impl WorldGenerator for CSharp { if !&self.world_fragments.is_empty() { src.push_str("\n"); - src.push_str("namespace exports {\n"); + if self + .world_fragments + .iter() + .any(|f| f.direction == Some(Direction::Import)) + { + src.push_str("\n"); - src.push_str(&format!("{access} static class {name}World\n")); - src.push_str("{"); + src.push_str("namespace Imports {\n"); - for fragment in &self.world_fragments { - src.push_str("\n"); + src.push_str(&format!( + "{access} partial class {name}WorldImportsInterop : I{name}WorldImports\n" + )); + src.push_str("{"); - src.push_str(&fragment.csharp_interop_src); + for fragment in self + .world_fragments + .iter() + .filter(|f| f.direction == Some(Direction::Import)) + { + if !fragment.csharp_interop_src.is_empty() { + src.push_str("\n"); + + src.push_str(&fragment.csharp_interop_src); + } + } + src.push_str("}\n"); + src.push_str("}\n"); + } + + if self + .world_fragments + .iter() + .any(|f| f.direction == Some(Direction::Export)) + { + src.push_str("namespace Exports {\n"); + + src.push_str(&format!("{access} static class {name}WorldInterop\n")); + src.push_str("{"); + + for fragment in self + .world_fragments + .iter() + .filter(|f| f.direction == Some(Direction::Export)) + { + src.push_str("\n"); + + src.push_str(&fragment.csharp_interop_src); + } + src.push_str("}\n"); + src.push_str("}\n"); } - src.push_str("}\n"); - src.push_str("}\n"); } if self.needs_async_support { @@ -513,6 +644,32 @@ impl WorldGenerator for CSharp { src.push_str(include_str!("AsyncSupport.cs")); } + if (self.needs_future_reader_support || self.needs_future_writer_support) + && self.interface_fragments.len() > 0 + { + let full_name = self.interface_fragments.iter().next().unwrap().0; + + let (namespace, interface_name) = + &CSharp::get_class_name_from_qualified_name(full_name); + let base_name = interface_name.strip_prefix("I").unwrap(); + let full_name = format!("{namespace}.{base_name}Interop"); + src.push_str("\n"); + src.push_str( + &include_str!("FutureCommonSupport.cs") + .replace("{{interop_name}}", full_name.as_str()), + ); + } + + if self.needs_future_reader_support { + src.push_str("\n"); + src.push_str(include_str!("FutureReaderSupport.cs")); + } + + if self.needs_future_writer_support { + src.push_str("\n"); + src.push_str(include_str!("FutureWriterSupport.cs")); + } + src.push_str("\n"); src.push_str("}\n"); @@ -575,7 +732,7 @@ impl WorldGenerator for CSharp { if self.opts.generate_stub { generate_stub( - format!("I{name}World"), + format!("I{name}WorldExports"), files, Stubs::World(&self.world_fragments), ); @@ -732,6 +889,12 @@ enum Stubs<'a> { Interface(&'a Vec), } +fn export_types(gen: &mut InterfaceGenerator, types: &[(&str, TypeId)]) { + for (ty_name, ty) in types { + gen.define_type(ty_name, *ty); + } +} + // We cant use "StructLayout.Pack" as dotnet will use the minimum of the type and the "Pack" field, // so for byte it would always use 1 regardless of the "Pack". pub fn dotnet_aligned_array(array_size: usize, required_alignment: usize) -> (usize, String) { @@ -800,14 +963,21 @@ fn interface_name( } }; - let name = match name { - WorldKey::Name(name) => name.to_upper_camel_case(), - WorldKey::Interface(id) => resolve.interfaces[*id] - .name - .as_ref() - .unwrap() - .to_upper_camel_case(), - }; + let name = format!( + "{}{}", + match name { + WorldKey::Name(name) => name.to_upper_camel_case(), + WorldKey::Interface(id) => resolve.interfaces[*id] + .name + .as_ref() + .unwrap() + .to_upper_camel_case(), + }, + match direction { + Direction::Import => "Imports", + Direction::Export => "Exports", + } + ); let namespace = match &pkg { Some(name) => { @@ -836,8 +1006,8 @@ fn interface_name( "{}wit.{}.{}I{name}", world_namespace, match direction { - Direction::Import => "imports", - Direction::Export => "exports", + Direction::Import => "Imports", + Direction::Export => "Exports", }, namespace ) diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 59c2482a0..773d547a6 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -816,7 +816,12 @@ impl Runner<'_> { } wasmparser::Validator::new_with_features(wasmparser::WasmFeatures::all()) .validate_all(&wasm) - .with_context(|| format!("compiler produced invalid wasm file {output:?}"))?; + .with_context(|| { + format!( + "compiler produced invalid wasm file {output:?} for component {}", + component.name + ) + })?; Ok(output) } diff --git a/tests/runtime-async/async/simple-future/runner.cs b/tests/runtime-async/async/simple-future/runner.cs new file mode 100644 index 000000000..02ace66e8 --- /dev/null +++ b/tests/runtime-async/async/simple-future/runner.cs @@ -0,0 +1,57 @@ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; +using RunnerWorld.wit.Imports.my.test; +using RunnerWorld; + +public class Program +{ + public static async Task Main(string[] args) + { + { + var (reader, writer) = IIImports.ReadFutureVoidNew(); + + var writeTask = writer.Write(); + Debug.Assert(!writeTask.IsCompleted); + + var task = IIImports.ReadFuture(reader); + Debug.Assert(task.IsCompleted); + + var set = IIImports.WaitableSetNew(); + IIImports.Join(writer, set); + + var ev = new EventWaitable(); + var status = IIImports.WaitableSetWait(set); + Debug.Assert(status.Event == EventCode.FutureWrite); + Debug.Assert(status.Waitable == writer.Handle); + Debug.Assert(status.Status.IsCompleted); + Debug.Assert(status.Status.Count == 0); + + writer.Dispose(); + set.Dispose(); + } + + { + var (reader, writer) = IIImports.DropFutureVoidNew(); + + var writeTask = writer.Write(); + Debug.Assert(!writeTask.IsCompleted); + + var task = IIImports.DropFuture(reader); + Debug.Assert(task.IsCompleted); + + var set = IIImports.WaitableSetNew(); + IIImports.Join(writer, set); + + var ev = new EventWaitable(); + var status = IIImports.WaitableSetWait(set); + Debug.Assert(status.Event == EventCode.FutureWrite); + Debug.Assert(status.Waitable == writer.Handle); + Debug.Assert(status.Status.IsDropped); + Debug.Assert(status.Status.Count == 0); + + writer.Dispose(); + set.Dispose(); + } + } +} \ No newline at end of file diff --git a/tests/runtime-async/async/simple-future/test.cs b/tests/runtime-async/async/simple-future/test.cs new file mode 100644 index 000000000..b165cf603 --- /dev/null +++ b/tests/runtime-async/async/simple-future/test.cs @@ -0,0 +1,38 @@ +using System.Diagnostics; + +namespace TestWorld.wit.Exports.my.test +{ + public class IExportsImpl : IIExports + { + public static Task ReadFuture(FutureReader reader) + { + var task = reader.Read(); + + Debug.Assert(task.IsCompleted); + // TODO: Should we check the Count? + + reader.Dispose(); + + return Task.CompletedTask; + } + + public static int ReadFutureCallback() + { + Debug.Assert(false); + return 0; + } + + public static Task DropFuture(FutureReader reader) + { + reader.Dispose(); + return Task.CompletedTask; + } + + public static int DropFutureCallback() + { + Debug.Assert(false); + return 0; + } + + } +} diff --git a/tests/runtime-async/async/simple-import-params-results/runner.cs b/tests/runtime-async/async/simple-import-params-results/runner.cs index 1098b9209..370037532 100644 --- a/tests/runtime-async/async/simple-import-params-results/runner.cs +++ b/tests/runtime-async/async/simple-import-params-results/runner.cs @@ -1,28 +1,28 @@ using System; using System.Runtime.InteropServices; using System.Diagnostics; -using RunnerWorld.wit.imports.a.b; +using RunnerWorld.wit.Imports.a.b; using System.Text; public class Program { public static async Task Main(string[] args) { - var t = II.OneArgument(1); + var t = IIImports.OneArgument(1); Debug.Assert(t.IsCompletedSuccessfully); - var tOneResult = II.OneResult(); + var tOneResult = IIImports.OneResult(); Debug.Assert(tOneResult.IsCompletedSuccessfully); Debug.Assert(tOneResult.Result == 2); - var tOneArgumentAndResult = II.OneArgumentAndResult(3); + var tOneArgumentAndResult = IIImports.OneArgumentAndResult(3); Debug.Assert(tOneArgumentAndResult.IsCompletedSuccessfully); Debug.Assert(tOneArgumentAndResult.Result == 4); - var tTwoArguments = II.TwoArguments(5, 6); + var tTwoArguments = IIImports.TwoArguments(5, 6); Debug.Assert(tTwoArguments.IsCompletedSuccessfully); - var tTwoArgumentsAndResult = II.TwoArgumentsAndResult(7, 8); + var tTwoArgumentsAndResult = IIImports.TwoArgumentsAndResult(7, 8); Debug.Assert(tTwoArgumentsAndResult.IsCompletedSuccessfully); Debug.Assert(tTwoArgumentsAndResult.Result == 9); } diff --git a/tests/runtime-async/async/simple-import-params-results/test.cs b/tests/runtime-async/async/simple-import-params-results/test.cs index 57c0ac905..8c9db521b 100644 --- a/tests/runtime-async/async/simple-import-params-results/test.cs +++ b/tests/runtime-async/async/simple-import-params-results/test.cs @@ -1,9 +1,9 @@ using System.Diagnostics; -using TestWorld.wit.exports.a.b; +using TestWorld.wit.Exports.a.b; -namespace TestWorld.wit.exports.a.b +namespace TestWorld.wit.Exports.a.b { - public class IImpl : II + public class IExportsImpl : IIExports { public static async Task OneArgument(uint x) { @@ -33,5 +33,30 @@ public static async Task TwoArgumentsAndResult(uint x, uint y) Debug.Assert(y == 8); return 9; } + + public static int OneArgumentCallback() + { + throw new NotImplementedException(); + } + + public static int OneResultCallback() + { + throw new NotImplementedException(); + } + + public static int OneArgumentAndResultCallback() + { + throw new NotImplementedException(); + } + + public static int TwoArgumentsCallback() + { + throw new NotImplementedException(); + } + + public static int TwoArgumentsAndResultCallback() + { + throw new NotImplementedException(); + } } } diff --git a/tests/runtime/demo/runner.cs b/tests/runtime/demo/runner.cs index 84cfc0fc1..2c3f68020 100644 --- a/tests/runtime/demo/runner.cs +++ b/tests/runtime/demo/runner.cs @@ -1,12 +1,9 @@ -using System; -using System.Runtime.InteropServices; -using System.Diagnostics; -using RunnerWorld.wit.imports.a.b; +using RunnerWorld.wit.Imports.a.b; public class Program { public static void Main(string[] args) { - TheTestInterop.X(); + ITheTestImports.X(); } } diff --git a/tests/runtime/demo/test.cs b/tests/runtime/demo/test.cs index e5c0974f8..44087a889 100644 --- a/tests/runtime/demo/test.cs +++ b/tests/runtime/demo/test.cs @@ -1,10 +1,6 @@ -using System; -using System.Diagnostics; -using TestWorld; +namespace TestWorld.wit.Exports.a.b; -namespace TestWorld; - -public class TheTestImpl : ITestWorld +public class TheTestExportsImpl : ITheTestExports { public static void X() { diff --git a/tests/runtime/lists/runner.cs b/tests/runtime/lists/runner.cs index c6f082646..20f96740d 100644 --- a/tests/runtime/lists/runner.cs +++ b/tests/runtime/lists/runner.cs @@ -1,7 +1,5 @@ -using System; -using System.Runtime.InteropServices; using System.Diagnostics; -using RunnerWorld.wit.imports.test.lists; +using RunnerWorld.wit.Imports.test.lists; using System.Text; @@ -9,30 +7,30 @@ public class Program { public static void Main(string[] args) { - ToTestInterop.EmptyListParam(new byte[0]); - ToTestInterop.EmptyStringParam(""); + IToTestImports.EmptyListParam(new byte[0]); + IToTestImports.EmptyStringParam(""); { - byte[] result = ToTestInterop.EmptyListResult(); + byte[] result = IToTestImports.EmptyListResult(); Debug.Assert(result.Length == 0); } { - string result = ToTestInterop.EmptyStringResult(); + string result = IToTestImports.EmptyStringResult(); Debug.Assert(result.Length == 0); } - ToTestInterop.ListParam(new byte[] { (byte)1, (byte)2, (byte)3, (byte)4 }); - ToTestInterop.ListParam((new byte[] { (byte)1, (byte)2, (byte)3, (byte)4 }).AsSpan()); - ToTestInterop.ListParam((new byte[] { (byte)1, (byte)2, (byte)3, (byte)4 }).AsMemory()); - ToTestInterop.ListParam2("foo"); - ToTestInterop.ListParam3(new List() { + IToTestImports.ListParam(new byte[] { (byte)1, (byte)2, (byte)3, (byte)4 }); + IToTestImports.ListParam((new byte[] { (byte)1, (byte)2, (byte)3, (byte)4 }).AsSpan()); + IToTestImports.ListParam((new byte[] { (byte)1, (byte)2, (byte)3, (byte)4 }).AsMemory()); + IToTestImports.ListParam2("foo"); + IToTestImports.ListParam3(new List() { "foo", "bar", "baz" }); - ToTestInterop.ListParam4(new List>() { + IToTestImports.ListParam4(new List>() { new List() { "foo", "bar" @@ -47,10 +45,10 @@ public static void Main(string[] args) { randomStrings.Add(Guid.NewGuid().ToString()); } - ToTestInterop.ListParamLarge(randomStrings); + IToTestImports.ListParamLarge(randomStrings); { - byte[] result = ToTestInterop.ListResult(); + byte[] result = IToTestImports.ListResult(); Debug.Assert(result.Length == 5); Debug.Assert(result[0] == (byte)1); Debug.Assert(result[1] == (byte)2); @@ -60,13 +58,13 @@ public static void Main(string[] args) } { - string result = ToTestInterop.ListResult2(); + string result = IToTestImports.ListResult2(); Console.WriteLine(result); Debug.Assert(result == "hello!"); } { - List result = ToTestInterop.ListResult3(); + List result = IToTestImports.ListResult3(); Debug.Assert(result.Count() == 2); Console.WriteLine(result[0]); Console.WriteLine(result[1]); @@ -77,15 +75,15 @@ public static void Main(string[] args) string[] strings = { "x", "", "hello", "hello ⚑ world" }; foreach (string s in strings) { - string result = ToTestInterop.StringRoundtrip(s); + string result = IToTestImports.StringRoundtrip(s); Debug.Assert(result == s); byte[] bytes = Encoding.UTF8.GetBytes(s); - Debug.Assert(bytes.SequenceEqual(ToTestInterop.ListRoundtrip(bytes))); + Debug.Assert(bytes.SequenceEqual(IToTestImports.ListRoundtrip(bytes))); } { - var (u, s) = ToTestInterop.ListMinmax8( + var (u, s) = IToTestImports.ListMinmax8( new byte[] { byte.MinValue, byte.MaxValue }, new sbyte[] { sbyte.MinValue, sbyte.MaxValue } ); @@ -95,7 +93,7 @@ public static void Main(string[] args) } { - var (u, s) = ToTestInterop.ListMinmax16( + var (u, s) = IToTestImports.ListMinmax16( new ushort[] { ushort.MinValue, ushort.MaxValue }, new short[] { short.MinValue, short.MaxValue } ); @@ -113,7 +111,7 @@ public static void Main(string[] args) } { - var (u, s) = ToTestInterop.ListMinmax32( + var (u, s) = IToTestImports.ListMinmax32( new uint[] { uint.MinValue, uint.MaxValue }, new int[] { int.MinValue, int.MaxValue } ); @@ -123,7 +121,7 @@ public static void Main(string[] args) } { - var (u, s) = ToTestInterop.ListMinmax64( + var (u, s) = IToTestImports.ListMinmax64( new ulong[] { ulong.MinValue, ulong.MaxValue }, new long[] { long.MinValue, long.MaxValue } ); @@ -134,7 +132,7 @@ public static void Main(string[] args) } { - var (u, s) = ToTestInterop.ListMinmaxFloat( + var (u, s) = IToTestImports.ListMinmaxFloat( new float[] { float.MinValue, float.MaxValue, diff --git a/tests/runtime/lists/test.cs b/tests/runtime/lists/test.cs index 1d2e1cbf6..77ff16410 100644 --- a/tests/runtime/lists/test.cs +++ b/tests/runtime/lists/test.cs @@ -3,9 +3,9 @@ using System.Diagnostics; using System.Text; -namespace TestWorld.wit.exports.test.lists +namespace TestWorld.wit.Exports.test.lists { - public class ToTestImpl : ITestWorld + public class ToTestExportsImpl : IToTestExports { public static uint AllocatedBytes() diff --git a/tests/runtime/many-arguments/runner.cs b/tests/runtime/many-arguments/runner.cs index 9d601bf41..34f07ca5e 100644 --- a/tests/runtime/many-arguments/runner.cs +++ b/tests/runtime/many-arguments/runner.cs @@ -1,13 +1,13 @@ using System; using System.Runtime.InteropServices; using System.Diagnostics; -using RunnerWorld.wit.imports.test.manyArguments; +using RunnerWorld.wit.Imports.test.manyArguments; public class Program { public static void Main(string[] args) { - ToTestInterop.ManyArguments( + IToTestImports.ManyArguments( 1, 2, 3, diff --git a/tests/runtime/many-arguments/test.cs b/tests/runtime/many-arguments/test.cs index 3bef9fd1a..04a4a2e70 100644 --- a/tests/runtime/many-arguments/test.cs +++ b/tests/runtime/many-arguments/test.cs @@ -1,9 +1,9 @@ using System.Diagnostics; -using TestWorld.wit.exports.test.manyArguments; +using TestWorld.wit.Exports.test.manyArguments; namespace TestWorld; -public class ToTestImpl : ITestWorld +public class ToTestExportsImpl : IToTestExports { public static void ManyArguments( ulong a1, diff --git a/tests/runtime/numbers/runner.cs b/tests/runtime/numbers/runner.cs index 96091735c..060a3baf4 100644 --- a/tests/runtime/numbers/runner.cs +++ b/tests/runtime/numbers/runner.cs @@ -1,62 +1,58 @@ using System; using System.Runtime.InteropServices; using System.Diagnostics; -using RunnerWorld.wit.imports.test.numbers; +using RunnerWorld.wit.Imports.test.numbers; public class Program { public static void Main(string[] args) { - Debug.Assert(NumbersInterop.RoundtripU8(1) == 1); - Debug.Assert(NumbersInterop.RoundtripU8(0) == 0); - Debug.Assert(NumbersInterop.RoundtripU8(Byte.MaxValue) == Byte.MaxValue); - - Debug.Assert(NumbersInterop.RoundtripS8(1) == 1); - Debug.Assert(NumbersInterop.RoundtripS8(SByte.MinValue) == SByte.MinValue); - Debug.Assert(NumbersInterop.RoundtripS8(SByte.MaxValue) == SByte.MaxValue); - - Debug.Assert(NumbersInterop.RoundtripU16(1) == 1); - Debug.Assert(NumbersInterop.RoundtripU16(0) == 0); - Debug.Assert(NumbersInterop.RoundtripU16(UInt16.MaxValue) == UInt16.MaxValue); - - Debug.Assert(NumbersInterop.RoundtripS16(1) == 1); - Debug.Assert(NumbersInterop.RoundtripS16(Int16.MinValue) == Int16.MinValue); - Debug.Assert(NumbersInterop.RoundtripS16(Int16.MaxValue) == Int16.MaxValue); - - Debug.Assert(NumbersInterop.RoundtripU32(1) == 1); - Debug.Assert(NumbersInterop.RoundtripU32(0) == 0); - Debug.Assert(NumbersInterop.RoundtripU32(UInt32.MaxValue) == UInt32.MaxValue); - - Debug.Assert(NumbersInterop.RoundtripS32(1) == 1); - Debug.Assert(NumbersInterop.RoundtripS32(Int32.MinValue) == Int32.MinValue); - Debug.Assert(NumbersInterop.RoundtripS32(Int32.MaxValue) == Int32.MaxValue); - - Debug.Assert(NumbersInterop.RoundtripU64(1) == 1); - Debug.Assert(NumbersInterop.RoundtripU64(0) == 0); - Debug.Assert(NumbersInterop.RoundtripU64(UInt64.MaxValue) == UInt64.MaxValue); - - Debug.Assert(NumbersInterop.RoundtripS64(1) == 1); - Debug.Assert(NumbersInterop.RoundtripS64(Int64.MinValue) == Int64.MinValue); - Debug.Assert(NumbersInterop.RoundtripS64(Int64.MaxValue) == Int64.MaxValue); - - Debug.Assert(NumbersInterop.RoundtripF32(1.0f) == 1.0f); - Debug.Assert(NumbersInterop.RoundtripF32(Single.PositiveInfinity) == Single.PositiveInfinity); - Debug.Assert(NumbersInterop.RoundtripF32(Single.NegativeInfinity) == Single.NegativeInfinity); - Debug.Assert(float.IsNaN(NumbersInterop.RoundtripF32(Single.NaN))); - - Debug.Assert(NumbersInterop.RoundtripF64(1.0) == 1.0); - Debug.Assert(NumbersInterop.RoundtripF64(Double.PositiveInfinity) == Double.PositiveInfinity); - Debug.Assert(NumbersInterop.RoundtripF64(Double.NegativeInfinity) == Double.NegativeInfinity); - Debug.Assert(double.IsNaN(NumbersInterop.RoundtripF64(Double.NaN))); - - Debug.Assert(NumbersInterop.RoundtripChar('a') == 'a'); - Debug.Assert(NumbersInterop.RoundtripChar(' ') == ' '); - Debug.Assert(Char.ConvertFromUtf32((int)NumbersInterop.RoundtripChar((uint)Char.ConvertToUtf32("🚩", 0))) == + Debug.Assert(INumbersImports.RoundtripU8(1) == 1); + Debug.Assert(INumbersImports.RoundtripU8(0) == 0); + Debug.Assert(INumbersImports.RoundtripU8(Byte.MaxValue) == Byte.MaxValue); + + Debug.Assert(INumbersImports.RoundtripS8(1) == 1); + Debug.Assert(INumbersImports.RoundtripS8(SByte.MinValue) == SByte.MinValue); + Debug.Assert(INumbersImports.RoundtripS8(SByte.MaxValue) == SByte.MaxValue); + + Debug.Assert(INumbersImports.RoundtripU16(1) == 1); + Debug.Assert(INumbersImports.RoundtripU16(0) == 0); + Debug.Assert(INumbersImports.RoundtripU16(UInt16.MaxValue) == UInt16.MaxValue); + + Debug.Assert(INumbersImports.RoundtripS16(1) == 1); + Debug.Assert(INumbersImports.RoundtripS16(Int16.MinValue) == Int16.MinValue); + Debug.Assert(INumbersImports.RoundtripS16(Int16.MaxValue) == Int16.MaxValue); + Debug.Assert(INumbersImports.RoundtripU32(1) == 1); + Debug.Assert(INumbersImports.RoundtripU32(0) == 0); + Debug.Assert(INumbersImports.RoundtripU32(UInt32.MaxValue) == UInt32.MaxValue); + + Debug.Assert(INumbersImports.RoundtripS32(1) == 1); + Debug.Assert(INumbersImports.RoundtripS32(Int32.MinValue) == Int32.MinValue); + Debug.Assert(INumbersImports.RoundtripS32(Int32.MaxValue) == Int32.MaxValue); + + Debug.Assert(INumbersImports.RoundtripU64(1) == 1); + Debug.Assert(INumbersImports.RoundtripU64(0) == 0); + Debug.Assert(INumbersImports.RoundtripU64(UInt64.MaxValue) == UInt64.MaxValue); + + Debug.Assert(INumbersImports.RoundtripS64(1) == 1); + Debug.Assert(INumbersImports.RoundtripS64(Int64.MinValue) == Int64.MinValue); + Debug.Assert(INumbersImports.RoundtripS64(Int64.MaxValue) == Int64.MaxValue); + Debug.Assert(INumbersImports.RoundtripF32(1.0f) == 1.0f); + Debug.Assert(INumbersImports.RoundtripF32(Single.PositiveInfinity) == Single.PositiveInfinity); + Debug.Assert(INumbersImports.RoundtripF32(Single.NegativeInfinity) == Single.NegativeInfinity); + Debug.Assert(float.IsNaN(INumbersImports.RoundtripF32(Single.NaN))); + Debug.Assert(INumbersImports.RoundtripF64(1.0) == 1.0); + Debug.Assert(INumbersImports.RoundtripF64(Double.PositiveInfinity) == Double.PositiveInfinity); + Debug.Assert(INumbersImports.RoundtripF64(Double.NegativeInfinity) == Double.NegativeInfinity); + Debug.Assert(double.IsNaN(INumbersImports.RoundtripF64(Double.NaN))); + Debug.Assert(INumbersImports.RoundtripChar('a') == 'a'); + Debug.Assert(INumbersImports.RoundtripChar(' ') == ' '); + Debug.Assert(Char.ConvertFromUtf32((int)INumbersImports.RoundtripChar((uint)Char.ConvertToUtf32("🚩", 0))) == "🚩"); // This is 2 chars long as it contains a surrogate pair - NumbersInterop.SetScalar(2); - Debug.Assert(NumbersInterop.GetScalar() == 2); - NumbersInterop.SetScalar(4); - Debug.Assert(NumbersInterop.GetScalar() == 4); + INumbersImports.SetScalar(2); + Debug.Assert(INumbersImports.GetScalar() == 2); + INumbersImports.SetScalar(4); + Debug.Assert(INumbersImports.GetScalar() == 4); } } diff --git a/tests/runtime/numbers/test.cs b/tests/runtime/numbers/test.cs index 149dd35e0..16ba0e44e 100644 --- a/tests/runtime/numbers/test.cs +++ b/tests/runtime/numbers/test.cs @@ -1,10 +1,8 @@ -using System; -using System.Runtime.InteropServices; -using System.Diagnostics; +using TestWorld.wit.Exports.test.numbers; -namespace TestWorld.wit.exports.test.numbers +namespace TestWorld.wit.Exports.test.numbers { - public class NumbersImpl : ITestWorld + public class NumbersExportsImpl : INumbersExports { static uint SCALAR = 0; diff --git a/tests/runtime/options/runner.cs b/tests/runtime/options/runner.cs index 8f145e343..313012af9 100644 --- a/tests/runtime/options/runner.cs +++ b/tests/runtime/options/runner.cs @@ -1,5 +1,5 @@ using System.Diagnostics; -using RunnerWorld.wit.imports.test.options; +using RunnerWorld.wit.Imports.test.options; namespace RunnerWorld { @@ -7,14 +7,14 @@ public class Program { public static void Main() { - ToTestInterop.OptionNoneParam(null); - ToTestInterop.OptionSomeParam("foo"); - Debug.Assert(ToTestInterop.OptionNoneResult() == null); - Debug.Assert(ToTestInterop.OptionSomeResult() == "foo"); - Debug.Assert(ToTestInterop.OptionRoundtrip("foo") == "foo"); - Debug.Assert(ToTestInterop.DoubleOptionRoundtrip(new Option(42)).Value == 42); - Debug.Assert(ToTestInterop.DoubleOptionRoundtrip(new Option(null)).Value == null); - Debug.Assert(!ToTestInterop.DoubleOptionRoundtrip(Option.None).HasValue); + IToTestImports.OptionNoneParam(null); + IToTestImports.OptionSomeParam("foo"); + Debug.Assert(IToTestImports.OptionNoneResult() == null); + Debug.Assert(IToTestImports.OptionSomeResult() == "foo"); + Debug.Assert(IToTestImports.OptionRoundtrip("foo") == "foo"); + Debug.Assert(IToTestImports.DoubleOptionRoundtrip(new Option(42)).Value == 42); + Debug.Assert(IToTestImports.DoubleOptionRoundtrip(new Option(null)).Value == null); + Debug.Assert(!IToTestImports.DoubleOptionRoundtrip(Option.None).HasValue); } } } diff --git a/tests/runtime/options/test.cs b/tests/runtime/options/test.cs index 38380c82c..9668603f6 100644 --- a/tests/runtime/options/test.cs +++ b/tests/runtime/options/test.cs @@ -1,8 +1,8 @@ using System.Diagnostics; -namespace TestWorld.wit.exports.test.options +namespace TestWorld.wit.Exports.test.options { - public class ToTestImpl : IToTest + public class ToTestExportsImpl : IToTestExports { public static void OptionNoneParam(string? a) { diff --git a/tests/runtime/records/runner.cs b/tests/runtime/records/runner.cs index 044690bf5..2c43448d3 100644 --- a/tests/runtime/records/runner.cs +++ b/tests/runtime/records/runner.cs @@ -1,58 +1,58 @@ using System; using System.Runtime.InteropServices; using System.Diagnostics; -using RunnerWorld.wit.imports.test.records; +using RunnerWorld.wit.Imports.test.records; using RunnerWorld; public class Program { public static void Main() { { - var results = ToTestInterop.MultipleResults(); + var results = IToTestImports.MultipleResults(); Debug.Assert(results.Item1 == 4); Debug.Assert(results.Item2 == 5); } (byte, uint) input = (1, 2); - (uint, byte) output = ToTestInterop.SwapTuple(input); + (uint, byte) output = IToTestImports.SwapTuple(input); Debug.Assert(output.Item1 == 2); Debug.Assert(output.Item2 == 1); - Debug.Assert(ToTestInterop.RoundtripFlags1(IToTest.F1.A) == IToTest.F1.A); - Debug.Assert(ToTestInterop.RoundtripFlags1(0) == 0); - Debug.Assert(ToTestInterop.RoundtripFlags1(IToTest.F1.B) == IToTest.F1.B); - Debug.Assert(ToTestInterop.RoundtripFlags1(IToTest.F1.A | IToTest.F1.B) == (IToTest.F1.A | IToTest.F1.B)); + Debug.Assert(IToTestImports.RoundtripFlags1(IToTestImports.F1.A) == IToTestImports.F1.A); + Debug.Assert(IToTestImports.RoundtripFlags1(0) == 0); + Debug.Assert(IToTestImports.RoundtripFlags1(IToTestImports.F1.B) == IToTestImports.F1.B); + Debug.Assert(IToTestImports.RoundtripFlags1(IToTestImports.F1.A | IToTestImports.F1.B) == (IToTestImports.F1.A | IToTestImports.F1.B)); - Debug.Assert(ToTestInterop.RoundtripFlags2(IToTest.F2.C) == IToTest.F2.C); - Debug.Assert(ToTestInterop.RoundtripFlags2(0) == 0); - Debug.Assert(ToTestInterop.RoundtripFlags2(IToTest.F2.D) == IToTest.F2.D); - Debug.Assert(ToTestInterop.RoundtripFlags2(IToTest.F2.C | IToTest.F2.E) == (IToTest.F2.C | IToTest.F2.E)); + Debug.Assert(IToTestImports.RoundtripFlags2(IToTestImports.F2.C) == IToTestImports.F2.C); + Debug.Assert(IToTestImports.RoundtripFlags2(0) == 0); + Debug.Assert(IToTestImports.RoundtripFlags2(IToTestImports.F2.D) == IToTestImports.F2.D); + Debug.Assert(IToTestImports.RoundtripFlags2(IToTestImports.F2.C | IToTestImports.F2.E) == (IToTestImports.F2.C | IToTestImports.F2.E)); { - var result = ToTestInterop.RoundtripFlags3(IToTest.Flag8.B0, IToTest.Flag16.B1, - IToTest.Flag32.B2); - Debug.Assert(result.Item1 == IToTest.Flag8.B0); - Debug.Assert(result.Item2 == IToTest.Flag16.B1); - Debug.Assert(result.Item3 == IToTest.Flag32.B2); + var result = IToTestImports.RoundtripFlags3(IToTestImports.Flag8.B0, IToTestImports.Flag16.B1, + IToTestImports.Flag32.B2); + Debug.Assert(result.Item1 == IToTestImports.Flag8.B0); + Debug.Assert(result.Item2 == IToTestImports.Flag16.B1); + Debug.Assert(result.Item3 == IToTestImports.Flag32.B2); } { - IToTest.R1 inputRecord = new(8, 0); - var result = ToTestInterop.RoundtripRecord1(inputRecord); + IToTestImports.R1 inputRecord = new(8, 0); + var result = IToTestImports.RoundtripRecord1(inputRecord); Debug.Assert(result.a == 8); Debug.Assert(result.b == 0); } { - IToTest.R1 inputRecord = new(0, IToTest.F1.A | IToTest.F1.B); + IToTestImports.R1 inputRecord = new(0, IToTestImports.F1.A | IToTestImports.F1.B); - var result = ToTestInterop.RoundtripRecord1(inputRecord); + var result = IToTestImports.RoundtripRecord1(inputRecord); Debug.Assert(result.a == 0); - Debug.Assert(result.b == (IToTest.F1.A | IToTest.F1.B)); + Debug.Assert(result.b == (IToTestImports.F1.A | IToTestImports.F1.B)); } { - var result = ToTestInterop.Tuple1(1); + var result = IToTestImports.Tuple1(1); Debug.Assert(result == 1); } } diff --git a/tests/runtime/records/test.cs b/tests/runtime/records/test.cs index 4e0b8310b..f6ae2f23b 100644 --- a/tests/runtime/records/test.cs +++ b/tests/runtime/records/test.cs @@ -1,6 +1,6 @@ -namespace TestWorld.wit.exports.test.records +namespace TestWorld.wit.Exports.test.records { - public class ToTestImpl : ITestWorld + public class ToTestExportsImpl : IToTestExports { public static (byte, ushort) MultipleResults() { @@ -12,30 +12,30 @@ public static (uint, byte) SwapTuple((byte, uint) a) return (a.Item2, a.Item1); } - public static IToTest.F1 RoundtripFlags1( - IToTest.F1 a) + public static IToTestExports.F1 RoundtripFlags1( + IToTestExports.F1 a) { return a; } - public static IToTest.F2 RoundtripFlags2( - IToTest.F2 a) + public static IToTestExports.F2 RoundtripFlags2( + IToTestExports.F2 a) { return a; } - public static (IToTest.Flag8, - IToTest.Flag16, - IToTest.Flag32) RoundtripFlags3( - IToTest.Flag8 a, - IToTest.Flag16 b, - IToTest.Flag32 c) + public static (IToTestExports.Flag8, + IToTestExports.Flag16, + IToTestExports.Flag32) RoundtripFlags3( + IToTestExports.Flag8 a, + IToTestExports.Flag16 b, + IToTestExports.Flag32 c) { return (a, b, c); } - public static IToTest.R1 RoundtripRecord1( - IToTest.R1 a) + public static IToTestExports.R1 RoundtripRecord1( + IToTestExports.R1 a) { return a; } diff --git a/tests/runtime/resource-borrow/runner.cs b/tests/runtime/resource-borrow/runner.cs index 5f4b6c13f..b3c77807c 100644 --- a/tests/runtime/resource-borrow/runner.cs +++ b/tests/runtime/resource-borrow/runner.cs @@ -1,9 +1,9 @@ -using RunnerWorld.wit.imports.test.resourceBorrow; +using RunnerWorld.wit.Imports.test.resourceBorrow; using System.Diagnostics; public class RunnerWorldImpl { public static void Main() { - uint ret = ToTestInterop.Foo(new IToTest.Thing(42)); + uint ret = IToTestImports.Foo(new IToTestImports.Thing(42)); Debug.Assert(ret == 42 + 1 + 2); } } diff --git a/tests/runtime/resource-borrow/test.cs b/tests/runtime/resource-borrow/test.cs index f5feab341..0acf0be34 100644 --- a/tests/runtime/resource-borrow/test.cs +++ b/tests/runtime/resource-borrow/test.cs @@ -1,7 +1,7 @@ -namespace TestWorld.wit.exports.test.resourceBorrow +namespace TestWorld.wit.Exports.test.resourceBorrow { - public class ToTestImpl : IToTest { - public class Thing : IToTest.Thing, IToTest.IThing { + public class ToTestExportsImpl : IToTestExports { + public class Thing : IToTestExports.Thing, IToTestExports.IThing { public uint val; public Thing(uint v) { @@ -9,7 +9,7 @@ public Thing(uint v) { } } - public static uint Foo(IToTest.Thing v) { + public static uint Foo(IToTestExports.Thing v) { return ((Thing) v).val + 2; } } diff --git a/tests/runtime/resource-import-and-export/intermediate.cs b/tests/runtime/resource-import-and-export/intermediate.cs index d2094cc2a..67d139721 100644 --- a/tests/runtime/resource-import-and-export/intermediate.cs +++ b/tests/runtime/resource-import-and-export/intermediate.cs @@ -1,10 +1,10 @@ -using Import = IntermediateWorld.wit.imports.test.resourceImportAndExport.ITest; -using Host = IntermediateWorld.wit.imports.test.resourceImportAndExport.TestInterop; +using IntermediateWorld.wit.Imports.test.resourceImportAndExport; +using Import = IntermediateWorld.wit.Imports.test.resourceImportAndExport.ITestImports; -namespace IntermediateWorld.wit.exports.test.resourceImportAndExport +namespace IntermediateWorld.wit.Exports.test.resourceImportAndExport { - public class TestImpl : ITest { - public class Thing : ITest.Thing, ITest.IThing { + public class TestExportsImpl : ITestExports { + public class Thing : ITestExports.Thing, ITestExports.IThing { public Import.Thing val; public Thing(uint v) { @@ -19,7 +19,7 @@ public void Bar(uint v) { this.val.Bar(v + 3); } - public static ITest.Thing Baz(ITest.Thing a, ITest.Thing b) { + public static ITestExports.Thing Baz(ITestExports.Thing a, ITestExports.Thing b) { return new Thing(Import.Thing.Baz(((Thing) a).val, ((Thing) b).val).Foo() + 4); } } @@ -27,9 +27,10 @@ public static ITest.Thing Baz(ITest.Thing a, ITest.Thing b) { } namespace IntermediateWorld { - public class IntermediateWorldImpl : IIntermediateWorld { + public class IntermediateWorldExportsImpl : ITestImports + { public static Import.Thing ToplevelExport(Import.Thing things) { - return exports.IntermediateWorld.ToplevelImport(things); + return IntermediateWorld.IIntermediateWorldImports.ToplevelImport(things); } } } diff --git a/tests/runtime/resource_aggregates/runner.cs b/tests/runtime/resource_aggregates/runner.cs index 066d9681b..9d495e082 100644 --- a/tests/runtime/resource_aggregates/runner.cs +++ b/tests/runtime/resource_aggregates/runner.cs @@ -1,33 +1,33 @@ using System; using System.Runtime.InteropServices; using System.Diagnostics; -using RunnerWorld.wit.imports.test.resourceAggregates; +using RunnerWorld.wit.Imports.test.resourceAggregates; using RunnerWorld; public class Program { public static void Main() { - var il1 = new List(); - il1.Add(new IToTest.Thing(9)); - il1.Add(new IToTest.Thing(10)); - var il2 = new List(); - il2.Add(new IToTest.Thing(11)); - il2.Add(new IToTest.Thing(12)); + var il1 = new List(); + il1.Add(new IToTestImports.Thing(9)); + il1.Add(new IToTestImports.Thing(10)); + var il2 = new List(); + il2.Add(new IToTestImports.Thing(11)); + il2.Add(new IToTestImports.Thing(12)); - uint res = ToTestInterop.Foo( - new IToTest.R1(new IToTest.Thing(0)), - new IToTest.R2(new IToTest.Thing(1)), - new IToTest.R3(new IToTest.Thing(2), new IToTest.Thing(3)), - (new IToTest.Thing(4), new IToTest.R1(new IToTest.Thing(5))), - new IToTest.Thing(6), - IToTest.V1.Thing(new IToTest.Thing(7)), - IToTest.V2.Thing(new IToTest.Thing(8)), + uint res = IToTestImports.Foo( + new IToTestImports.R1(new IToTestImports.Thing(0)), + new IToTestImports.R2(new IToTestImports.Thing(1)), + new IToTestImports.R3(new IToTestImports.Thing(2), new IToTestImports.Thing(3)), + (new IToTestImports.Thing(4), new IToTestImports.R1(new IToTestImports.Thing(5))), + new IToTestImports.Thing(6), + IToTestImports.V1.Thing(new IToTestImports.Thing(7)), + IToTestImports.V2.Thing(new IToTestImports.Thing(8)), il1, il2, - new IToTest.Thing(13), - new IToTest.Thing(14), - Result.Ok(new IToTest.Thing(15)), - Result.Ok(new IToTest.Thing(16)) + new IToTestImports.Thing(13), + new IToTestImports.Thing(14), + Result.Ok(new IToTestImports .Thing(15)), + Result.Ok(new IToTestImports.Thing(16)) ); Debug.Assert(res == 156); } diff --git a/tests/runtime/resource_aggregates/test.cs b/tests/runtime/resource_aggregates/test.cs index 4b0d01afa..6f769f8d6 100644 --- a/tests/runtime/resource_aggregates/test.cs +++ b/tests/runtime/resource_aggregates/test.cs @@ -1,7 +1,7 @@ -namespace TestWorld.wit.exports.test.resourceAggregates +namespace TestWorld.wit.Exports.test.resourceAggregates { - public class ToTestImpl : IToTest { - public class Thing : IToTest.Thing, IToTest.IThing { + public class ToTestExportsImpl : IToTestExports { + public class Thing : IToTestExports.Thing, IToTestExports.IThing { public uint val; public Thing(uint v) { @@ -10,19 +10,19 @@ public Thing(uint v) { } public static uint Foo( - IToTest.R1 r1, - IToTest.R2 r2, - IToTest.R3 r3, - (IToTest.Thing, IToTest.R1) t1, - IToTest.Thing t2, - IToTest.V1 v1, - IToTest.V2 v2, - List l1, - List l2, - IToTest.Thing? o1, - IToTest.Thing? o2, - Result result1, - Result result2 + IToTestExports.R1 r1, + IToTestExports.R2 r2, + IToTestExports.R3 r3, + (IToTestExports.Thing, IToTestExports.R1) t1, + IToTestExports.Thing t2, + IToTestExports.V1 v1, + IToTestExports.V2 v2, + List l1, + List l2, + IToTestExports.Thing? o1, + IToTestExports.Thing? o2, + Result result1, + Result result2 ) { uint sumIl1 = 0; diff --git a/tests/runtime/resource_alias/test.cs b/tests/runtime/resource_alias/test.cs index 41421e9c1..79601f847 100644 --- a/tests/runtime/resource_alias/test.cs +++ b/tests/runtime/resource_alias/test.cs @@ -1,7 +1,7 @@ -namespace TestWorld.wit.exports.test.resourceAlias +namespace TestWorld.wit.Exports.test.resourceAlias { - public class E1Impl : IE1 { - public class X : IE1.X, IE1.IX { + public class E1ExportsImpl : IE1Exports { + public class X : IE1Exports.X, IE1Exports.IX { public uint val; public X(uint v) { @@ -9,14 +9,14 @@ public X(uint v) { } } - public static List A(IE1.Foo f) { - return new List() { f.x }; + public static List A(IE1Exports.Foo f) { + return new List() { f.x }; } } - public class E2Impl : IE2 { - public static List A(IE2.Foo f, IE1.Foo g, IE1.X h) { - return new List() { f.x, g.x }; + public class E2ExportsImpl : IE2Exports { + public static List A(IE2Exports.Foo f, IE1Exports.Foo g, IE1Exports.X h) { + return new List() { f.x, g.x }; } } } diff --git a/tests/runtime/resource_alias_redux/_test_disabled.cs b/tests/runtime/resource_alias_redux/_test_disabled.cs deleted file mode 100644 index 8a60a05b5..000000000 --- a/tests/runtime/resource_alias_redux/_test_disabled.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace TestWorld.wit.exports.test.resourceAliasRedux -{ - public class ResourceAlias1Impl : IResourceAlias1 { - public class Thing : IResourceAlias1.Thing, IResourceAlias1.IThing { - public string val; - - public Thing(string v) { - this.val = v + " GuestThing"; - } - - public string Get() { - return this.val + " GuestThing.get"; - } - } - - public static List A(IResourceAlias1.Foo f) { - var newList = new List(); - newList.Add(f.thing); - return newList; - } - } - - public class ResourceAlias2Impl : IResourceAlias2 { - public static List B(IResourceAlias2.Foo f, IResourceAlias1.Foo g) { - var newList = new List(); - newList.Add(f.thing); - newList.Add(g.thing); - return newList; - } - } -} - -namespace TestWorld { - using TestWorld.wit.exports.test.resourceAliasRedux; - - public class TestWorldImpl : ITestWorld { - public static List Test(List things) { - return things; - } - } -} diff --git a/tests/runtime/resource_alias_redux/runner.cs b/tests/runtime/resource_alias_redux/runner.cs index ee18ac7ab..b511fd364 100644 --- a/tests/runtime/resource_alias_redux/runner.cs +++ b/tests/runtime/resource_alias_redux/runner.cs @@ -1,25 +1,25 @@ using System; using System.Runtime.InteropServices; using System.Diagnostics; -using RunnerWorld.wit.imports.test.resourceAliasRedux; -using RunnerWorld.wit.imports; +using RunnerWorld.wit.Imports.test.resourceAliasRedux; +using RunnerWorld.wit.Imports; using System.Text; public class Program { public static void Main() { - IResourceAlias1.Thing thing1 = new IResourceAlias1.Thing("Ni Hao"); - List myList = new List(); + IResourceAlias1Imports.Thing thing1 = new IResourceAlias1Imports.Thing("Ni Hao"); + List myList = new List(); myList.Add(thing1); - List ret = TheTestInterop.Test(myList); + List ret = ITheTestImports.Test(myList); Debug.Assert(ret[0].Get() == "Ni Hao GuestThing GuestThing.get"); - ret = ResourceAlias1Interop.A( - new IResourceAlias1.Foo(new IResourceAlias1.Thing("Ciao"))); + ret = IResourceAlias1Imports.A( + new IResourceAlias1Imports.Foo(new IResourceAlias1Imports.Thing("Ciao"))); Debug.Assert(ret[0].Get() == "Ciao GuestThing GuestThing.get"); - ret = ResourceAlias2Interop.B( - new IResourceAlias2.Foo(new IResourceAlias1.Thing("Ciao")), - new IResourceAlias1.Foo(new IResourceAlias1.Thing("Aloha")) + ret = IResourceAlias2Imports.B( + new IResourceAlias2Imports.Foo(new IResourceAlias1Imports.Thing("Ciao")), + new IResourceAlias1Imports.Foo(new IResourceAlias1Imports.Thing("Aloha")) ); Debug.Assert(ret[0].Get() == "Ciao GuestThing GuestThing.get"); Debug.Assert(ret[1].Get() == "Aloha GuestThing GuestThing.get"); diff --git a/tests/runtime/resource_alias_redux/test.cs b/tests/runtime/resource_alias_redux/test.cs new file mode 100644 index 000000000..fff69a4d4 --- /dev/null +++ b/tests/runtime/resource_alias_redux/test.cs @@ -0,0 +1,45 @@ +using TestWorld.wit.Exports.test.resourceAliasRedux; + +namespace TestWorld.wit.Exports +{ + public class ResourceAlias1ExportsImpl : IResourceAlias1Exports { + public class Thing : IResourceAlias1Exports.Thing, IResourceAlias1Exports.IThing { + public string val; + + public Thing(string v) { + this.val = v + " GuestThing"; + } + + public string Get() { + return this.val + " GuestThing.get"; + } + } + + public static List A(IResourceAlias1Exports.Foo f) { + var newList = new List(); + newList.Add(f.thing); + return newList; + } + } + + public class ResourceAlias2ExportsImpl : IResourceAlias2Exports { + public static List B(IResourceAlias2Exports.Foo f, IResourceAlias1Exports.Foo g) { + var newList = new List(); + newList.Add(f.thing); + newList.Add(g.thing); + return newList; + } + } +} + +namespace TestWorld { + using TestWorld.wit.Exports.test.resourceAliasRedux; + using TestWorld.wit.Exports; + + public class TheTestExportsImpl : ITheTestExports + { + public static List Test(List things) { + return things; + } + } +} diff --git a/tests/runtime/resource_borrow_in_record/runner.cs b/tests/runtime/resource_borrow_in_record/runner.cs index 80e4780ec..4c479d5ff 100644 --- a/tests/runtime/resource_borrow_in_record/runner.cs +++ b/tests/runtime/resource_borrow_in_record/runner.cs @@ -1,18 +1,18 @@ using System; using System.Runtime.InteropServices; using System.Diagnostics; -using RunnerWorld.wit.imports.test.resourceBorrowInRecord; +using RunnerWorld.wit.Imports.test.resourceBorrowInRecord; using System.Text; public class Program { public static void Main() { - IToTest.Thing thing1 = new IToTest.Thing("Bonjour"); - IToTest.Thing thing2 = new IToTest.Thing("mon cher"); + IToTestImports.Thing thing1 = new IToTestImports.Thing("Bonjour"); + IToTestImports.Thing thing2 = new IToTestImports.Thing("mon cher"); - List myList = new List(); - myList.Add(new IToTest.Foo(thing1)); - myList.Add(new IToTest.Foo(thing2)); - List ret = ToTestInterop.Test(myList); + List myList = new List(); + myList.Add(new IToTestImports.Foo(thing1)); + myList.Add(new IToTestImports.Foo(thing2)); + List ret = IToTestImports.Test(myList); Debug.Assert(ret[0].Get() == "Bonjour new test get"); Debug.Assert(ret[1].Get() == "mon cher new test get"); diff --git a/tests/runtime/resource_borrow_in_record/test.cs b/tests/runtime/resource_borrow_in_record/test.cs index d0f417230..c78183707 100644 --- a/tests/runtime/resource_borrow_in_record/test.cs +++ b/tests/runtime/resource_borrow_in_record/test.cs @@ -1,7 +1,7 @@ -using TestWorld.wit.exports.test.resourceBorrowInRecord; +using TestWorld.wit.Exports.test.resourceBorrowInRecord; -public class ToTestImpl : IToTest { - public class Thing : IToTest.Thing, IToTest.IThing { +public class ToTestExportsImpl : IToTestExports { + public class Thing : IToTestExports.Thing, IToTestExports.IThing { public string val; public Thing(string v) { @@ -17,8 +17,8 @@ public string Get() { } } - public static List Test(List v) { - var myResult = new List(); + public static List Test(List v) { + var myResult = new List(); foreach (var foo in v) { myResult.Add(new Thing((Thing) foo.thing)); diff --git a/tests/runtime/resource_floats/intermediate.cs b/tests/runtime/resource_floats/intermediate.cs index f1b3190f0..d135c7da2 100644 --- a/tests/runtime/resource_floats/intermediate.cs +++ b/tests/runtime/resource_floats/intermediate.cs @@ -1,29 +1,29 @@ -using Import1 = IntermediateWorld.wit.imports.IImports; -using Import2 = IntermediateWorld.wit.imports.test.resourceFloats.ITest; +using Import1 = IntermediateWorld.wit.Imports.IImportsImports; +using Import2 = IntermediateWorld.wit.Imports.test.resourceFloats.ITestImports; -namespace IntermediateWorld.wit.exports +namespace IntermediateWorld.wit.Exports { - public class ExportsImpl : IExports { - public class Float : IExports.Float, IExports.IFloat { + public class ExportsExportsImpl : IExportsExports { + public class Float : IExportsExports.Float, IExportsExports.IFloat { public Import1.Float val; public Float(double v) { - this.val = new Import1.Float(v + 1.0); + this.val = new Import1.Float(v + 1.0); } public double Get() { - return this.val.Get() + 3.0; + return this.val.Get() + 3.0; } - public static IExports.Float Add(IExports.Float a, double b) { - return new Float(Import1.Float.Add(((Float) a).val, b).Get() + 5.0); + public static IExportsExports.Float Add(IExportsExports.Float a, double b) { + return new Float(Import1.Float.Add(((Float) a).val, b).Get() + 5.0); } } } } namespace IntermediateWorld { - public class IntermediateWorldImpl : IIntermediateWorld { + public class IntermediateWorldExportsImpl : Import2 { public static Import2.Float Add(Import2.Float a, Import2.Float b) { return new Import2.Float(a.Get() + b.Get() + 5.0); } diff --git a/tests/runtime/resource_with_lists/resource-with-lists.cs b/tests/runtime/resource_with_lists/resource-with-lists.cs index 7a9804149..ea334eda3 100644 --- a/tests/runtime/resource_with_lists/resource-with-lists.cs +++ b/tests/runtime/resource_with_lists/resource-with-lists.cs @@ -1,11 +1,10 @@ using System.Text; -using Import = ResourceWithListsWorld.wit.imports.test.resourceWithLists.ITest; -using Host = ResourceWithListsWorld.wit.imports.test.resourceWithLists.TestInterop; +using Import = ResourceWithListsWorld.wit.Imports.test.resourceWithLists.ITestImports; -namespace ResourceWithListsWorld.wit.exports.test.resourceWithLists +namespace ResourceWithListsWorld.wit.Exports.test.resourceWithLists { - public class TestImpl : ITest { - public class Thing : ITest.Thing, ITest.IThing { + public class TestExportsImpl : ITestExports { + public class Thing : ITestExports.Thing, ITestExports.IThing { public Import.Thing val; public Thing(byte[] v) { diff --git a/tests/runtime/resources/resources.cs b/tests/runtime/resources/resources.cs index 7528234ae..ee8255a17 100644 --- a/tests/runtime/resources/resources.cs +++ b/tests/runtime/resources/resources.cs @@ -1,45 +1,45 @@ using System.Diagnostics; using ResourcesWorld; -using ResourcesWorld.wit.imports; +using ResourcesWorld.wit.Imports; -namespace ResourcesWorld.wit.exports +namespace ResourcesWorld.wit.Exports { - public class ExportsImpl : IExports + public class ExportsExportsImpl : IExportsExports { - public static IExports.Z Add(IExports.Z a, IExports.Z b) + public static IExportsExports.Z Add(IExportsExports.Z a, IExportsExports.Z b) { return new Z(((Z) a).val + ((Z) b).val); } - public static void Consume(IExports.X x) + public static void Consume(IExportsExports.X x) { x.Dispose(); } public static void TestImports() { - var y1 = new IImports.Y(10); + var y1 = new IImportsImports.Y(10); Debug.Assert(y1.GetA() == 10); y1.SetA(20); Debug.Assert(y1.GetA() == 20); - var y2 = IImports.Y.Add(y1, 20); + var y2 = IImportsImports.Y.Add(y1, 20); Debug.Assert(y2.GetA() == 40); - var y3 = new IImports.Y(1); - var y4 = new IImports.Y(2); + var y3 = new IImportsImports.Y(1); + var y4 = new IImportsImports.Y(2); Debug.Assert(y3.GetA() == 1); Debug.Assert(y4.GetA() == 2); y3.SetA(10); y4.SetA(20); Debug.Assert(y3.GetA() == 10); Debug.Assert(y4.GetA() == 20); - var y5 = IImports.Y.Add(y3, 20); - var y6 = IImports.Y.Add(y4, 30); + var y5 = IImportsImports.Y.Add(y3, 20); + var y6 = IImportsImports.Y.Add(y4, 30); Debug.Assert(y5.GetA() == 30); Debug.Assert(y6.GetA() == 50); } - public class X : IExports.X, IExports.IX { + public class X : IExportsExports.X, IExportsExports.IX { public int val; public X(int val) { @@ -54,14 +54,14 @@ public int GetA() { return val; } - public static IExports.X Add(IExports.X a, int b) { + public static IExportsExports.X Add(IExportsExports.X a, int b) { var myA = (X) a; myA.SetA(myA.GetA() + b); return myA; } } - public class Z : IExports.Z, IExports.IZ { + public class Z : IExportsExports.Z, IExportsExports.IZ { private static uint numDropped = 0; public int val; @@ -85,7 +85,7 @@ override protected void Dispose(bool disposing) { } } - public class KebabCase : IExports.KebabCase, IExports.IKebabCase { + public class KebabCase : IExportsExports.KebabCase, IExportsExports.IKebabCase { public uint val; public KebabCase(uint val) { @@ -96,7 +96,7 @@ public uint GetA() { return val; } - public static uint TakeOwned(IExports.KebabCase a) { + public static uint TakeOwned(IExportsExports.KebabCase a) { return ((KebabCase) a).val; } } diff --git a/tests/runtime/results/intermediate.cs b/tests/runtime/results/intermediate.cs index 7f09fa335..084e2c25a 100644 --- a/tests/runtime/results/intermediate.cs +++ b/tests/runtime/results/intermediate.cs @@ -1,17 +1,17 @@ -namespace IntermediateWorld.wit.exports.test.results +namespace IntermediateWorld.wit.Exports.test.results { - public class TestImpl : ITest + public class TestExportsImpl : ITestExports { public static float StringError(float a) { - return imports.test.results.TestInterop.StringError(a); + return Imports.test.results.ITestImports.StringError(a); } public static float EnumError(float a) { try { - return imports.test.results.TestInterop.EnumError(a); - } catch (WitException e) { + return Imports.test.results.ITestImports.EnumError(a); + } catch (WitException e) { throw new WitException(e.TypedValue, 0); } } @@ -19,22 +19,22 @@ public static float EnumError(float a) public static float RecordError(float a) { try { - return imports.test.results.TestInterop.RecordError(a); - } catch (WitException e) { - throw new WitException(new ITest.E2(e.TypedValue.line, e.TypedValue.column), 0); + return Imports.test.results.ITestImports.RecordError(a); + } catch (WitException e) { + throw new WitException(new ITestExports.E2(e.TypedValue.line, e.TypedValue.column), 0); } } public static float VariantError(float a) { try { - return imports.test.results.TestInterop.VariantError(a); - } catch (WitException e) - when (e.TypedValue.Tag == imports.test.results.ITest.E3.Tags.E1) { - throw new WitException(ITest.E3.E1((ITest.E)Enum.Parse(typeof(ITest.E), e.TypedValue.AsE1.ToString())), 0); - } catch (WitException e) - when (e.TypedValue.Tag == imports.test.results.ITest.E3.Tags.E2) { - throw new WitException(ITest.E3.E2(new ITest.E2(e.TypedValue.AsE2.line, e.TypedValue.AsE2.column)), 0); + return Imports.test.results.ITestImports.VariantError(a); + } catch (WitException e) + when (e.TypedValue.Tag == Imports.test.results.ITestImports.E3.Tags.E1) { + throw new WitException(ITestExports.E3.E1((ITestExports.E)Enum.Parse(typeof(ITestExports.E), e.TypedValue.AsE1.ToString())), 0); + } catch (WitException e) + when (e.TypedValue.Tag == Imports.test.results.ITestImports.E3.Tags.E2) { + throw new WitException(ITestExports.E3.E2(new ITestExports.E2(e.TypedValue.AsE2.line, e.TypedValue.AsE2.column)), 0); } catch { throw new Exception("unreachable"); @@ -43,12 +43,12 @@ public static float VariantError(float a) public static uint EmptyError(uint a) { - return imports.test.results.TestInterop.EmptyError(a); + return Imports.test.results.ITestImports.EmptyError(a); } public static void DoubleError(uint a) { - imports.test.results.TestInterop.DoubleError(a); + Imports.test.results.ITestImports.DoubleError(a); } } } diff --git a/tests/runtime/results/intermediate_with_wit_results.cs b/tests/runtime/results/intermediate_with_wit_results.cs index d8710dd5c..f48ac5724 100644 --- a/tests/runtime/results/intermediate_with_wit_results.cs +++ b/tests/runtime/results/intermediate_with_wit_results.cs @@ -1,61 +1,62 @@ //@ args = '--with-wit-results' +using IntermediateWorld.wit.Imports.test.results; -namespace IntermediateWorld.wit.exports.test.results +namespace IntermediateWorld.wit.Exports.test.results { - public class TestImpl : ITest + public class TestExportsImpl : ITestExports { public static Result StringError(float a) { - return imports.test.results.TestInterop.StringError(a); + return ITestImports.StringError(a); } - public static Result EnumError(float a) + public static Result EnumError(float a) { - var result = imports.test.results.TestInterop.EnumError(a); + var result = ITestImports.EnumError(a); if (result.IsOk) { - return Result.Ok(result.AsOk); + return Result.Ok(result.AsOk); } else { switch (result.AsErr){ - case imports.test.results.ITest.E.A: - return Result.Err(ITest.E.A); - case imports.test.results.ITest.E.B: - return Result.Err(ITest.E.B); - case imports.test.results.ITest.E.C: - return Result.Err(ITest.E.C); + case ITestImports.E.A: + return Result.Err(ITestExports.E.A); + case ITestImports.E.B: + return Result.Err(ITestExports.E.B); + case ITestImports.E.C: + return Result.Err(ITestExports.E.C); default: throw new Exception("unreachable"); } } } - public static Result RecordError(float a) + public static Result RecordError(float a) { - var result = imports.test.results.TestInterop.RecordError(a); + var result = ITestImports.RecordError(a); if (result.IsOk) { - return Result.Ok(result.AsOk); + return Result.Ok(result.AsOk); } else { switch (result.AsErr) { - case imports.test.results.ITest.E2: - return Result.Err(new ITest.E2(result.AsErr.line, result.AsErr.column)); + case ITestImports.E2: + return Result.Err(new ITestExports.E2(result.AsErr.line, result.AsErr.column)); default: throw new Exception("unreachable"); } } } - public static Result VariantError(float a) + public static Result VariantError(float a) { - var result = imports.test.results.TestInterop.VariantError(a); + var result = ITestImports.VariantError(a); if (result.IsOk) { - return Result.Ok(result.AsOk); + return Result.Ok(result.AsOk); } else { switch (result.AsErr) { - case imports.test.results.ITest.E3: + case ITestImports.E3: switch (result.AsErr.Tag){ - case imports.test.results.ITest.E3.Tags.E1: - return Result.Err(ITest.E3.E1((ITest.E)Enum.Parse(typeof(ITest.E), result.AsErr.AsE1.ToString()))); - case imports.test.results.ITest.E3.Tags.E2: - return Result.Err(ITest.E3.E2(new ITest.E2(result.AsErr.AsE2.line, result.AsErr.AsE2.column))); + case ITestImports.E3.Tags.E1: + return Result.Err(ITestExports.E3.E1((ITestExports.E)Enum.Parse(typeof(ITestExports.E), result.AsErr.AsE1.ToString()))); + case ITestImports.E3.Tags.E2: + return Result.Err(ITestExports.E3.E2(new ITestExports.E2(result.AsErr.AsE2.line, result.AsErr.AsE2.column))); default: throw new Exception("unreachable"); } @@ -67,12 +68,12 @@ public static Result StringError(float a) public static Result EmptyError(uint a) { - return imports.test.results.TestInterop.EmptyError(a); + return ITestImports.EmptyError(a); } public static Result, string> DoubleError(uint a) { - return imports.test.results.TestInterop.DoubleError(a); + return ITestImports.DoubleError(a); } } } diff --git a/tests/runtime/rust/resource_into_inner/test.cs b/tests/runtime/rust/resource_into_inner/test.cs index aa784569d..46001f3c1 100644 --- a/tests/runtime/rust/resource_into_inner/test.cs +++ b/tests/runtime/rust/resource_into_inner/test.cs @@ -1,16 +1,17 @@ using System.Diagnostics; +using TestWorld.wit.Exports.test.resourceIntoInner; -namespace TestWorld.wit.exports.test.resourceIntoInner +namespace TestWorld.wit.Exports.test.resourceIntoInner { - public class ToTestImpl : IToTest { - public class Thing : IToTest.Thing, IToTest.IThing { + public class ToTestExportsImpl : IToTestExports { + public class Thing : IToTestExports.Thing, IToTestExports.IThing { public string val; public Thing(string v) { this.val = v; } } - + public static void Test() { // Unlike wasm.rs, this doesn't test anything interesting // due there being no ownership semantics in C# (and also diff --git a/tests/runtime/strings/runner.cs b/tests/runtime/strings/runner.cs index cac32947d..835494e42 100644 --- a/tests/runtime/strings/runner.cs +++ b/tests/runtime/strings/runner.cs @@ -1,16 +1,16 @@ using System; using System.Runtime.InteropServices; using System.Diagnostics; -using RunnerWorld.wit.imports.test.strings; +using RunnerWorld.wit.Imports.test.strings; using System.Text; public class Program { public static void Main(string[] args){ - ToTestInterop.TakeBasic("latin utf16"); - Debug.Assert(ToTestInterop.ReturnUnicode() == "🚀🚀🚀 𠈄𓀀"); + IToTestImports.TakeBasic("latin utf16"); + Debug.Assert(IToTestImports.ReturnUnicode() == "🚀🚀🚀 𠈄𓀀"); - Debug.Assert(ToTestInterop.ReturnEmpty() == string.Empty); - Debug.Assert(ToTestInterop.Roundtrip("🚀🚀🚀 𠈄𓀀") == "🚀🚀🚀 𠈄𓀀"); + Debug.Assert(IToTestImports.ReturnEmpty() == string.Empty); + Debug.Assert(IToTestImports.Roundtrip("🚀🚀🚀 𠈄𓀀") == "🚀🚀🚀 𠈄𓀀"); } } diff --git a/tests/runtime/strings/test.cs b/tests/runtime/strings/test.cs index b5dfc63a8..458f16dd5 100644 --- a/tests/runtime/strings/test.cs +++ b/tests/runtime/strings/test.cs @@ -1,10 +1,8 @@ -using System; -using System.Runtime.InteropServices; using System.Diagnostics; -namespace TestWorld.wit.exports.test.strings +namespace TestWorld.wit.Exports.test.strings { - public class ToTestImpl : ITestWorld + public class ToTestExportsImpl : IToTestExports { public static void TakeBasic(string s) { diff --git a/tests/runtime/variants/runner.cs b/tests/runtime/variants/runner.cs index 322d8b182..b44aed171 100644 --- a/tests/runtime/variants/runner.cs +++ b/tests/runtime/variants/runner.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.InteropServices; using System.Diagnostics; -using RunnerWorld.wit.imports.test.variants; +using RunnerWorld.wit.Imports.test.variants; using System.Text; using RunnerWorld; @@ -9,15 +9,15 @@ public class Program { public static void Main(string[] args) { - Debug.Assert(ToTestInterop.RoundtripOption(1.0f).Value == 1); - Debug.Assert(ToTestInterop.RoundtripOption(null).HasValue == false); - Debug.Assert(ToTestInterop.RoundtripOption(2.0f).Value == 2); + Debug.Assert(IToTestImports.RoundtripOption(1.0f).Value == 1); + Debug.Assert(IToTestImports.RoundtripOption(null).HasValue == false); + Debug.Assert(IToTestImports.RoundtripOption(2.0f).Value == 2); - Debug.Assert(ToTestInterop.RoundtripResult(Result.Ok(2)) == 2.0); - Debug.Assert(ToTestInterop.RoundtripResult(Result.Ok(4)) == 4.0); + Debug.Assert(IToTestImports.RoundtripResult(Result.Ok(2)) == 2.0); + Debug.Assert(IToTestImports.RoundtripResult(Result.Ok(4)) == 4.0); try { - ToTestInterop.RoundtripResult(Result.Err(5.3f)); + IToTestImports.RoundtripResult(Result.Err(5.3f)); throw new Exception(); } catch (WitException e) @@ -25,14 +25,13 @@ public static void Main(string[] args) Debug.Assert((byte)e.Value == 5); } - Debug.Assert(ToTestInterop.RoundtripEnum(IToTest.E1.A) == IToTest.E1.A); - Debug.Assert(ToTestInterop.RoundtripEnum(IToTest.E1.B) == IToTest.E1.B); - - Debug.Assert(ToTestInterop.InvertBool(true) == false); - Debug.Assert(ToTestInterop.InvertBool(false) == true); + Debug.Assert(IToTestImports.RoundtripEnum(IToTestImports.E1.A) == IToTestImports.E1.A); + Debug.Assert(IToTestImports.RoundtripEnum(IToTestImports.E1.B) == IToTestImports.E1.B); + Debug.Assert(IToTestImports.InvertBool(true) == false); + Debug.Assert(IToTestImports.InvertBool(false) == true); var (a1, a2, a3, a4, a5, a6) = - ToTestInterop.VariantCasts((IToTest.C1.A(1), IToTest.C2.A(2), IToTest.C3.A(3), IToTest.C4.A(4), IToTest.C5.A(5), IToTest.C6.A(6.0f))); + IToTestImports.VariantCasts((IToTestImports.C1.A(1), IToTestImports.C2.A(2), IToTestImports.C3.A(3), IToTestImports.C4.A(4), IToTestImports.C5.A(5), IToTestImports.C6.A(6.0f))); Debug.Assert(a1.AsA == 1); Debug.Assert(a2.AsA == 2); Debug.Assert(a3.AsA == 3); @@ -41,7 +40,7 @@ public static void Main(string[] args) Debug.Assert(a6.AsA == 6.0f); var (b1, b2, b3, b4, b5, b6) = -ToTestInterop.VariantCasts((IToTest.C1.B(1), IToTest.C2.B(2), IToTest.C3.B(3), IToTest.C4.B(4), IToTest.C5.B(5), IToTest.C6.B(6.0))); +IToTestImports.VariantCasts((IToTestImports.C1.B(1), IToTestImports.C2.B(2), IToTestImports.C3.B(3), IToTestImports.C4.B(4), IToTestImports.C5.B(5), IToTestImports.C6.B(6.0))); Debug.Assert(b1.AsB == 1); Debug.Assert(b2.AsB == 2.0f); Debug.Assert(b3.AsB == 3.0f); @@ -50,25 +49,25 @@ public static void Main(string[] args) Debug.Assert(b6.AsB == 6.0); var (za1, za2, za3, za4) = -ToTestInterop.VariantZeros((IToTest.Z1.A(1), IToTest.Z2.A(2), IToTest.Z3.A(3.0f), IToTest.Z4.A(4.0f))); +IToTestImports.VariantZeros((IToTestImports.Z1.A(1), IToTestImports.Z2.A(2), IToTestImports.Z3.A(3.0f), IToTestImports.Z4.A(4.0f))); Debug.Assert(za1.AsA == 1); Debug.Assert(za2.AsA == 2); Debug.Assert(za3.AsA == 3.0f); Debug.Assert(za4.AsA == 4.0f); var (zb1, zb2, zb3, zb4) = -ToTestInterop.VariantZeros((IToTest.Z1.B(), IToTest.Z2.B(), IToTest.Z3.B(), IToTest.Z4.B())); +IToTestImports.VariantZeros((IToTestImports.Z1.B(), IToTestImports.Z2.B(), IToTestImports.Z3.B(), IToTestImports.Z4.B())); //TODO: Add comparison operator to variants and None //Debug.Assert(zb1.AsB == IToTest.Z1.b()); //Debug.Assert(zb2.AsB == IToTest.Z2.b()); //Debug.Assert(zb3.AsB == IToTest.Z3.b()); //Debug.Assert(zb4.AsB == IToTest.Z4.b()); - ToTestInterop.VariantTypedefs(null, false, Result.Err(new None())); + IToTestImports.VariantTypedefs(null, false, Result.Err(new None())); - var (a, b, c) = ToTestInterop.VariantEnums(true, Result.Ok(new None()), IToTest.MyErrno.SUCCESS); + var (a, b, c) = IToTestImports.VariantEnums(true, Result.Ok(new None()), IToTestImports.MyErrno.SUCCESS); Debug.Assert(a == true); var test = b.AsOk; - Debug.Assert(c == IToTest.MyErrno.SUCCESS); + Debug.Assert(c == IToTestImports.MyErrno.SUCCESS); } } diff --git a/tests/runtime/variants/test.cs b/tests/runtime/variants/test.cs index 3530be950..bad27cd16 100644 --- a/tests/runtime/variants/test.cs +++ b/tests/runtime/variants/test.cs @@ -1,6 +1,6 @@ -namespace TestWorld.wit.exports.test.variants +namespace TestWorld.wit.Exports.test.variants { - public class ToTestImpl : ITestWorld + public class ToTestExportsImpl : ITestWorldImports { public static byte? RoundtripOption(float? a) { @@ -17,7 +17,7 @@ public static double RoundtripResult(Result a) } } - public static IToTest.E1 RoundtripEnum(IToTest.E1 a) + public static IToTestExports.E1 RoundtripEnum(IToTestExports.E1 a) { return a; } @@ -27,21 +27,21 @@ public static bool InvertBool(bool a) return !a; } - public static (IToTest.C1, IToTest.C2, IToTest.C3, IToTest.C4, IToTest.C5, IToTest.C6) - VariantCasts((IToTest.C1, IToTest.C2, IToTest.C3, IToTest.C4, IToTest.C5, IToTest.C6) a) + public static (IToTestExports.C1, IToTestExports.C2, IToTestExports.C3, IToTestExports.C4, IToTestExports.C5, IToTestExports.C6) + VariantCasts((IToTestExports.C1, IToTestExports.C2, IToTestExports.C3, IToTestExports.C4, IToTestExports.C5, IToTestExports.C6) a) { return a; } - public static (bool, Result, IToTest.MyErrno) - VariantEnums(bool a, Result b, IToTest.MyErrno c) + public static (bool, Result, IToTestExports.MyErrno) + VariantEnums(bool a, Result b, IToTestExports.MyErrno c) { return new(a, b, c); } public static void VariantTypedefs(uint? a, bool b, Result c) { } - public static (IToTest.Z1, IToTest.Z2, IToTest.Z3, IToTest.Z4) VariantZeros((IToTest.Z1, IToTest.Z2, IToTest.Z3, IToTest.Z4) a) + public static (IToTestExports.Z1, IToTestExports.Z2, IToTestExports.Z3, IToTestExports.Z4) VariantZeros((IToTestExports.Z1, IToTestExports.Z2, IToTestExports.Z3, IToTestExports.Z4) a) { return a; } diff --git a/tests/runtime/versions/runner.cs b/tests/runtime/versions/runner.cs index aee15941d..49ff66854 100644 --- a/tests/runtime/versions/runner.cs +++ b/tests/runtime/versions/runner.cs @@ -1,17 +1,15 @@ -using System; -using System.Runtime.InteropServices; using System.Diagnostics; -using v1 = RunnerWorld.wit.imports.test.dep.v0_1_0; -using v2 = RunnerWorld.wit.imports.test.dep.v0_2_0; +using v1 = RunnerWorld.wit.Imports.test.dep.v0_1_0; +using v2 = RunnerWorld.wit.Imports.test.dep.v0_2_0; using System.Text; public class Program { public static void Main(string[] args){ - Debug.Assert(v1.TestInterop.X() == 1.0f); - Debug.Assert(v1.TestInterop.Y(1.0f) == 2.0f); + Debug.Assert(v1.ITestImports.X() == 1.0f); + Debug.Assert(v1.ITestImports.Y(1.0f) == 2.0f); - Debug.Assert(v2.TestInterop.X() == 2.0f); - Debug.Assert(v2.TestInterop.Z(1.0f, 1.0f) == 4.0f); + Debug.Assert(v2.ITestImports.X() == 2.0f); + Debug.Assert(v2.ITestImports.Z(1.0f, 1.0f) == 4.0f); } } diff --git a/tests/runtime/versions/test.cs b/tests/runtime/versions/test.cs index a5eb592b1..62c90f765 100644 --- a/tests/runtime/versions/test.cs +++ b/tests/runtime/versions/test.cs @@ -1,7 +1,7 @@ using System.Diagnostics; -namespace TestWorld.wit.exports.test.dep.v0_1_0 { - public class TestImpl : TestWorld.wit.exports.test.dep.v0_1_0.ITest +namespace TestWorld.wit.Exports.test.dep.v0_1_0 { + public class TestExportsImpl : TestWorld.wit.Exports.test.dep.v0_1_0.ITestExports { public static float X() { return 1.0f; @@ -13,9 +13,9 @@ public static float Y(float a){ } } -namespace TestWorld.wit.exports.test.dep.v0_2_0 +namespace TestWorld.wit.Exports.test.dep.v0_2_0 { - public class TestImpl : TestWorld.wit.exports.test.dep.v0_2_0.ITest + public class TestExportsImpl : TestWorld.wit.Exports.test.dep.v0_2_0.ITestExports { public static float X() { return 2.0f; From bc6fb930bd58f91f179d29d0ca03bed77647fe7e Mon Sep 17 00:00:00 2001 From: yowl Date: Sat, 6 Dec 2025 09:00:41 -0500 Subject: [PATCH 2/2] Address feedback Combine FutureReader and FutureWriter to AsyncSupport. Start the process of adding futures per type --- crates/csharp/src/AsyncSupport.cs | 168 ++++++++++++++- crates/csharp/src/FutureCommonSupport.cs | 20 -- crates/csharp/src/FutureReaderSupport.cs | 49 ----- crates/csharp/src/FutureWriterSupport.cs | 44 ---- crates/csharp/src/function.rs | 16 +- crates/csharp/src/interface.rs | 192 ++++++------------ crates/csharp/src/world_generator.rs | 27 +-- .../async/simple-future/runner.cs | 16 +- 8 files changed, 251 insertions(+), 281 deletions(-) delete mode 100644 crates/csharp/src/FutureReaderSupport.cs delete mode 100644 crates/csharp/src/FutureWriterSupport.cs diff --git a/crates/csharp/src/AsyncSupport.cs b/crates/csharp/src/AsyncSupport.cs index a80ec6e4a..837d63f68 100644 --- a/crates/csharp/src/AsyncSupport.cs +++ b/crates/csharp/src/AsyncSupport.cs @@ -2,8 +2,174 @@ * Helpers for the async support. */ - public enum CallbackCode +public enum CallbackCode { Exit = 0, Yield = 1, } + +public partial class WaitableSet(int handle) : IDisposable +{ + public int Handle { get; } = handle; + + void Dispose(bool _disposing) + { + AsyncSupport.WaitableSetDrop(this); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~WaitableSet() + { + Dispose(false); + } +} + +public static class AsyncSupport +{ + private static class Interop + { + [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-set-new]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern int WaitableSetNew(); + + [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-join]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern void WaitableJoin(int waitable, int set); + + [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-set-wait]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static unsafe extern int WaitableSetWait(int waitable, int* waitableHandlePtr); + + [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-set-drop]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static unsafe extern void WaitableSetDrop(int waitable); + } + + public static WaitableSet WaitableSetNew() + {{ + var waitable = Interop.WaitableSetNew(); + return new WaitableSet(waitable); + }} + + public static void Join(FutureWriter writer, WaitableSet set) + {{ + Interop.WaitableJoin(writer.Handle, set.Handle); + }} + + public unsafe static EventWaitable WaitableSetWait(WaitableSet set) + {{ + int* buffer = stackalloc int[2]; + var eventCode = (EventCode)Interop.WaitableSetWait(set.Handle, buffer); + return new EventWaitable(eventCode, buffer[0], buffer[1]); + }} + + public static void WaitableSetDrop(WaitableSet set) + {{ + Interop.WaitableSetDrop(set.Handle); + }} +} + +/** + * Helpers for future reader support. + */ +public abstract class FutureReader(int handle) : IDisposable // : TODO Waitable +{ + public int Handle { get; private set; } = handle; + + public int TakeHandle() + { + if(Handle == 0) + { + throw new InvalidOperationException("Handle already taken"); + } + var handle = Handle; + Handle = 0; + return handle; + } + + // TODO: Generate per type for this instrinsic. + public Task Read() + { + // TODO: Generate for the interop name and the namespace. + + var status = new WaitableStatus(ReadInternal()); + if (status.IsBlocked) + { + //TODO: store somewhere so we can complete it later. + var tcs = new TaskCompletionSource(); + + return tcs.Task; + } + if (status.IsCompleted) + { + return Task.CompletedTask; + } + + throw new NotImplementedException(); + } + + void Dispose(bool _disposing) + { + // Free unmanaged resources if any. + Drop(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~FutureReader() + { + Dispose(false); + } + + protected abstract int ReadInternal(); + protected abstract void Drop(); +} + +/** + * Helpers for future writer support. + */ +public abstract class FutureWriter(int handle) // : TODO Waitable +{ + public int Handle { get; } = handle; + + // TODO: Generate per type for this instrinsic. + public Task Write() + { + // TODO: Generate for the interop name. + var status = new WaitableStatus(Write(Handle, IntPtr.Zero)); + if (status.IsBlocked) + { + //TODO: store somewhere so we can complete it later. + var tcs = new TaskCompletionSource(); + return tcs.Task; + } + + throw new NotImplementedException(); + } + + protected abstract void Drop(); + + void Dispose(bool _disposing) + { + // Free unmanaged resources if any. + Drop(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~FutureWriter() + { + Dispose(false); + } + + protected abstract int Write(int handle, IntPtr buffer); +} diff --git a/crates/csharp/src/FutureCommonSupport.cs b/crates/csharp/src/FutureCommonSupport.cs index 275dc53ca..a5c4e6f19 100644 --- a/crates/csharp/src/FutureCommonSupport.cs +++ b/crates/csharp/src/FutureCommonSupport.cs @@ -37,23 +37,3 @@ public EventWaitable(EventCode eventCode, int waitable, int code) public readonly WaitableStatus Status; } -public partial class WaitableSet(int handle) : IDisposable -{ - public int Handle { get; } = handle; - - void Dispose(bool _disposing) - { - {{interop_name}}.WaitableSetDrop(Handle); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~WaitableSet() - { - Dispose(false); - } -} \ No newline at end of file diff --git a/crates/csharp/src/FutureReaderSupport.cs b/crates/csharp/src/FutureReaderSupport.cs deleted file mode 100644 index 8def9ad1f..000000000 --- a/crates/csharp/src/FutureReaderSupport.cs +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Helpers for future reader support. - */ - -public abstract class FutureReader(int handle) : IDisposable // : TODO Waitable -{ - public int Handle { get; } = handle; - - // TODO: Generate per type for this instrinsic. - public Task Read() - { - // TODO: Generate for the interop name and the namespace. - - var status = new WaitableStatus(ReadInternal()); - if (status.IsBlocked) - { - //TODO: store somewhere so we can complete it later. - var tcs = new TaskCompletionSource(); - - return tcs.Task; - } - if (status.IsCompleted) - { - return Task.CompletedTask; - } - - throw new NotImplementedException(); - } - - void Dispose(bool _disposing) - { - // Free unmanaged resources if any. - Drop(); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~FutureReader() - { - Dispose(false); - } - - protected abstract int ReadInternal(); - protected abstract void Drop(); -} \ No newline at end of file diff --git a/crates/csharp/src/FutureWriterSupport.cs b/crates/csharp/src/FutureWriterSupport.cs deleted file mode 100644 index 2e81fedcb..000000000 --- a/crates/csharp/src/FutureWriterSupport.cs +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Helpers for future writer support. - */ - -public abstract class FutureWriter(int handle) // : TODO Waitable -{ - public int Handle { get; } = handle; - - // TODO: Generate per type for this instrinsic. - public Task Write() - { - // TODO: Generate for the interop name. - var status = new WaitableStatus(Write(Handle, IntPtr.Zero)); - if (status.IsBlocked) - { - //TODO: store somewhere so we can complete it later. - var tcs = new TaskCompletionSource(); - return tcs.Task; - } - - throw new NotImplementedException(); - } - - protected abstract void Drop(); - - void Dispose(bool _disposing) - { - // Free unmanaged resources if any. - Drop(); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~FutureWriter() - { - Dispose(false); - } - - protected abstract int Write(int handle, IntPtr buffer); -} diff --git a/crates/csharp/src/function.rs b/crates/csharp/src/function.rs index 8c33d1125..0a0a1ee80 100644 --- a/crates/csharp/src/function.rs +++ b/crates/csharp/src/function.rs @@ -1399,9 +1399,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { results.extend(operands.iter().take(*amt).map(|v| v.clone())); } - Instruction::FutureLower { .. } => { + Instruction::FutureLower { payload: _, ty } => { let op = &operands[0]; - self.interface_gen.add_future(self.func_name); + self.interface_gen.add_future(self.func_name, ty); results.push(format!("{op}.Handle")); } @@ -1410,15 +1410,15 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "// TODO_task_cancel.forget();"); } - Instruction::FutureLift { payload: _, ty: _ } => { + Instruction::FutureLift { payload: _, ty } => { // TODO get the prefix for the type let sig_type_name = "Void"; - uwriteln!(self.src, "var reader = new {}.FutureReader{}({});", self.interface_gen.name, sig_type_name, operands[0]); - self.interface_gen.csharp_gen.needs_future_reader_support = true; - results.push("reader".to_string()); + let reader_var = self.locals.tmp("reader"); + uwriteln!(self.src, "var {reader_var} = new {}.FutureReader{}({});", self.interface_gen.name, sig_type_name, operands[0]); + results.push(reader_var); - self.interface_gen.add_future(self.func_name); - self.interface_gen.csharp_gen.needs_future_reader_support = true; + self.interface_gen.add_future(self.func_name, ty); + self.interface_gen.csharp_gen.needs_async_support = true; } Instruction::StreamLower { .. } diff --git a/crates/csharp/src/interface.rs b/crates/csharp/src/interface.rs index 572b3faec..c5d6d05bf 100644 --- a/crates/csharp/src/interface.rs +++ b/crates/csharp/src/interface.rs @@ -4,6 +4,7 @@ use crate::function::ResourceInfo; use crate::world_generator::CSharp; use heck::{ToShoutySnakeCase, ToUpperCamelCase}; use std::collections::HashMap; +use std::collections::HashSet; use std::fmt::Write; use std::ops::Deref; use wit_bindgen_core::abi::LiftLower; @@ -37,6 +38,11 @@ impl InterfaceTypeAndFragments { } } +pub(crate) struct FutureInfo { + pub name: String, + pub ty: TypeId, +} + /// InterfaceGenerator generates the C# code for wit interfaces. /// It produces types by interface in wit and then generates the interop code /// by calling out to FunctionGenerator @@ -48,7 +54,7 @@ pub(crate) struct InterfaceGenerator<'a> { pub(crate) resolve: &'a Resolve, pub(crate) name: &'a str, pub(crate) direction: Direction, - pub(crate) futures: Vec, + pub(crate) futures: Vec, pub(crate) is_world: bool, } @@ -207,103 +213,72 @@ impl InterfaceGenerator<'_> { let (_namespace, interface_name) = &CSharp::get_class_name_from_qualified_name(self.name); let interop_name = format!("{}Interop", interface_name.strip_prefix("I").unwrap()); + let mut generated_future_types: HashSet = HashSet::new(); for future in &self.futures { - let camel_name = future.to_upper_camel_case(); - - //TODO: we need these per future type. - // Create a hash map.. - uwrite!( - self.csharp_interop_src, - r#" - [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-new-0][async]{future}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] - internal static extern ulong {camel_name}VoidNew(); - "# - ); - - // TODO: Move this and other type dependent functions out to another function. - uwrite!( - self.csharp_interop_src, - r#" - [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-cancel-read-0][async]{future}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] - internal static extern uint {camel_name}CancelRead(int readable); - "# - ); + let future_name = &future.name; + if !generated_future_types.contains(&future.ty) { + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-new-0][async]{future_name}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern ulong FutureNew(); + "# + ); - uwrite!( - self.csharp_interop_src, - r#" - [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-cancel-write-0][async]{future}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] - internal static extern uint {camel_name}CancelWrite(int writeable); - "# - ); + // TODO: Move this and other type dependent functions out to another function. + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-cancel-read-0][async]{future_name}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern uint FutureCancelRead(int readable); + "# + ); - uwrite!( - self.csharp_interop_src, - r#" - [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-drop-writeable-0][async]{future}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] - internal static extern void {camel_name}DropWriteable(int writeable); - "# - ); + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-cancel-write-0][async]{future_name}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern uint FutureCancelWrite(int writeable); + "# + ); - self.csharp_gen - .interface_fragments - .entry(self.name.to_string()) - .or_insert_with(|| InterfaceTypeAndFragments::new(false)) - .interface_fragments - .push(InterfaceFragment { - csharp_src: format!(r#" - public static (FutureReader, FutureWriter) {camel_name}VoidNew() - {{ - var packed = {interop_name}.{camel_name}VoidNew(); - var readerHandle = (int)(packed & 0xFFFFFFFF); - var writerHandle = (int)(packed >> 32); + uwrite!( + self.csharp_interop_src, + r#" + [global::System.Runtime.InteropServices.DllImportAttribute("{import_module_name}", EntryPoint = "[future-drop-writeable-0][async]{future_name}"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] + internal static extern void FutureDropWriteable(int writeable); + "# + ); - return (new FutureReaderVoid(readerHandle), new FutureWriterVoid(writerHandle)); - }} - "#).to_string(), - csharp_interop_src: "".to_string(), - stub: "".to_string(), - direction: Some(self.direction), - }); + self.csharp_gen + .interface_fragments + .entry(self.name.to_string()) + .or_insert_with(|| InterfaceTypeAndFragments::new(false)) + .interface_fragments + .push(InterfaceFragment { + csharp_src: format!(r#" + public static (FutureReader, FutureWriter) FutureNew() + {{ + var packed = {interop_name}.FutureNew(); + var readerHandle = (int)(packed & 0xFFFFFFFF); + var writerHandle = (int)(packed >> 32); - self.csharp_gen.needs_future_reader_support = true; - self.csharp_gen.needs_future_writer_support = true; + return (new FutureReaderVoid(readerHandle), new FutureWriterVoid(writerHandle)); + }} + "#).to_string(), + csharp_interop_src: "".to_string(), + stub: "".to_string(), + direction: Some(self.direction), + }); + generated_future_types.insert(future.ty); + } } - uwrite!( - self.csharp_interop_src, - r#" - [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-set-new]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] - internal static extern int WaitableSetNew(); - "# - ); - - uwrite!( - self.csharp_interop_src, - r#" - [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-join]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] - internal static extern void WaitableJoin(int waitable, int set); - "# - ); - - uwrite!( - self.csharp_interop_src, - r#" - [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-set-wait]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] - internal static unsafe extern int WaitableSetWait(int waitable, int* waitableHandlePtr); - "# - ); - - uwrite!( - self.csharp_interop_src, - r#" - [global::System.Runtime.InteropServices.DllImportAttribute("$root", EntryPoint = "[waitable-set-drop]"), global::System.Runtime.InteropServices.WasmImportLinkageAttribute] - internal static unsafe extern void WaitableSetDrop(int waitable); - "# - ); + self.csharp_gen.needs_async_support = true; - // TODO: for each type: + // TODO: When we do the next type, switch to a generic implementation with a vtable style call + // for the interop functions. This will align c# with other lanaguage implementations. let future_type_name = "Void"; self.csharp_gen @@ -359,40 +334,6 @@ impl InterfaceGenerator<'_> { stub: "".to_string(), direction: Some(self.direction), }); - - self.csharp_gen - .interface_fragments - .entry(self.name.to_string()) - .or_insert_with(|| InterfaceTypeAndFragments::new(false)) - .interface_fragments - .push(InterfaceFragment { - csharp_src: format!( - r#" - public static WaitableSet WaitableSetNew() - {{ - var waitable = {interop_name}.WaitableSetNew(); - return new WaitableSet(waitable); - }} - - public static void Join(FutureWriter writer, WaitableSet set) - {{ - {interop_name}.WaitableJoin(writer.Handle, set.Handle); - }} - - public unsafe static EventWaitable WaitableSetWait(WaitableSet set) - {{ - int* buffer = stackalloc int[2]; - var eventCode = (EventCode){interop_name}.WaitableSetWait(set.Handle, buffer); - return new EventWaitable(eventCode, buffer[0], buffer[1]); - }} - - "# - ) - .to_string(), - csharp_interop_src: "".to_string(), - stub: "".to_string(), - direction: Some(self.direction), - }); } pub(crate) fn add_world_fragment(self, direction: Option) { @@ -1030,7 +971,8 @@ impl InterfaceGenerator<'_> { if name.is_empty() { return "FutureReader".to_owned(); } else { - return format!("FutureReader<{name}>"); + // TODO: When we add more future types, this will become a generic. + return format!("FutureReader{name}"); } } _ => { @@ -1310,8 +1252,8 @@ impl InterfaceGenerator<'_> { format!("{access} {modifiers} {result_type} {camel_name}({params})") } - pub(crate) fn add_future(&mut self, func_name: &str) { - self.futures.push(func_name.to_string()); + pub(crate) fn add_future(&mut self, func_name: &str, ty: &TypeId) { + self.futures.push(FutureInfo { name: func_name.to_string(), ty: ty.clone() }); } } diff --git a/crates/csharp/src/world_generator.rs b/crates/csharp/src/world_generator.rs index ac8b8abea..d17fb7770 100644 --- a/crates/csharp/src/world_generator.rs +++ b/crates/csharp/src/world_generator.rs @@ -34,8 +34,6 @@ pub struct CSharp { pub(crate) needs_rep_table: bool, pub(crate) needs_wit_exception: bool, pub(crate) needs_async_support: bool, - pub(crate) needs_future_reader_support: bool, - pub(crate) needs_future_writer_support: bool, pub(crate) interface_fragments: HashMap, pub(crate) world_fragments: Vec, pub(crate) sizes: SizeAlign, @@ -642,32 +640,9 @@ impl WorldGenerator for CSharp { if self.needs_async_support { src.push_str("\n"); src.push_str(include_str!("AsyncSupport.cs")); - } - - if (self.needs_future_reader_support || self.needs_future_writer_support) - && self.interface_fragments.len() > 0 - { - let full_name = self.interface_fragments.iter().next().unwrap().0; - - let (namespace, interface_name) = - &CSharp::get_class_name_from_qualified_name(full_name); - let base_name = interface_name.strip_prefix("I").unwrap(); - let full_name = format!("{namespace}.{base_name}Interop"); - src.push_str("\n"); - src.push_str( - &include_str!("FutureCommonSupport.cs") - .replace("{{interop_name}}", full_name.as_str()), - ); - } - - if self.needs_future_reader_support { - src.push_str("\n"); - src.push_str(include_str!("FutureReaderSupport.cs")); - } - if self.needs_future_writer_support { src.push_str("\n"); - src.push_str(include_str!("FutureWriterSupport.cs")); + src.push_str(&include_str!("FutureCommonSupport.cs")); } src.push_str("\n"); diff --git a/tests/runtime-async/async/simple-future/runner.cs b/tests/runtime-async/async/simple-future/runner.cs index 02ace66e8..730d8dca1 100644 --- a/tests/runtime-async/async/simple-future/runner.cs +++ b/tests/runtime-async/async/simple-future/runner.cs @@ -9,7 +9,7 @@ public class Program public static async Task Main(string[] args) { { - var (reader, writer) = IIImports.ReadFutureVoidNew(); + var (reader, writer) = IIImports.FutureNew(); var writeTask = writer.Write(); Debug.Assert(!writeTask.IsCompleted); @@ -17,11 +17,11 @@ public static async Task Main(string[] args) var task = IIImports.ReadFuture(reader); Debug.Assert(task.IsCompleted); - var set = IIImports.WaitableSetNew(); - IIImports.Join(writer, set); + var set = AsyncSupport.WaitableSetNew(); + AsyncSupport.Join(writer, set); var ev = new EventWaitable(); - var status = IIImports.WaitableSetWait(set); + var status = AsyncSupport.WaitableSetWait(set); Debug.Assert(status.Event == EventCode.FutureWrite); Debug.Assert(status.Waitable == writer.Handle); Debug.Assert(status.Status.IsCompleted); @@ -32,7 +32,7 @@ public static async Task Main(string[] args) } { - var (reader, writer) = IIImports.DropFutureVoidNew(); + var (reader, writer) = IIImports.FutureNew(); var writeTask = writer.Write(); Debug.Assert(!writeTask.IsCompleted); @@ -40,11 +40,11 @@ public static async Task Main(string[] args) var task = IIImports.DropFuture(reader); Debug.Assert(task.IsCompleted); - var set = IIImports.WaitableSetNew(); - IIImports.Join(writer, set); + var set = AsyncSupport.WaitableSetNew(); + AsyncSupport.Join(writer, set); var ev = new EventWaitable(); - var status = IIImports.WaitableSetWait(set); + var status = AsyncSupport.WaitableSetWait(set); Debug.Assert(status.Event == EventCode.FutureWrite); Debug.Assert(status.Waitable == writer.Handle); Debug.Assert(status.Status.IsDropped);