Skip to content

Commit

Permalink
[C#] minor improvement to reduce unused using statements (bytecodeall…
Browse files Browse the repository at this point in the history
…iance#1070)

* minor improvement to reduce unused using statements

* tidy usings, update to net10

* cargo fmt
  • Loading branch information
yowl authored Nov 19, 2024
1 parent 6d0120f commit 1739caf
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 26 deletions.
4 changes: 2 additions & 2 deletions crates/csharp/src/csproj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ impl CSProjectLLVMBuilder {
csproj.push_str(
r#"
<ItemGroup>
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="9.0.0-rc.1.24412.1" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="9.0.0-rc.1.24412.1" />
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
</ItemGroup>
"#,
);
Expand Down
138 changes: 114 additions & 24 deletions crates/csharp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -106,6 +94,8 @@ struct InterfaceFragment {
csharp_src: String,
csharp_interop_src: String,
stub: String,
usings: HashSet<String>,
interop_usings: HashSet<String>,
}

pub struct InterfaceTypeAndFragments {
Expand Down Expand Up @@ -133,6 +123,8 @@ pub enum FunctionLevel {
pub struct CSharp {
opts: Opts,
name: String,
usings: HashSet<String>,
interop_usings: HashSet<String>,
return_area_size: usize,
return_area_align: usize,
tuple_counts: HashSet<usize>,
Expand Down Expand Up @@ -180,6 +172,8 @@ impl CSharp {
resolve,
name,
direction,
usings: HashSet::<String>::new(),
interop_usings: HashSet::<String>::new(),
}
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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 {{
Expand All @@ -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",
Expand All @@ -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#"
Expand Down Expand Up @@ -495,6 +515,7 @@ impl WorldGenerator for CSharp {
}

if self.needs_option {
self.require_using("System.Diagnostics.CodeAnalysis");
uwrite!(
src,
r#"
Expand Down Expand Up @@ -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#"
Expand Down Expand Up @@ -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,
"
Expand Down Expand Up @@ -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::<Vec<String>>()
.join("\n"),
);

src.push_str(&format!("{access} static class {name}World\n"));
src.push_str("{");

Expand All @@ -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::<Vec<String>>()
.join("\n"),
);

files.push(&format!("{name}.cs"), indent(&src).as_bytes());

let generate_stub = |name: String, files: &mut Files, stubs: Stubs| {
Expand Down Expand Up @@ -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} {{
Expand Down Expand Up @@ -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::<Vec<String>>()
.join("\n"),
);

files.push(&format!("{full_name}.cs"), indent(&body).as_bytes());
Expand All @@ -812,15 +862,21 @@ impl WorldGenerator for CSharp {
let class_name = interface_name.strip_prefix("I").unwrap();
let body = format!(
"{header}
{CSHARP_IMPORTS}
{0}
namespace {namespace}
{{
{access} static class {class_name}Interop {{
{body}
}}
}}
"
",
fragments
.iter()
.flat_map(|f| &f.interop_usings)
.map(|s| "using ".to_owned() + s + ";\n")
.collect::<Vec<String>>()
.join(""),
);

files.push(
Expand All @@ -845,6 +901,8 @@ struct InterfaceGenerator<'a> {
resolve: &'a Resolve,
name: &'a str,
direction: Direction,
usings: HashSet<String>,
interop_usings: HashSet<String>,
}

impl InterfaceGenerator<'_> {
Expand Down Expand Up @@ -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,
});
}

Expand All @@ -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,
});
}

Expand Down Expand Up @@ -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
};

Expand Down Expand Up @@ -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#"
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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#"
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 1739caf

Please sign in to comment.