diff --git a/crates/csharp/src/csproj.rs b/crates/csharp/src/csproj.rs index 626e89df2..fe5aa24b1 100644 --- a/crates/csharp/src/csproj.rs +++ b/crates/csharp/src/csproj.rs @@ -92,8 +92,8 @@ impl CSProjectLLVMBuilder { csproj.push_str( r#" - - + + "#, ); diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index 6f11ef47b..ff4c767c0 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -25,18 +25,6 @@ use wit_component::{StringEncoding, WitPrinter}; mod csproj; pub use csproj::CSProject; -//TODO remove unused -const CSHARP_IMPORTS: &str = "\ -using System; -using System.Runtime.CompilerServices; -using System.Collections; -using System.Runtime.InteropServices; -using System.Text; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -"; - #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Args))] pub struct Opts { @@ -106,6 +94,8 @@ struct InterfaceFragment { csharp_src: String, csharp_interop_src: String, stub: String, + usings: HashSet, + interop_usings: HashSet, } pub struct InterfaceTypeAndFragments { @@ -133,6 +123,8 @@ pub enum FunctionLevel { pub struct CSharp { opts: Opts, name: String, + usings: HashSet, + interop_usings: HashSet, return_area_size: usize, return_area_align: usize, tuple_counts: HashSet, @@ -180,6 +172,8 @@ impl CSharp { resolve, name, direction, + usings: HashSet::::new(), + interop_usings: HashSet::::new(), } } @@ -196,6 +190,20 @@ impl CSharp { (String::new(), String::new()) } } + + fn require_using(&mut self, using_ns: &str) { + if !self.usings.contains(using_ns) { + let using_ns_string = using_ns.to_string(); + self.usings.insert(using_ns_string); + } + } + + fn require_interop_using(&mut self, using_ns: &str) { + if !self.interop_usings.contains(using_ns) { + let using_ns_string = using_ns.to_string(); + self.interop_usings.insert(using_ns_string); + } + } } impl WorldGenerator for CSharp { @@ -405,10 +413,11 @@ impl WorldGenerator for CSharp { let access = self.access_modifier(); + let using_pos = src.len(); + uwrite!( src, - "{CSHARP_IMPORTS} - + " namespace {world_namespace} {{ {access} interface I{name}World {{ @@ -424,6 +433,16 @@ impl WorldGenerator for CSharp { .join("\n"), ); + let usings: Vec<_> = self + .world_fragments + .iter() + .flat_map(|f| &f.usings) + .cloned() + .collect(); + usings.iter().for_each(|u| { + self.require_using(u); + }); + let mut producers = wasm_metadata::Producers::empty(); producers.add( "processed-by", @@ -434,6 +453,7 @@ impl WorldGenerator for CSharp { src.push_str("}\n"); if self.needs_result { + self.require_using("System.Runtime.InteropServices"); uwrite!( src, r#" @@ -495,6 +515,7 @@ impl WorldGenerator for CSharp { } if self.needs_option { + self.require_using("System.Diagnostics.CodeAnalysis"); uwrite!( src, r#" @@ -525,6 +546,8 @@ impl WorldGenerator for CSharp { } if self.needs_interop_string { + self.require_using("System.Text"); + self.require_using("System.Runtime.InteropServices"); uwrite!( src, r#" @@ -568,6 +591,8 @@ impl WorldGenerator for CSharp { let (array_size, element_type) = dotnet_aligned_array(self.return_area_size, self.return_area_align); + + self.require_using("System.Runtime.CompilerServices"); uwrite!( ret_area_str, " @@ -607,6 +632,17 @@ impl WorldGenerator for CSharp { src.push_str("\n"); src.push_str("namespace exports {\n"); + + src.push_str( + &self + .world_fragments + .iter() + .flat_map(|f| &f.interop_usings) + .map(|s| "using ".to_owned() + s + ";") + .collect::>() + .join("\n"), + ); + src.push_str(&format!("{access} static class {name}World\n")); src.push_str("{"); @@ -623,6 +659,16 @@ impl WorldGenerator for CSharp { src.push_str("}\n"); + src.insert_str( + using_pos, + &self + .usings + .iter() + .map(|s| "using ".to_owned() + s + ";") + .collect::>() + .join("\n"), + ); + files.push(&format!("{name}.cs"), indent(&src).as_bytes()); let generate_stub = |name: String, files: &mut Files, stubs: Stubs| { @@ -668,8 +714,6 @@ impl WorldGenerator for CSharp { let body = format!( "{header} - {CSHARP_IMPORTS} - namespace {fully_qualified_namespace}; {access} partial class {stub_class_name} : {interface_or_class_name} {{ @@ -789,14 +833,20 @@ impl WorldGenerator for CSharp { if body.len() > 0 { let body = format!( "{header} - {CSHARP_IMPORTS} + {0} namespace {namespace}; {access} interface {interface_name} {{ {body} }} - " + ", + fragments + .iter() + .flat_map(|f| &f.usings) + .map(|s| "using ".to_owned() + s + ";") + .collect::>() + .join("\n"), ); files.push(&format!("{full_name}.cs"), indent(&body).as_bytes()); @@ -812,7 +862,7 @@ impl WorldGenerator for CSharp { let class_name = interface_name.strip_prefix("I").unwrap(); let body = format!( "{header} - {CSHARP_IMPORTS} + {0} namespace {namespace} {{ @@ -820,7 +870,13 @@ impl WorldGenerator for CSharp { {body} }} }} - " + ", + fragments + .iter() + .flat_map(|f| &f.interop_usings) + .map(|s| "using ".to_owned() + s + ";\n") + .collect::>() + .join(""), ); files.push( @@ -845,6 +901,8 @@ struct InterfaceGenerator<'a> { resolve: &'a Resolve, name: &'a str, direction: Direction, + usings: HashSet, + interop_usings: HashSet, } impl InterfaceGenerator<'_> { @@ -956,6 +1014,8 @@ impl InterfaceGenerator<'_> { csharp_src: self.src, csharp_interop_src: self.csharp_interop_src, stub: self.stub, + usings: self.usings, + interop_usings: self.interop_usings, }); } @@ -964,6 +1024,8 @@ impl InterfaceGenerator<'_> { csharp_src: self.src, csharp_interop_src: self.csharp_interop_src, stub: self.stub, + usings: self.usings, + interop_usings: self.interop_usings, }); } @@ -1083,8 +1145,10 @@ impl InterfaceGenerator<'_> { let import_name = &func.name; let target = if let FunctionKind::Freestanding = &func.kind { + self.require_interop_using("System.Runtime.InteropServices"); &mut self.csharp_interop_src } else { + self.require_using("System.Runtime.InteropServices"); &mut self.src }; @@ -1229,6 +1293,7 @@ impl InterfaceGenerator<'_> { let export_name = func.legacy_core_export_name(core_module_name.as_deref()); let access = self.gen.access_modifier(); + self.require_interop_using("System.Runtime.InteropServices"); uwrite!( self.csharp_interop_src, r#" @@ -1429,6 +1494,20 @@ impl InterfaceGenerator<'_> { } } + fn require_using(&mut self, using_ns: &str) { + if !self.usings.contains(using_ns) { + let using_ns_string = using_ns.to_string(); + self.usings.insert(using_ns_string); + } + } + + fn require_interop_using(&mut self, using_ns: &str) { + if !self.interop_usings.contains(using_ns) { + let using_ns_string = using_ns.to_string(); + self.interop_usings.insert(using_ns_string); + } + } + fn start_resource(&mut self, id: TypeId, key: Option<&WorldKey>) { let access = self.gen.access_modifier(); let qualified = self.type_name_with_qualifier(&Type::Id(id), true); @@ -1444,6 +1523,7 @@ impl InterfaceGenerator<'_> { .map(|key| self.resolve.name_world_key(key)) .unwrap_or_else(|| "$root".into()); + self.require_using("System.Runtime.InteropServices"); // As of this writing, we cannot safely drop a handle to an imported resource from a .NET finalizer // because it may still have one or more open child resources. Once WIT has explicit syntax for // indicating parent/child relationships, we should be able to use that information to keep track @@ -1482,6 +1562,7 @@ impl InterfaceGenerator<'_> { .map(|s| format!("{}#", self.resolve.name_world_key(s))) .unwrap_or_else(String::new); + self.require_interop_using("System.Runtime.InteropServices"); uwrite!( self.csharp_interop_src, r#" @@ -1500,6 +1581,7 @@ impl InterfaceGenerator<'_> { .map(|key| format!("[export]{}", self.resolve.name_world_key(key))) .unwrap_or_else(|| "[export]$root".into()); + self.require_using("System.Runtime.InteropServices"); // The ergonomics of exported resources are not ideal, currently. Implementing such a resource // requires both extending a class and implementing an interface. The reason for the class is to // allow implementers to inherit code which tracks and disposes of the resource handle; the reason @@ -2584,10 +2666,18 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.gen.gen.needs_interop_string = true; } - Instruction::StringLift { .. } => results.push(format!( - "Encoding.UTF8.GetString((byte*){}, {})", - operands[0], operands[1] - )), + Instruction::StringLift { .. } => { + if FunctionKind::Freestanding == *self.kind || self.gen.direction == Direction::Export { + self.gen.require_interop_using("System.Text"); + } else { + self.gen.require_using("System.Text"); + } + + results.push(format!( + "Encoding.UTF8.GetString((byte*){}, {})", + operands[0], operands[1] + )); + } Instruction::ListLower { element, realloc } => { let Block {