Skip to content

Commit 4992ae6

Browse files
silesmoTimmy Silesmo
andauthored
C# Fixes wasi http header bug and adds a test for it (#1215)
* Fixes wasi http header bug and adds a test for it * Fixes alignment * refactored test to work with new test structure * fmt * Fixes review comments and allows mac builds pointing to locally built ILC * fmt * fixed go test * reverted macos changes and moved to separate pr * Fixed int to nint that I had missed * fmt * Changed int to nint --------- Co-authored-by: Timmy Silesmo <timmy@raybrowser.com>
1 parent 2ca3b8c commit 4992ae6

File tree

15 files changed

+513
-192
lines changed

15 files changed

+513
-192
lines changed

crates/csharp/src/function.rs

Lines changed: 361 additions & 164 deletions
Large diffs are not rendered by default.

crates/csharp/src/interface.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -600,12 +600,12 @@ impl InterfaceGenerator<'_> {
600600
fn gen_import_src(
601601
&mut self,
602602
func: &Function,
603-
results: &Vec<TypeId>,
603+
results: &[TypeId],
604604
parameter_type: ParameterType,
605605
) -> (String, String) {
606606
let mut bindgen = FunctionBindgen::new(
607607
self,
608-
&func.item_name(),
608+
func.item_name(),
609609
&func.kind,
610610
func.params
611611
.iter()
@@ -618,7 +618,7 @@ impl InterfaceGenerator<'_> {
618618
}
619619
})
620620
.collect(),
621-
results.clone(),
621+
results.to_vec(),
622622
parameter_type,
623623
func.result,
624624
);
@@ -772,7 +772,7 @@ var {async_status_var} = {raw_name}({wasm_params});
772772

773773
let mut bindgen = FunctionBindgen::new(
774774
self,
775-
&func.item_name(),
775+
func.item_name(),
776776
&func.kind,
777777
(0..sig.params.len()).map(|i| format!("p{i}")).collect(),
778778
results,
@@ -1281,7 +1281,7 @@ var {async_status_var} = {raw_name}({wasm_params});
12811281
Direction::Export => {
12821282
let prefix = key
12831283
.map(|s| format!("{}#", self.resolve.name_world_key(s)))
1284-
.unwrap_or_else(String::new);
1284+
.unwrap_or_default();
12851285

12861286
uwrite!(
12871287
self.csharp_interop_src,

crates/csharp/src/world_generator.rs

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub struct CSharp {
3535
pub(crate) needs_rep_table: bool,
3636
pub(crate) needs_wit_exception: bool,
3737
pub(crate) needs_async_support: bool,
38+
pub(crate) needs_align_stack_ptr: bool,
3839
pub(crate) interface_fragments: HashMap<String, InterfaceTypeAndFragments>,
3940
pub(crate) world_fragments: Vec<InterfaceFragment>,
4041
pub(crate) sizes: SizeAlign,
@@ -388,7 +389,7 @@ impl WorldGenerator for CSharp {
388389
let world = &resolve.worlds[id];
389390
let world_namespace = self.qualifier();
390391
let world_namespace = world_namespace.strip_suffix(".").unwrap();
391-
let namespace = format!("{world_namespace}");
392+
let namespace = world_namespace;
392393
let name = world.name.to_upper_camel_case();
393394

394395
let version = env!("CARGO_PKG_VERSION");
@@ -623,13 +624,28 @@ impl WorldGenerator for CSharp {
623624
src.push_str(&ret_area_str);
624625
}
625626

627+
if self.needs_align_stack_ptr {
628+
uwrite!(
629+
src,
630+
"
631+
{access} static class MemoryHelper
632+
{{
633+
{access} static unsafe void* AlignStackPtr(void* stackAddress, uint alignment)
634+
{{
635+
return (void*)(((nint)stackAddress) + ((int)alignment - 1) & -(int)alignment);
636+
}}
637+
}}
638+
"
639+
);
640+
}
641+
626642
if self.needs_rep_table {
627-
src.push_str("\n");
643+
src.push('\n');
628644
src.push_str(include_str!("RepTable.cs"));
629645
}
630646

631647
if !&self.world_fragments.is_empty() {
632-
src.push_str("\n");
648+
src.push('\n');
633649

634650
if self
635651
.world_fragments
@@ -712,7 +728,7 @@ impl WorldGenerator for CSharp {
712728
src.push_str(&include_str!("FutureCommonSupport.cs"));
713729
}
714730

715-
src.push_str("\n");
731+
src.push('\n');
716732

717733
src.push_str("}\n");
718734

@@ -740,11 +756,11 @@ impl WorldGenerator for CSharp {
740756

741757
let (fragments, fully_qualified_namespace) = match stubs {
742758
Stubs::World(fragments) => {
743-
let fully_qualified_namespace = format!("{namespace}");
759+
let fully_qualified_namespace = namespace.to_string();
744760
(fragments, fully_qualified_namespace)
745761
}
746762
Stubs::Interface(fragments) => {
747-
let fully_qualified_namespace = format!("{stub_namespace}");
763+
let fully_qualified_namespace = stub_namespace.clone();
748764
(fragments, fully_qualified_namespace)
749765
}
750766
};
@@ -818,13 +834,9 @@ impl WorldGenerator for CSharp {
818834
// intended to be used non-interactively at link time, the
819835
// linker will have no additional information to resolve such
820836
// ambiguity.
821-
let (resolve, world) =
822-
wit_parser::decoding::decode_world(&wit_component::metadata::encode(
823-
&resolve,
824-
id,
825-
self.opts.string_encoding,
826-
None,
827-
)?)?;
837+
let (resolve, world) = wit_parser::decoding::decode_world(
838+
&wit_component::metadata::encode(resolve, id, self.opts.string_encoding, None)?,
839+
)?;
828840
let pkg = resolve.worlds[world].package.unwrap();
829841

830842
let mut printer = WitPrinter::default();
@@ -878,7 +890,7 @@ impl WorldGenerator for CSharp {
878890
.collect::<Vec<_>>()
879891
.join("\n");
880892

881-
if body.len() > 0 {
893+
if !body.is_empty() {
882894
let body = format!(
883895
"{header}
884896
@@ -943,11 +955,12 @@ fn export_types(r#gen: &mut InterfaceGenerator, types: &[(&str, TypeId)]) {
943955
// We cant use "StructLayout.Pack" as dotnet will use the minimum of the type and the "Pack" field,
944956
// so for byte it would always use 1 regardless of the "Pack".
945957
pub fn dotnet_aligned_array(array_size: usize, required_alignment: usize) -> (usize, String) {
958+
let num_elements = array_size.div_ceil(required_alignment);
946959
match required_alignment {
947-
1 => (array_size, "byte".to_owned()),
948-
2 => ((array_size + 1) / 2, "ushort".to_owned()),
949-
4 => ((array_size + 3) / 4, "uint".to_owned()),
950-
8 => ((array_size + 7) / 8, "ulong".to_owned()),
960+
1 => (num_elements, "byte".to_owned()),
961+
2 => (num_elements, "ushort".to_owned()),
962+
4 => (num_elements, "uint".to_owned()),
963+
8 => (num_elements, "ulong".to_owned()),
951964
_ => todo!("unsupported return_area_align {}", required_alignment),
952965
}
953966
}
@@ -1033,11 +1046,7 @@ fn interface_name(
10331046
);
10341047

10351048
if let Some(version) = &name.version {
1036-
let v = version
1037-
.to_string()
1038-
.replace('.', "_")
1039-
.replace('-', "_")
1040-
.replace('+', "_");
1049+
let v = version.to_string().replace(['.', '-', '+'], "_");
10411050
ns = format!("{}v{}.", ns, &v);
10421051
}
10431052
ns

tests/runtime/lists/runner.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,33 @@ void exports_runner_run() {
232232
runner_list_f32_free(&ret.f0);
233233
runner_list_f64_free(&ret.f1);
234234
}
235+
236+
{
237+
runner_tuple2_string_list_u8_t headers_data[2];
238+
runner_string_set(&headers_data[0].f0, "Content-Type");
239+
uint8_t val0[] = "text/plain";
240+
headers_data[0].f1.ptr = val0;
241+
headers_data[0].f1.len = 10;
242+
243+
runner_string_set(&headers_data[1].f0, "Content-Length");
244+
uint8_t val1[] = "9";
245+
headers_data[1].f1.ptr = val1;
246+
headers_data[1].f1.len = 1;
247+
248+
runner_list_tuple2_string_list_u8_t headers = { headers_data, 2 };
249+
runner_list_tuple2_string_list_u8_t result;
250+
test_lists_to_test_wasi_http_headers_roundtrip(&headers, &result);
251+
252+
assert(result.len == 2);
253+
assert(result.ptr[0].f0.len == 12);
254+
assert(memcmp(result.ptr[0].f0.ptr, "Content-Type", 12) == 0);
255+
assert(result.ptr[0].f1.len == 10);
256+
assert(memcmp(result.ptr[0].f1.ptr, "text/plain", 10) == 0);
257+
assert(result.ptr[1].f0.len == 14);
258+
assert(memcmp(result.ptr[1].f0.ptr, "Content-Length", 14) == 0);
259+
assert(result.ptr[1].f1.len == 1);
260+
assert(result.ptr[1].f1.ptr[0] == '9');
261+
262+
runner_list_tuple2_string_list_u8_free(&result);
263+
}
235264
}

tests/runtime/lists/runner.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,18 @@ void exports::runner::Run()
105105
std::vector<double>{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL}
106106
)
107107
));
108+
109+
{
110+
std::vector<uint8_t> val0 = {'t', 'e', 'x', 't', '/', 'p', 'l', 'a', 'i', 'n'};
111+
std::vector<uint8_t> val1 = {'9'};
112+
std::vector<std::tuple<std::string_view, std::span<const uint8_t>>> headers;
113+
headers.push_back(std::make_tuple(std::string_view("Content-Type"), std::span<const uint8_t>(val0)));
114+
headers.push_back(std::make_tuple(std::string_view("Content-Length"), std::span<const uint8_t>(val1)));
115+
auto result = WasiHttpHeadersRoundtrip(headers);
116+
assert(result.size() == 2);
117+
assert(equal(std::get<0>(result[0]), "Content-Type"));
118+
assert(equal(std::get<1>(result[0]), std::vector<uint8_t>{'t', 'e', 'x', 't', '/', 'p', 'l', 'a', 'i', 'n'}));
119+
assert(equal(std::get<0>(result[1]), "Content-Length"));
120+
assert(equal(std::get<1>(result[1]), std::vector<uint8_t>{'9'}));
121+
}
108122
}

tests/runtime/lists/runner.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,5 +159,15 @@ public static void Run()
159159
&& s[2] == double.NegativeInfinity
160160
&& s[3] == double.PositiveInfinity);
161161
}
162+
163+
{
164+
var headers = new List<(string, byte[])>() { ("Content-Type", Encoding.UTF8.GetBytes("text/plain")), ("Content-Length", Encoding.UTF8.GetBytes("Not found".Count().ToString())) };
165+
var result = IToTestImports.WasiHttpHeadersRoundtrip(headers);
166+
for (var i = 0; i < result.Count(); i++)
167+
{
168+
Debug.Assert(result[i].Item1 == headers[i].Item1);
169+
Debug.Assert(headers[i].Item2.SequenceEqual(result[i].Item2));
170+
}
171+
}
162172
}
163173
}

tests/runtime/lists/runner.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ func Run() {
3232
assertEqual(test.ListResult2(), "hello!")
3333
assert(slices.Equal(test.ListResult3(), []string{"hello,", "world!"}))
3434
assert(slices.Equal(test.ListRoundtrip([]uint8{}), []uint8{}))
35+
36+
{
37+
headers := []Tuple2[string, []uint8]{
38+
{"Content-Type", []uint8("text/plain")},
39+
{"Content-Length", []uint8("9")},
40+
}
41+
result := test.WasiHttpHeadersRoundtrip(headers)
42+
assertEqual(len(result), 2)
43+
assertEqual(result[0].F0, "Content-Type")
44+
assert(slices.Equal(result[0].F1, []uint8("text/plain")))
45+
assertEqual(result[1].F0, "Content-Length")
46+
assert(slices.Equal(result[1].F1, []uint8("9")))
47+
}
3548
}
3649

3750
func assertEqual[T comparable](a T, b T) {

tests/runtime/lists/runner.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,24 @@ impl Guest for Component {
162162
),
163163
);
164164
}
165+
166+
{
167+
let _guard = Guard::new();
168+
let headers = vec![
169+
("Content-Type".to_owned(), b"text/plain".to_vec()),
170+
(
171+
"Content-Length".to_owned(),
172+
"Not found".len().to_string().into_bytes(),
173+
),
174+
];
175+
let result = wasi_http_headers_roundtrip(&headers);
176+
assert_eq!(result[0].0, "Content-Type");
177+
assert_eq!(result[0].1, b"text/plain");
178+
assert_eq!(result[1].0, "Content-Length");
179+
assert_eq!(
180+
result[1].1,
181+
"Not found".len().to_string().into_bytes()
182+
);
183+
}
165184
}
166185
}

tests/runtime/lists/test.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,7 @@ void exports_test_lists_to_test_list_minmax_float(test_list_f32_t *a, test_list_
161161
ret->f0 = *a;
162162
ret->f1 = *b;
163163
}
164+
165+
void exports_test_lists_to_test_wasi_http_headers_roundtrip(test_list_tuple2_string_list_u8_t *a, test_list_tuple2_string_list_u8_t *ret0) {
166+
*ret0 = *a;
167+
}

tests/runtime/lists/test.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,7 @@ std::tuple<wit::vector<uint64_t>, wit::vector<int64_t>> exports::test::lists::to
129129
std::tuple<wit::vector<float>, wit::vector<double>> exports::test::lists::to_test::ListMinmaxFloat(wit::vector<float> a, wit::vector<double> b) {
130130
return std::make_tuple(std::move(a), std::move(b));
131131
}
132+
133+
wit::vector<std::tuple<wit::string, wit::vector<uint8_t>>> exports::test::lists::to_test::WasiHttpHeadersRoundtrip(wit::vector<std::tuple<wit::string, wit::vector<uint8_t>>> a) {
134+
return a;
135+
}

0 commit comments

Comments
 (0)