Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(c#): strings code generation test #760

Merged
merged 3 commits into from
Dec 1, 2023
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
129 changes: 97 additions & 32 deletions crates/csharp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ impl WorldGenerator for CSharp {
gen.import(&resolve.name_world_key(key), func);
}

gen.add_import_return_area();
gen.add_interface_fragment(false);
}

Expand Down Expand Up @@ -181,6 +182,7 @@ impl WorldGenerator for CSharp {
gen.export(func, Some(key));
}

gen.add_export_return_area();
gen.add_interface_fragment(true);
Ok(())
}
Expand Down Expand Up @@ -589,6 +591,99 @@ impl InterfaceGenerator<'_> {
});
}

fn add_import_return_area(&mut self) {
let mut ret_struct_type = String::new();
if self.gen.return_area_size > 0 {
uwrite!(
ret_struct_type,
r#"
private unsafe struct ReturnArea
{{
private int GetS32(IntPtr ptr, int offset)
{{
var span = new Span<byte>((void*)ptr, {});

return BitConverter.ToInt32(span.Slice(offset, 4));
}}

public string GetUTF8String(IntPtr ptr)
{{
return Encoding.UTF8.GetString((byte*)GetS32(ptr, 0), GetS32(ptr, 4));
}}

}}

[ThreadStatic]
[FixedAddressValueType]
private static ReturnArea returnArea;
"#,
self.gen.return_area_size
);
}

uwrite!(
self.csharp_interop_src,
r#"
{ret_struct_type}
"#
);
}

fn add_export_return_area(&mut self) {
// Declare a statically-allocated return area, if needed. We only do
// this for export bindings, because import bindings allocate their
// return-area on the stack.
if self.gen.return_area_size > 0 {
let mut ret_area_str = String::new();

uwrite!(
ret_area_str,
"
[InlineArray({})]
[StructLayout(LayoutKind.Sequential, Pack = {})]
private struct ReturnArea
{{
private byte buffer;

private int GetS32(int offset)
{{
ReadOnlySpan<byte> span = this;

return BitConverter.ToInt32(span.Slice(offset, 4));
}}

public void SetS32(int offset, int value)
{{
Span<byte> span = this;

BitConverter.TryWriteBytes(span.Slice(offset), value);
}}

internal unsafe int AddrOfBuffer()
{{
fixed(byte* ptr = &buffer)
{{
return (int)ptr;
}}
}}

public unsafe string GetUTF8String(int p0, int p1)
{{
return Encoding.UTF8.GetString((byte*)p0, p1);
}}
}}

[ThreadStatic]
private static ReturnArea returnArea = default;
",
self.gen.return_area_size,
self.gen.return_area_align,
);

self.csharp_interop_src.push_str(&ret_area_str);
}
}

fn add_world_fragment(self) {
self.gen.world_fragments.push(InterfaceFragment {
csharp_src: self.src,
Expand Down Expand Up @@ -675,39 +770,9 @@ impl InterfaceGenerator<'_> {
"#
);

let mut ret_struct_type = String::new();
if self.gen.return_area_size > 0 {
uwrite!(
ret_struct_type,
r#"
private unsafe struct ReturnArea
{{
private int GetS32(IntPtr ptr, int offset)
{{
var span = new Span<byte>((void*)ptr, {});

return BitConverter.ToInt32(span.Slice(offset, 4));
}}

public string GetUTF8String(IntPtr ptr)
{{
return Encoding.UTF8.GetString((byte*)GetS32(ptr, 0), GetS32(ptr, 4));
}}

}}

[ThreadStatic]
[FixedAddressValueType]
private static ReturnArea returnArea;
"#,
self.gen.return_area_size
);
}

uwrite!(
self.csharp_interop_src,
r#"
{ret_struct_type}
internal static unsafe {result_type} {camel_name}({params})
{{
{src}
Expand Down Expand Up @@ -1458,7 +1523,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
self.src,
"
var {result_var} = {op};
IntPtr {interop_string} = InteropString.FromString({result_var}, out int length);"
IntPtr {interop_string} = InteropString.FromString({result_var}, out int length{result_var});"
);

//TODO: Oppertunity to optimize and not reallocate every call
Expand All @@ -1467,7 +1532,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
} else {
results.push(format!("{interop_string}.ToInt32()"));
}
results.push(format!("length"));
results.push(format!("length{result_var}"));

self.gen.gen.needs_interop_string = true;
}
Expand Down
1 change: 1 addition & 0 deletions crates/csharp/tests/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ macro_rules! codegen_test {
"simple-lists",
"small-anonymous",
"strings",
"smoke-default",
"unused-import",
"use-across-interfaces",
"variants",
Expand Down
24 changes: 24 additions & 0 deletions tests/runtime/strings/wasm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using System.Diagnostics;
using wit_strings.Wit.imports.test.strings.Imports;

namespace wit_strings;

public class StringsWorldImpl : StringsWorld
{
public static void TestImports()
{
ImportsInterop.TakeBasic("latin utf16");
Debug.Assert(ImportsInterop.ReturnUnicode() == "🚀🚀🚀 𠈄𓀀");
}

public static string ReturnEmpty()
{
return "";
}

public static string Roundtrip(string s)
{
return s;
}
}
Loading