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 {