Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading