diff --git a/crates/csharp/src/csproj.rs b/crates/csharp/src/csproj.rs
index 1d9d86aaa..c94b39064 100644
--- a/crates/csharp/src/csproj.rs
+++ b/crates/csharp/src/csproj.rs
@@ -1,5 +1,5 @@
use anyhow::Result;
-use std::{fs, path::PathBuf};
+use std::{env, fs, path::PathBuf};
use heck::ToUpperCamelCase;
@@ -103,21 +103,36 @@ impl CSProjectLLVMBuilder {
let os = match std::env::consts::OS {
"windows" => "win",
"linux" => std::env::consts::OS,
+ "macos" => "osx",
other => todo!("OS {} not supported", other),
};
+ let arch = match std::env::consts::ARCH {
+ "aarch64" => "arm64",
+ "x86_64" => "x64",
+ other => todo!("ARCH {} not supported", other),
+ };
+
+ let ilc_version = env::var("ILC_VERSION").unwrap_or_else(|_| "10.0.0-*".to_string());
+
csproj.push_str(
&format!(
r#"
-
-
+
+
"#),
);
+ let local_source = match env::var("ILC_PACKAGES_PATH") {
+ Ok(path) => format!(r#""#),
+ Err(_) => String::new(),
+ };
+
fs::write(
self.dir.join("nuget.config"),
- r#"
+ format!(
+ r#"
@@ -126,11 +141,12 @@ impl CSProjectLLVMBuilder {
+ {local_source}
-
- "#,
+ "#
+ ),
)?;
}
diff --git a/crates/csharp/src/function.rs b/crates/csharp/src/function.rs
index 8d7ab4a1e..49d5049c0 100644
--- a/crates/csharp/src/function.rs
+++ b/crates/csharp/src/function.rs
@@ -451,24 +451,102 @@ impl Bindgen for FunctionBindgen<'_, '_> {
}
.to_owned()
})),
- Instruction::I32Load { offset }
- | Instruction::PointerLoad { offset }
- | Instruction::LengthLoad { offset } => results.push(format!("global::System.BitConverter.ToInt32(new global::System.Span((byte*){} + {offset}, 4))",operands[0],offset = offset.size_wasm32())),
- Instruction::I32Load8U { offset } => results.push(format!("new global::System.Span((byte*){} + {offset}, 1)[0]",operands[0],offset = offset.size_wasm32())),
- Instruction::I32Load8S { offset } => results.push(format!("(sbyte)new global::System.Span((byte*){} + {offset}, 1)[0]",operands[0],offset = offset.size_wasm32())),
- Instruction::I32Load16U { offset } => results.push(format!("global::System.BitConverter.ToUInt16(new global::System.Span((byte*){} + {offset}, 2))",operands[0],offset = offset.size_wasm32())),
- Instruction::I32Load16S { offset } => results.push(format!("global::System.BitConverter.ToInt16(new global::System.Span((byte*){} + {offset}, 2))",operands[0],offset = offset.size_wasm32())),
- Instruction::I64Load { offset } => results.push(format!("global::System.BitConverter.ToInt64(new global::System.Span((byte*){} + {offset}, 8))",operands[0],offset = offset.size_wasm32())),
- Instruction::F32Load { offset } => results.push(format!("global::System.BitConverter.ToSingle(new global::System.Span((byte*){} + {offset}, 4))",operands[0],offset = offset.size_wasm32())),
- Instruction::F64Load { offset } => results.push(format!("global::System.BitConverter.ToDouble(new global::System.Span((byte*){} + {offset}, 8))",operands[0],offset = offset.size_wasm32())),
- Instruction::I32Store { offset }
- | Instruction::PointerStore { offset }
- | Instruction::LengthStore { offset } => uwriteln!(self.src, "global::System.BitConverter.TryWriteBytes(new global::System.Span((byte*){} + {offset}, 4), {});", operands[1], operands[0],offset = offset.size_wasm32()),
- Instruction::I32Store8 { offset } => uwriteln!(self.src, "*(byte*)({} + {offset}) = (byte){};", operands[1], operands[0],offset = offset.size_wasm32()),
- Instruction::I32Store16 { offset } => uwriteln!(self.src, "global::System.BitConverter.TryWriteBytes(new global::System.Span((byte*){} + {offset}, 2), (short){});", operands[1], operands[0],offset = offset.size_wasm32()),
- Instruction::I64Store { offset } => uwriteln!(self.src, "global::System.BitConverter.TryWriteBytes(new global::System.Span((byte*){} + {offset}, 8), unchecked((long){}));", operands[1], operands[0],offset = offset.size_wasm32()),
- Instruction::F32Store { offset } => uwriteln!(self.src, "global::System.BitConverter.TryWriteBytes(new global::System.Span((byte*){} + {offset}, 4), unchecked((float){}));", operands[1], operands[0],offset = offset.size_wasm32()),
- Instruction::F64Store { offset } => uwriteln!(self.src, "global::System.BitConverter.TryWriteBytes(new global::System.Span((byte*){} + {offset}, 8), unchecked((double){}));", operands[1], operands[0],offset = offset.size_wasm32()),
+ Instruction::I32Load { offset } | Instruction::LengthLoad { offset } => {
+ results.push(format!(
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0]",
+ operands[0],
+ offset = offset.size_wasm32()
+ ))
+ }
+ Instruction::PointerLoad { offset } => results.push(format!(
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0]",
+ operands[0],
+ offset = offset.size_wasm32()
+ )),
+ Instruction::I32Load8U { offset } => results.push(format!(
+ "new global::System.Span((byte*){} + {offset}, 1)[0]",
+ operands[0],
+ offset = offset.size_wasm32()
+ )),
+ Instruction::I32Load8S { offset } => results.push(format!(
+ "(sbyte)new global::System.Span((byte*){} + {offset}, 1)[0]",
+ operands[0],
+ offset = offset.size_wasm32()
+ )),
+ Instruction::I32Load16U { offset } => results.push(format!(
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0]",
+ operands[0],
+ offset = offset.size_wasm32()
+ )),
+ Instruction::I32Load16S { offset } => results.push(format!(
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0]",
+ operands[0],
+ offset = offset.size_wasm32()
+ )),
+ Instruction::I64Load { offset } => results.push(format!(
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0]",
+ operands[0],
+ offset = offset.size_wasm32()
+ )),
+ Instruction::F32Load { offset } => results.push(format!(
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0]",
+ operands[0],
+ offset = offset.size_wasm32()
+ )),
+ Instruction::F64Load { offset } => results.push(format!(
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0]",
+ operands[0],
+ offset = offset.size_wasm32()
+ )),
+ Instruction::I32Store { offset } | Instruction::LengthStore { offset } => uwriteln!(
+ self.src,
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0] = {};",
+ operands[1],
+ operands[0],
+ offset = offset.size_wasm32()
+ ),
+ Instruction::PointerStore { offset } => uwriteln!(
+ self.src,
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0] = (nint){};",
+ operands[1],
+ operands[0],
+ offset = offset.size_wasm32()
+ ),
+ Instruction::I32Store8 { offset } => uwriteln!(
+ self.src,
+ "*(byte*)({} + {offset}) = (byte){};",
+ operands[1],
+ operands[0],
+ offset = offset.size_wasm32()
+ ),
+ Instruction::I32Store16 { offset } => uwriteln!(
+ self.src,
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0] = (short){};",
+ operands[1],
+ operands[0],
+ offset = offset.size_wasm32()
+ ),
+ Instruction::I64Store { offset } => uwriteln!(
+ self.src,
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0] = unchecked((long){});",
+ operands[1],
+ operands[0],
+ offset = offset.size_wasm32()
+ ),
+ Instruction::F32Store { offset } => uwriteln!(
+ self.src,
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0] = unchecked((float){});",
+ operands[1],
+ operands[0],
+ offset = offset.size_wasm32()
+ ),
+ Instruction::F64Store { offset } => uwriteln!(
+ self.src,
+ "new global::System.Span((void*)((byte*){} + {offset}), 1)[0] = unchecked((double){});",
+ operands[1],
+ operands[0],
+ offset = offset.size_wasm32()
+ ),
Instruction::I64FromU64 => results.push(format!("unchecked((long)({}))", operands[0])),
Instruction::I32FromChar => results.push(format!("((int){})", operands[0])),
@@ -494,9 +572,12 @@ impl Bindgen for FunctionBindgen<'_, '_> {
| Instruction::S32FromI32
| Instruction::S64FromI64 => results.push(operands[0].clone()),
- Instruction::Bitcasts { casts } => {
- results.extend(casts.iter().zip(operands).map(|(cast, op)| perform_cast(op, cast)))
- }
+ Instruction::Bitcasts { casts } => results.extend(
+ casts
+ .iter()
+ .zip(operands)
+ .map(|(cast, op)| perform_cast(op, cast)),
+ ),
Instruction::I32FromBool => {
results.push(format!("({} ? 1 : 0)", operands[0]));
@@ -511,14 +592,11 @@ impl Bindgen for FunctionBindgen<'_, '_> {
if flags.flags.len() > 32 {
results.push(format!(
"unchecked((int)(((long){}) & uint.MaxValue))",
- operands[0].to_string()
- ));
- results.push(format!(
- "unchecked(((int)((long){} >> 32)))",
- operands[0].to_string()
+ operands[0]
));
+ results.push(format!("unchecked(((int)((long){} >> 32)))", operands[0]));
} else {
- results.push(format!("(int){}", operands[0].to_string()));
+ results.push(format!("(int){}", operands[0]));
}
}
@@ -531,9 +609,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
if flags.flags.len() > 32 {
results.push(format!(
"({})(unchecked((uint)({})) | (ulong)(unchecked((uint)({}))) << 32)",
- qualified_type_name,
- operands[0].to_string(),
- operands[1].to_string()
+ qualified_type_name, operands[0], operands[1]
));
} else {
results.push(format!("({})({})", qualified_type_name, operands[0]))
@@ -555,7 +631,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
let mut result = format!("new {qualified_type_name} (\n");
result.push_str(&operands.join(", "));
- result.push_str(")");
+ result.push(')');
results.push(result);
}
@@ -627,52 +703,54 @@ impl Bindgen for FunctionBindgen<'_, '_> {
let declarations = lowered
.iter()
.zip(lowered_types.iter())
- .map(|(lowered, ty)| format!("{} {lowered};", crate::world_generator::wasm_type(*ty)))
+ .map(|(lowered, ty)| {
+ format!("{} {lowered};", crate::world_generator::wasm_type(*ty))
+ })
.collect::>()
.join("\n");
let op = &operands[0];
let nesting = if let Type::Id(id) = payload {
- matches!(&self.interface_gen.resolve.types[*id].kind, TypeDefKind::Option(_))
+ matches!(
+ &self.interface_gen.resolve.types[*id].kind,
+ TypeDefKind::Option(_)
+ )
} else {
false
};
- let mut block = |ty: Option<&Type>, Block { body, results, .. }, payload, nesting| {
- let payload = if let Some(ty) = self.interface_gen.non_empty_type(ty) {
- let ty = self.interface_gen.type_name_with_qualifier(ty, true);
- if nesting {
- format!("var {payload} = {op}.Value;")
+ let mut block =
+ |ty: Option<&Type>, Block { body, results, .. }, payload, nesting| {
+ let payload = if let Some(ty) = self.interface_gen.non_empty_type(ty) {
+ let ty = self.interface_gen.type_name_with_qualifier(ty, true);
+ if nesting {
+ format!("var {payload} = {op}.Value;")
+ } else {
+ format!("var {payload} = ({ty}) {op};")
+ }
} else {
- format!("var {payload} = ({ty}) {op};")
- }
- } else {
- String::new()
- };
+ String::new()
+ };
- let assignments = lowered
- .iter()
- .zip(&results)
- .map(|(lowered, result)| format!("{lowered} = {result};\n"))
- .collect::>()
- .concat();
+ let assignments = lowered
+ .iter()
+ .zip(&results)
+ .map(|(lowered, result)| format!("{lowered} = {result};\n"))
+ .collect::>()
+ .concat();
- format!(
- "{payload}
+ format!(
+ "{payload}
{body}
{assignments}"
- )
- };
+ )
+ };
let none = block(None, none, none_payload, nesting);
let some = block(Some(payload), some, some_payload, nesting);
- let test = if nesting {
- ".HasValue"
- } else {
- " != null"
- };
+ let test = if nesting { ".HasValue" } else { " != null" };
uwrite!(
self.src,
@@ -692,12 +770,17 @@ impl Bindgen for FunctionBindgen<'_, '_> {
let some = self.blocks.pop().unwrap();
let _none = self.blocks.pop().unwrap();
- let ty = self.interface_gen.type_name_with_qualifier(&Type::Id(*ty), true);
+ let ty = self
+ .interface_gen
+ .type_name_with_qualifier(&Type::Id(*ty), true);
let lifted = self.locals.tmp("lifted");
let op = &operands[0];
let nesting = if let Type::Id(id) = payload {
- matches!(&self.interface_gen.resolve.types[*id].kind, TypeDefKind::Option(_))
+ matches!(
+ &self.interface_gen.resolve.types[*id].kind,
+ TypeDefKind::Option(_)
+ )
} else {
false
};
@@ -762,7 +845,9 @@ impl Bindgen for FunctionBindgen<'_, '_> {
Instruction::EnumLower { .. } => results.push(format!("(int){}", operands[0])),
Instruction::EnumLift { ty, .. } => {
- let t = self.interface_gen.type_name_with_qualifier(&Type::Id(*ty), true);
+ let t = self
+ .interface_gen
+ .type_name_with_qualifier(&Type::Id(*ty), true);
let op = &operands[0];
results.push(format!("({t}){op}"));
@@ -786,7 +871,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
item_to_pin: list.clone(),
ptr_name: ptr.clone(),
});
- }else if !self.is_block && self.parameter_type == ParameterType::Memory {
+ } else if !self.is_block && self.parameter_type == ParameterType::Memory {
self.fixed_statments.push(Fixed {
item_to_pin: format!("{list}.Span"),
ptr_name: ptr.clone(),
@@ -807,13 +892,18 @@ impl Bindgen for FunctionBindgen<'_, '_> {
"
);
}
- results.push(format!("(nint){ptr}"));
+ results.push(format!("(int){ptr}"));
results.push(format!("({list}).Length"));
}
Direction::Export => {
let (_, ty) = list_element_info(element);
let address = self.locals.tmp("address");
- let size = self.interface_gen.csharp_gen.sizes.size(element).size_wasm32();
+ let size = self
+ .interface_gen
+ .csharp_gen
+ .sizes
+ .size(element)
+ .size_wasm32();
let byte_length = self.locals.tmp("byteLength");
uwrite!(
self.src,
@@ -899,7 +989,8 @@ impl Bindgen for FunctionBindgen<'_, '_> {
let op0 = &operands[0];
let op1 = &operands[1];
- let get_str = format!("global::System.Text.Encoding.UTF8.GetString((byte*){op0}, {op1})");
+ let get_str =
+ format!("global::System.Text.Encoding.UTF8.GetString((byte*){op0}, {op1})");
uwriteln!(
self.src,
@@ -924,30 +1015,46 @@ impl Bindgen for FunctionBindgen<'_, '_> {
assert!(block_results.is_empty());
let list = &operands[0];
- let size = self.interface_gen.csharp_gen.sizes.size(element).size_wasm32();
+ let size = self
+ .interface_gen
+ .csharp_gen
+ .sizes
+ .size(element)
+ .size_wasm32();
let ty = self.interface_gen.type_name_with_qualifier(element, true);
let index = self.locals.tmp("index");
let address = self.locals.tmp("address");
let buffer_size = self.locals.tmp("bufferSize");
//TODO: wasm64
- let align = self.interface_gen.csharp_gen.sizes.align(element).align_wasm32();
+ let align = self
+ .interface_gen
+ .csharp_gen
+ .sizes
+ .align(element)
+ .align_wasm32();
- let (array_size, element_type) = crate::world_generator::dotnet_aligned_array(
- size,
- align,
- );
+ let (array_size, element_type) =
+ crate::world_generator::dotnet_aligned_array(size, align);
let ret_area = self.locals.tmp("retArea");
+ let array_size = if align > 1 {
+ // Add one additional element in case the starting address is not aligned
+ format!("{array_size} * {list}.Count + 1")
+ } else {
+ format!("{array_size} * {list}.Count")
+ };
+
match realloc {
None => {
self.needs_cleanup = true;
+ self.interface_gen.csharp_gen.needs_align_stack_ptr = true;
uwrite!(self.src,
"
void* {address};
if (({size} * {list}.Count) < 1024) {{
- var {ret_area} = stackalloc {element_type}[({array_size}*{list}.Count)+1];
- {address} = (void*)(((int){ret_area}) + ({align} - 1) & -{align});
+ var {ret_area} = stackalloc {element_type}[{array_size}];
+ {address} = MemoryHelper.AlignStackPtr({ret_area}, {align});
}}
else
{{
@@ -969,7 +1076,8 @@ impl Bindgen for FunctionBindgen<'_, '_> {
}
}
- uwrite!(self.src,
+ uwrite!(
+ self.src,
"
for (int {index} = 0; {index} < {list}.Count; ++{index}) {{
{ty} {block_element} = {list}[{index}];
@@ -994,7 +1102,12 @@ impl Bindgen for FunctionBindgen<'_, '_> {
let length = &operands[1];
let array = self.locals.tmp("array");
let ty = self.interface_gen.type_name_with_qualifier(element, true);
- let size = self.interface_gen.csharp_gen.sizes.size(element).size_wasm32();
+ let size = self
+ .interface_gen
+ .csharp_gen
+ .sizes
+ .size(element)
+ .size_wasm32();
let index = self.locals.tmp("index");
let result = match &block_results[..] {
@@ -1038,9 +1151,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
assignment
}
- [] => {
- String::new()
- }
+ [] => String::new(),
_ => unreachable!(),
};
@@ -1050,17 +1161,29 @@ impl Bindgen for FunctionBindgen<'_, '_> {
let (_namespace, interface_name) =
&CSharp::get_class_name_from_qualified_name(self.interface_gen.name);
- let mut interop_name = format!("{}ImportsInterop", interface_name.strip_prefix("I").unwrap()
- .strip_suffix(if self.interface_gen.direction == Direction::Import { "Imports" } else { "Exports" }).unwrap().to_upper_camel_case());
+ let mut interop_name = format!(
+ "{}ImportsInterop",
+ interface_name
+ .strip_prefix("I")
+ .unwrap()
+ .strip_suffix(if self.interface_gen.direction == Direction::Import {
+ "Imports"
+ } else {
+ "Exports"
+ })
+ .unwrap()
+ .to_upper_camel_case()
+ );
- if self.interface_gen.is_world && self.interface_gen.direction == Direction::Import {
+ if self.interface_gen.is_world && self.interface_gen.direction == Direction::Import
+ {
interop_name = format!("Imports.{interop_name}");
}
let resource_type_name = match self.kind {
- FunctionKind::Method(resource_type_id) |
- FunctionKind::Static(resource_type_id) |
- FunctionKind::Constructor(resource_type_id) => {
+ FunctionKind::Method(resource_type_id)
+ | FunctionKind::Static(resource_type_id)
+ | FunctionKind::Constructor(resource_type_id) => {
format!(
".{}",
self.interface_gen.csharp_gen.all_resources[resource_type_id]
@@ -1108,21 +1231,26 @@ impl Bindgen for FunctionBindgen<'_, '_> {
let is_async = InterfaceGenerator::is_async(self.kind);
match self.kind {
FunctionKind::Constructor(id) => {
- let target = self.interface_gen.csharp_gen.all_resources[id].export_impl_name();
+ let target =
+ self.interface_gen.csharp_gen.all_resources[id].export_impl_name();
let ret = self.locals.tmp("ret");
uwriteln!(self.src, "var {ret} = new {target}({oper});");
results.push(ret);
}
_ => {
let target = match self.kind {
- FunctionKind::Static(id) |FunctionKind::AsyncStatic(id)=> self.interface_gen.csharp_gen.all_resources[id].export_impl_name(),
- FunctionKind::Method(_) |FunctionKind::AsyncMethod(_)=> operands[0].clone(),
- _ => format!("{class_name_root}Impl")
+ FunctionKind::Static(id) | FunctionKind::AsyncStatic(id) => {
+ self.interface_gen.csharp_gen.all_resources[id].export_impl_name()
+ }
+ FunctionKind::Method(_) | FunctionKind::AsyncMethod(_) => {
+ operands[0].clone()
+ }
+ _ => format!("{class_name_root}Impl"),
};
match func.result {
None => {
- if is_async{
+ if is_async {
uwriteln!(self.src, "var ret = {target}.{func_name}({oper});");
} else {
uwriteln!(self.src, "{target}.{func_name}({oper});");
@@ -1140,11 +1268,13 @@ impl Bindgen for FunctionBindgen<'_, '_> {
self.interface_gen.csharp_gen.needs_async_support = true;
let name = self.func_name.to_upper_camel_case();
let ret_param = match func.result {
- None => "",
- Some(_ty) => "ret.Result"
- };
+ None => "",
+ Some(_ty) => "ret.Result",
+ };
- uwriteln!(self.src, r#"if (ret.IsCompletedSuccessfully)
+ uwriteln!(
+ self.src,
+ r#"if (ret.IsCompletedSuccessfully)
{{
{name}TaskReturn({ret_param});
return (uint)CallbackCode.Exit;
@@ -1165,34 +1295,51 @@ impl Bindgen for FunctionBindgen<'_, '_> {
// TODO: Defer dropping borrowed resources until a result is returned.
ContextTask* contextTaskPtr = AsyncSupport.ContextGet();
return (uint)CallbackCode.Wait | (uint)(contextTaskPtr->WaitableSetHandle << 4);
- "#);
+ "#
+ );
}
- for (_, drop) in &self.resource_drops {
+ for (_, drop) in &self.resource_drops {
uwriteln!(self.src, "{drop}?.Dispose();");
}
}
Instruction::Return { amt, .. } => {
- if self.fixed_statments.len() > 0 {
- let fixed: String = self.fixed_statments.iter().map(|f| format!("{} = {}", f.ptr_name, f.item_to_pin)).collect::>().join(", ");
- self.src.insert_str(0, &format!("fixed (void* {fixed})
+ if !self.fixed_statments.is_empty() {
+ let fixed: String = self
+ .fixed_statments
+ .iter()
+ .map(|f| format!("{} = {}", f.ptr_name, f.item_to_pin))
+ .collect::>()
+ .join(", ");
+ self.src.insert_str(
+ 0,
+ &format!(
+ "fixed (void* {fixed})
{{
- "));
+ "
+ ),
+ );
}
if self.needs_cleanup {
self.src.insert_str(0, "var cleanups = new global::System.Collections.Generic.List();
");
- uwriteln!(self.src, "
+ uwriteln!(
+ self.src,
+ "
foreach (var cleanup in cleanups)
{{
cleanup();
- }}");
+ }}"
+ );
}
- if !matches!((self.interface_gen.direction, self.kind), (Direction::Import, FunctionKind::Constructor(_))) {
+ if !matches!(
+ (self.interface_gen.direction, self.kind),
+ (Direction::Import, FunctionKind::Constructor(_))
+ ) {
match *amt {
0 => (),
1 => {
@@ -1205,7 +1352,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
}
}
- if self.fixed_statments.len() > 0 {
+ if !self.fixed_statments.is_empty() {
uwriteln!(self.src, "}}");
}
}
@@ -1214,11 +1361,19 @@ impl Bindgen for FunctionBindgen<'_, '_> {
Instruction::GuestDeallocate { .. } => {
// the original alloc here comes from cabi_realloc implementation (wasi-libc in .net)
- uwriteln!(self.src, r#"global::System.Runtime.InteropServices.NativeMemory.Free((void*){});"#, operands[0]);
+ uwriteln!(
+ self.src,
+ r#"global::System.Runtime.InteropServices.NativeMemory.Free((void*){});"#,
+ operands[0]
+ );
}
Instruction::GuestDeallocateString => {
- uwriteln!(self.src, r#"global::System.Runtime.InteropServices.NativeMemory.Free((void*){});"#, operands[0]);
+ uwriteln!(
+ self.src,
+ r#"global::System.Runtime.InteropServices.NativeMemory.Free((void*){});"#,
+ operands[0]
+ );
}
Instruction::GuestDeallocateVariant { blocks } => {
@@ -1239,19 +1394,21 @@ impl Bindgen for FunctionBindgen<'_, '_> {
.collect::>()
.join("\n");
- let op = &operands[0];
+ let op = &operands[0];
- uwrite!(
- self.src,
- "
+ uwrite!(
+ self.src,
+ "
switch ({op}) {{
{cases}
}}
"
- );
+ );
}
- Instruction::GuestDeallocateList { element: element_type } => {
+ Instruction::GuestDeallocateList {
+ element: element_type,
+ } => {
let Block {
body,
results: block_results,
@@ -1262,7 +1419,12 @@ impl Bindgen for FunctionBindgen<'_, '_> {
let address = &operands[0];
let length = &operands[1];
- let size = self.interface_gen.csharp_gen.sizes.size(element_type).size_wasm32();
+ let size = self
+ .interface_gen
+ .csharp_gen
+ .sizes
+ .size(element_type)
+ .size_wasm32();
if !body.trim().is_empty() {
let index = self.locals.tmp("index");
@@ -1278,18 +1440,20 @@ impl Bindgen for FunctionBindgen<'_, '_> {
);
}
- uwriteln!(self.src, r#"global::System.Runtime.InteropServices.NativeMemory.Free((void*){});"#, operands[0]);
+ uwriteln!(
+ self.src,
+ r#"global::System.Runtime.InteropServices.NativeMemory.Free((void*){});"#,
+ operands[0]
+ );
}
- Instruction::HandleLower {
- handle,
- ..
- } => {
+ Instruction::HandleLower { handle, .. } => {
let (Handle::Own(ty) | Handle::Borrow(ty)) = handle;
let is_own = matches!(handle, Handle::Own(_));
let handle = self.locals.tmp("handle");
let id = dealias(self.interface_gen.resolve, *ty);
- let ResourceInfo { direction, .. } = &self.interface_gen.csharp_gen.all_resources[&id];
+ let ResourceInfo { direction, .. } =
+ &self.interface_gen.csharp_gen.all_resources[&id];
let op = &operands[0];
uwriteln!(self.src, "var {handle} = {op}.Handle;");
@@ -1303,7 +1467,8 @@ impl Bindgen for FunctionBindgen<'_, '_> {
Direction::Export => {
self.interface_gen.csharp_gen.needs_rep_table = true;
let local_rep = self.locals.tmp("localRep");
- let export_name = self.interface_gen.csharp_gen.all_resources[&id].export_impl_name();
+ let export_name =
+ self.interface_gen.csharp_gen.all_resources[&id].export_impl_name();
if is_own {
// Note that we set `{op}.Handle` to zero below to ensure that application code doesn't
// try to use the instance while the host has ownership. We'll set it back to non-zero
@@ -1329,27 +1494,27 @@ impl Bindgen for FunctionBindgen<'_, '_> {
}
}
}
- results.push(format!("{handle}"));
+ results.push(handle);
}
- Instruction::HandleLift {
- handle,
- ..
- } => {
+ Instruction::HandleLift { handle, .. } => {
let (Handle::Own(ty) | Handle::Borrow(ty)) = handle;
let is_own = matches!(handle, Handle::Own(_));
let mut resource = self.locals.tmp("resource");
let id = dealias(self.interface_gen.resolve, *ty);
- let ResourceInfo { direction, .. } = &self.interface_gen.csharp_gen.all_resources[&id];
+ let ResourceInfo { direction, .. } =
+ &self.interface_gen.csharp_gen.all_resources[&id];
let op = &operands[0];
match direction {
Direction::Import => {
- let import_name = self.interface_gen.type_name_with_qualifier(&Type::Id(id), true);
+ let import_name = self
+ .interface_gen
+ .type_name_with_qualifier(&Type::Id(id), true);
if let FunctionKind::Constructor(_) = self.kind {
resource = "this".to_owned();
- uwriteln!(self.src,"{resource}.Handle = {op};");
+ uwriteln!(self.src, "{resource}.Handle = {op};");
} else {
let var = if is_own { "var" } else { "" };
uwriteln!(
@@ -1364,7 +1529,8 @@ impl Bindgen for FunctionBindgen<'_, '_> {
Direction::Export => {
self.interface_gen.csharp_gen.needs_rep_table = true;
- let export_name = self.interface_gen.csharp_gen.all_resources[&id].export_impl_name();
+ let export_name =
+ self.interface_gen.csharp_gen.all_resources[&id].export_impl_name();
if is_own {
uwriteln!(
self.src,
@@ -1373,7 +1539,10 @@ impl Bindgen for FunctionBindgen<'_, '_> {
{resource}.Handle = {op};"
);
} else {
- uwriteln!(self.src, "var {resource} = ({export_name}) {export_name}.repTable.Get({op});");
+ uwriteln!(
+ self.src,
+ "var {resource} = ({export_name}) {export_name}.repTable.Get({op});"
+ );
}
self.resource_type_name = Some(export_name);
}
@@ -1386,21 +1555,29 @@ impl Bindgen for FunctionBindgen<'_, '_> {
}
Instruction::FutureLower { payload, ty: _ }
- | Instruction::StreamLower { payload, ty: _ }=> {
+ | Instruction::StreamLower { payload, ty: _ } => {
let op = &operands[0];
let generic_type_name = match payload {
- Some(generic_type) => {
- &self.interface_gen.type_name_with_qualifier(generic_type, false)
- }
- None => ""
+ Some(generic_type) => &self
+ .interface_gen
+ .type_name_with_qualifier(generic_type, false),
+ None => "",
};
match inst {
Instruction::FutureLower { .. } => {
- self.interface_gen.add_future(self.func_name, &generic_type_name, **payload);
+ self.interface_gen.add_future(
+ self.func_name,
+ &generic_type_name,
+ **payload,
+ );
}
_ => {
- self.interface_gen.add_stream(self.func_name, &generic_type_name, **payload);
+ self.interface_gen.add_stream(
+ self.func_name,
+ &generic_type_name,
+ **payload,
+ );
}
}
@@ -1412,49 +1589,62 @@ impl Bindgen for FunctionBindgen<'_, '_> {
}
Instruction::FutureLift { payload, ty: _ }
- | Instruction:: StreamLift { payload, ty: _ } => {
- let generic_type_name_with_qualifier = match payload {
- Some(generic_type) => {
- &self.interface_gen.type_name_with_qualifier(generic_type, true)
- }
- None => ""
+ | Instruction::StreamLift { payload, ty: _ } => {
+ let generic_type_name_with_qualifier = match payload {
+ Some(generic_type) => &self
+ .interface_gen
+ .type_name_with_qualifier(generic_type, true),
+ None => "",
};
- let generic_type_name = match payload {
- Some(generic_type) => {
- &self.interface_gen.type_name_with_qualifier(generic_type, false)
- }
- None => ""
+ let generic_type_name = match payload {
+ Some(generic_type) => &self
+ .interface_gen
+ .type_name_with_qualifier(generic_type, false),
+ None => "",
};
let upper_camel = generic_type_name.to_upper_camel_case();
let bracketed_generic = match payload {
Some(_) => {
format!("<{generic_type_name_with_qualifier}>")
}
- None => String::new()
+ None => String::new(),
};
- // let sig_type_name = "Void";
+ // let sig_type_name = "Void";
let reader_var = self.locals.tmp("reader");
let module = self.interface_gen.name;
- let (import_name, interface_name) = CSharp::get_class_name_from_qualified_name(module);
- let base_interface_name = interface_name
- .strip_prefix("I").unwrap();
+ let (import_name, interface_name) =
+ CSharp::get_class_name_from_qualified_name(module);
+ let base_interface_name = interface_name.strip_prefix("I").unwrap();
let future_stream_name = match inst {
- Instruction::FutureLift{payload: _, ty: _} => "Future",
- Instruction::StreamLift{payload: _, ty: _} => "Stream",
+ Instruction::FutureLift { payload: _, ty: _ } => "Future",
+ Instruction::StreamLift { payload: _, ty: _ } => "Stream",
_ => {
panic!("Unexpected instruction for lift");
}
};
- uwriteln!(self.src, "var {reader_var} = new {future_stream_name}Reader{bracketed_generic}({}, {import_name}.{base_interface_name}Interop.{future_stream_name}VTable{});", operands[0], upper_camel);
+ uwriteln!(
+ self.src,
+ "var {reader_var} = new {future_stream_name}Reader{bracketed_generic}({}, {import_name}.{base_interface_name}Interop.{future_stream_name}VTable{});",
+ operands[0],
+ upper_camel
+ );
results.push(reader_var);
match inst {
Instruction::FutureLift { .. } => {
- self.interface_gen.add_future(self.func_name, &generic_type_name, **payload);
+ self.interface_gen.add_future(
+ self.func_name,
+ &generic_type_name,
+ **payload,
+ );
}
_ => {
- self.interface_gen.add_stream(self.func_name, &generic_type_name, **payload);
+ self.interface_gen.add_stream(
+ self.func_name,
+ &generic_type_name,
+ **payload,
+ );
}
}
@@ -1467,8 +1657,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
| Instruction::FixedLengthListLift { .. }
| Instruction::FixedLengthListLower { .. }
| Instruction::FixedLengthListLowerToMemory { .. }
- | Instruction::FixedLengthListLiftFromMemory { .. }
- => {
+ | Instruction::FixedLengthListLiftFromMemory { .. } => {
dbg!(inst);
todo!()
}
@@ -1495,15 +1684,23 @@ impl Bindgen for FunctionBindgen<'_, '_> {
// to align the allocation via the stackalloc command, unlike with a fixed array where the pointer will be aligned.
// We get the final ptr to pass to the wasm runtime by shifting to the
// correctly aligned pointer (sometimes it can be already aligned).
+ let array_size = if self.import_return_pointer_area_align > 1 {
+ // Add one additional element in case the starting address is not aligned
+ array_size + 1
+ } else {
+ array_size
+ };
+
+ self.interface_gen.csharp_gen.needs_align_stack_ptr = true;
uwrite!(
self.src,
"
- var {ret_area} = stackalloc {element_type}[{array_size} + 1];
- var {ptr} = ((int){ret_area}) + ({align} - 1) & -{align};
+ var {ret_area} = stackalloc {element_type}[{array_size}];
+ var {ptr} = (nint)MemoryHelper.AlignStackPtr({ret_area}, {align});
",
align = align.align_wasm32()
);
- format!("{ptr}")
+ ptr
}
Direction::Export => {
// exports need their return area to be live until the post-return call.
@@ -1526,7 +1723,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
);
self.interface_gen.csharp_gen.needs_export_return_area = true;
- format!("{ptr}")
+ ptr
}
}
}
@@ -1602,8 +1799,8 @@ fn perform_cast(op: &String, cast: &Bitcast) -> String {
Bitcast::F64ToI64 => format!("global::System.BitConverter.DoubleToInt64Bits({op})"),
Bitcast::I32ToI64 => format!("(long) ({op})"),
Bitcast::I64ToI32 => format!("(int) ({op})"),
- Bitcast::I64ToP64 => format!("{op}"),
- Bitcast::P64ToI64 => format!("{op}"),
+ Bitcast::I64ToP64 => op.to_string(),
+ Bitcast::P64ToI64 => op.to_string(),
Bitcast::LToI64 | Bitcast::PToP64 => format!("(long) ({op})"),
Bitcast::I64ToL | Bitcast::P64ToP => format!("(int) ({op})"),
Bitcast::I32ToP
diff --git a/crates/csharp/src/interface.rs b/crates/csharp/src/interface.rs
index 49d55cd8c..21e4dd57b 100644
--- a/crates/csharp/src/interface.rs
+++ b/crates/csharp/src/interface.rs
@@ -597,12 +597,12 @@ impl InterfaceGenerator<'_> {
fn gen_import_src(
&mut self,
func: &Function,
- results: &Vec,
+ results: &[TypeId],
parameter_type: ParameterType,
) -> (String, String) {
let mut bindgen = FunctionBindgen::new(
self,
- &func.item_name(),
+ func.item_name(),
&func.kind,
func.params
.iter()
@@ -615,7 +615,7 @@ impl InterfaceGenerator<'_> {
}
})
.collect(),
- results.clone(),
+ results.to_vec(),
parameter_type,
func.result,
);
@@ -773,7 +773,7 @@ var {async_status_var} = {raw_name}({wasm_params});
let mut bindgen = FunctionBindgen::new(
self,
- &func.item_name(),
+ func.item_name(),
&func.kind,
(0..sig.params.len()).map(|i| format!("p{i}")).collect(),
results,
@@ -1283,7 +1283,7 @@ var {async_status_var} = {raw_name}({wasm_params});
Direction::Export => {
let prefix = key
.map(|s| format!("{}#", self.resolve.name_world_key(s)))
- .unwrap_or_else(String::new);
+ .unwrap_or_default();
uwrite!(
self.csharp_interop_src,
diff --git a/crates/csharp/src/world_generator.rs b/crates/csharp/src/world_generator.rs
index 7c88b662d..343516dea 100644
--- a/crates/csharp/src/world_generator.rs
+++ b/crates/csharp/src/world_generator.rs
@@ -35,6 +35,7 @@ pub struct CSharp {
pub(crate) needs_rep_table: bool,
pub(crate) needs_wit_exception: bool,
pub(crate) needs_async_support: bool,
+ pub(crate) needs_align_stack_ptr: bool,
pub(crate) interface_fragments: HashMap,
pub(crate) world_fragments: Vec,
pub(crate) sizes: SizeAlign,
@@ -388,7 +389,7 @@ impl WorldGenerator for CSharp {
let world = &resolve.worlds[id];
let world_namespace = self.qualifier();
let world_namespace = world_namespace.strip_suffix(".").unwrap();
- let namespace = format!("{world_namespace}");
+ let namespace = world_namespace;
let name = world.name.to_upper_camel_case();
let version = env!("CARGO_PKG_VERSION");
@@ -623,13 +624,28 @@ impl WorldGenerator for CSharp {
src.push_str(&ret_area_str);
}
+ if self.needs_align_stack_ptr {
+ uwrite!(
+ src,
+ "
+ {access} static class MemoryHelper
+ {{
+ {access} static unsafe void* AlignStackPtr(void* stackAddress, uint alignment)
+ {{
+ return (void*)(((int)stackAddress) + ((int)alignment - 1) & -(int)alignment);
+ }}
+ }}
+ "
+ );
+ }
+
if self.needs_rep_table {
- src.push_str("\n");
+ src.push('\n');
src.push_str(include_str!("RepTable.cs"));
}
if !&self.world_fragments.is_empty() {
- src.push_str("\n");
+ src.push('\n');
if self
.world_fragments
@@ -712,7 +728,7 @@ impl WorldGenerator for CSharp {
src.push_str(&include_str!("FutureCommonSupport.cs"));
}
- src.push_str("\n");
+ src.push('\n');
src.push_str("}\n");
@@ -740,11 +756,11 @@ impl WorldGenerator for CSharp {
let (fragments, fully_qualified_namespace) = match stubs {
Stubs::World(fragments) => {
- let fully_qualified_namespace = format!("{namespace}");
+ let fully_qualified_namespace = namespace.to_string();
(fragments, fully_qualified_namespace)
}
Stubs::Interface(fragments) => {
- let fully_qualified_namespace = format!("{stub_namespace}");
+ let fully_qualified_namespace = stub_namespace.clone();
(fragments, fully_qualified_namespace)
}
};
@@ -818,13 +834,9 @@ impl WorldGenerator for CSharp {
// intended to be used non-interactively at link time, the
// linker will have no additional information to resolve such
// ambiguity.
- let (resolve, world) =
- wit_parser::decoding::decode_world(&wit_component::metadata::encode(
- &resolve,
- id,
- self.opts.string_encoding,
- None,
- )?)?;
+ let (resolve, world) = wit_parser::decoding::decode_world(
+ &wit_component::metadata::encode(resolve, id, self.opts.string_encoding, None)?,
+ )?;
let pkg = resolve.worlds[world].package.unwrap();
let mut printer = WitPrinter::default();
@@ -878,7 +890,7 @@ impl WorldGenerator for CSharp {
.collect::>()
.join("\n");
- if body.len() > 0 {
+ if !body.is_empty() {
let body = format!(
"{header}
@@ -943,11 +955,12 @@ fn export_types(r#gen: &mut InterfaceGenerator, types: &[(&str, TypeId)]) {
// We cant use "StructLayout.Pack" as dotnet will use the minimum of the type and the "Pack" field,
// so for byte it would always use 1 regardless of the "Pack".
pub fn dotnet_aligned_array(array_size: usize, required_alignment: usize) -> (usize, String) {
+ let num_elements = array_size.div_ceil(required_alignment);
match required_alignment {
- 1 => (array_size, "byte".to_owned()),
- 2 => ((array_size + 1) / 2, "ushort".to_owned()),
- 4 => ((array_size + 3) / 4, "uint".to_owned()),
- 8 => ((array_size + 7) / 8, "ulong".to_owned()),
+ 1 => (num_elements, "byte".to_owned()),
+ 2 => (num_elements, "ushort".to_owned()),
+ 4 => (num_elements, "uint".to_owned()),
+ 8 => (num_elements, "ulong".to_owned()),
_ => todo!("unsupported return_area_align {}", required_alignment),
}
}
@@ -1033,11 +1046,7 @@ fn interface_name(
);
if let Some(version) = &name.version {
- let v = version
- .to_string()
- .replace('.', "_")
- .replace('-', "_")
- .replace('+', "_");
+ let v = version.to_string().replace(['.', '-', '+'], "_");
ns = format!("{}v{}.", ns, &v);
}
ns
diff --git a/crates/test/src/csharp.rs b/crates/test/src/csharp.rs
index 8e11ca7f7..692221e02 100644
--- a/crates/test/src/csharp.rs
+++ b/crates/test/src/csharp.rs
@@ -99,6 +99,7 @@ impl LanguageMethods for Csharp {
let os = match std::env::consts::OS {
"windows" => "win",
"linux" => std::env::consts::OS,
+ "macos" => "osx",
other => todo!("OS {} not supported", other),
};
diff --git a/tests/runtime/lists/runner.c b/tests/runtime/lists/runner.c
index bddbfda49..4370bccfc 100644
--- a/tests/runtime/lists/runner.c
+++ b/tests/runtime/lists/runner.c
@@ -232,4 +232,33 @@ void exports_runner_run() {
runner_list_f32_free(&ret.f0);
runner_list_f64_free(&ret.f1);
}
+
+ {
+ runner_tuple2_string_list_u8_t headers_data[2];
+ runner_string_set(&headers_data[0].f0, "Content-Type");
+ uint8_t val0[] = "text/plain";
+ headers_data[0].f1.ptr = val0;
+ headers_data[0].f1.len = 10;
+
+ runner_string_set(&headers_data[1].f0, "Content-Length");
+ uint8_t val1[] = "9";
+ headers_data[1].f1.ptr = val1;
+ headers_data[1].f1.len = 1;
+
+ runner_list_tuple2_string_list_u8_t headers = { headers_data, 2 };
+ runner_list_tuple2_string_list_u8_t result;
+ test_lists_to_test_wasi_http_headers_roundtrip(&headers, &result);
+
+ assert(result.len == 2);
+ assert(result.ptr[0].f0.len == 12);
+ assert(memcmp(result.ptr[0].f0.ptr, "Content-Type", 12) == 0);
+ assert(result.ptr[0].f1.len == 10);
+ assert(memcmp(result.ptr[0].f1.ptr, "text/plain", 10) == 0);
+ assert(result.ptr[1].f0.len == 14);
+ assert(memcmp(result.ptr[1].f0.ptr, "Content-Length", 14) == 0);
+ assert(result.ptr[1].f1.len == 1);
+ assert(result.ptr[1].f1.ptr[0] == '9');
+
+ runner_list_tuple2_string_list_u8_free(&result);
+ }
}
diff --git a/tests/runtime/lists/runner.cpp b/tests/runtime/lists/runner.cpp
index cc898f29f..497e4b5bf 100644
--- a/tests/runtime/lists/runner.cpp
+++ b/tests/runtime/lists/runner.cpp
@@ -105,4 +105,18 @@ void exports::runner::Run()
std::vector{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL}
)
));
+
+ {
+ std::vector val0 = {'t', 'e', 'x', 't', '/', 'p', 'l', 'a', 'i', 'n'};
+ std::vector val1 = {'9'};
+ std::vector>> headers;
+ headers.push_back(std::make_tuple(std::string_view("Content-Type"), std::span(val0)));
+ headers.push_back(std::make_tuple(std::string_view("Content-Length"), std::span(val1)));
+ auto result = WasiHttpHeadersRoundtrip(headers);
+ assert(result.size() == 2);
+ assert(equal(std::get<0>(result[0]), "Content-Type"));
+ assert(equal(std::get<1>(result[0]), std::vector{'t', 'e', 'x', 't', '/', 'p', 'l', 'a', 'i', 'n'}));
+ assert(equal(std::get<0>(result[1]), "Content-Length"));
+ assert(equal(std::get<1>(result[1]), std::vector{'9'}));
+ }
}
diff --git a/tests/runtime/lists/runner.cs b/tests/runtime/lists/runner.cs
index f353e56b5..a81bf68a9 100644
--- a/tests/runtime/lists/runner.cs
+++ b/tests/runtime/lists/runner.cs
@@ -159,5 +159,15 @@ public static void Run()
&& s[2] == double.NegativeInfinity
&& s[3] == double.PositiveInfinity);
}
+
+ {
+ var headers = new List<(string, byte[])>() { ("Content-Type", Encoding.UTF8.GetBytes("text/plain")), ("Content-Length", Encoding.UTF8.GetBytes("Not found".Count().ToString())) };
+ var result = IToTestImports.WasiHttpHeadersRoundtrip(headers);
+ for (var i = 0; i < result.Count(); i++)
+ {
+ Debug.Assert(result[i].Item1 == headers[i].Item1);
+ Debug.Assert(headers[i].Item2.SequenceEqual(result[i].Item2));
+ }
+ }
}
}
diff --git a/tests/runtime/lists/runner.go b/tests/runtime/lists/runner.go
index 5defefca5..e64f842cd 100644
--- a/tests/runtime/lists/runner.go
+++ b/tests/runtime/lists/runner.go
@@ -32,6 +32,19 @@ func Run() {
assertEqual(test.ListResult2(), "hello!")
assert(slices.Equal(test.ListResult3(), []string{"hello,", "world!"}))
assert(slices.Equal(test.ListRoundtrip([]uint8{}), []uint8{}))
+
+ {
+ headers := []Tuple2[string, []uint8]{
+ {"Content-Type", []uint8("text/plain")},
+ {"Content-Length", []uint8("9")},
+ }
+ result := test.WasiHttpHeadersRoundtrip(headers)
+ assertEqual(len(result), 2)
+ assertEqual(result[0].F0, "Content-Type")
+ assert(slices.Equal(result[0].F1, []uint8("text/plain")))
+ assertEqual(result[1].F0, "Content-Length")
+ assert(slices.Equal(result[1].F1, []uint8("9")))
+ }
}
func assertEqual[T comparable](a T, b T) {
diff --git a/tests/runtime/lists/runner.rs b/tests/runtime/lists/runner.rs
index b6e77c75d..90e843727 100644
--- a/tests/runtime/lists/runner.rs
+++ b/tests/runtime/lists/runner.rs
@@ -162,5 +162,24 @@ impl Guest for Component {
),
);
}
+
+ {
+ let _guard = Guard::new();
+ let headers = vec![
+ ("Content-Type".to_owned(), b"text/plain".to_vec()),
+ (
+ "Content-Length".to_owned(),
+ "Not found".len().to_string().into_bytes(),
+ ),
+ ];
+ let result = wasi_http_headers_roundtrip(&headers);
+ assert_eq!(result[0].0, "Content-Type");
+ assert_eq!(result[0].1, b"text/plain");
+ assert_eq!(result[1].0, "Content-Length");
+ assert_eq!(
+ result[1].1,
+ "Not found".len().to_string().into_bytes()
+ );
+ }
}
}
diff --git a/tests/runtime/lists/test.c b/tests/runtime/lists/test.c
index e7a91bd97..66a3f54a0 100644
--- a/tests/runtime/lists/test.c
+++ b/tests/runtime/lists/test.c
@@ -161,3 +161,7 @@ void exports_test_lists_to_test_list_minmax_float(test_list_f32_t *a, test_list_
ret->f0 = *a;
ret->f1 = *b;
}
+
+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) {
+ *ret0 = *a;
+}
diff --git a/tests/runtime/lists/test.cpp b/tests/runtime/lists/test.cpp
index c4073bdc9..f05016fff 100644
--- a/tests/runtime/lists/test.cpp
+++ b/tests/runtime/lists/test.cpp
@@ -129,3 +129,7 @@ std::tuple, wit::vector> exports::test::lists::to
std::tuple, wit::vector> exports::test::lists::to_test::ListMinmaxFloat(wit::vector a, wit::vector b) {
return std::make_tuple(std::move(a), std::move(b));
}
+
+wit::vector>> exports::test::lists::to_test::WasiHttpHeadersRoundtrip(wit::vector>> a) {
+ return a;
+}
diff --git a/tests/runtime/lists/test.cs b/tests/runtime/lists/test.cs
index 77ff16410..6df8daabf 100644
--- a/tests/runtime/lists/test.cs
+++ b/tests/runtime/lists/test.cs
@@ -158,5 +158,10 @@ public static string StringRoundtrip(string a)
{
return a;
}
+
+ public static List<(string, byte[])> WasiHttpHeadersRoundtrip(List<(string, byte[])> a)
+ {
+ return a;
+ }
}
}
diff --git a/tests/runtime/lists/test.go b/tests/runtime/lists/test.go
index 455b9db9a..47b300460 100644
--- a/tests/runtime/lists/test.go
+++ b/tests/runtime/lists/test.go
@@ -111,3 +111,7 @@ func ListMinmax64(x []uint64, y []int64) ([]uint64, []int64) {
func ListMinmaxFloat(x []float32, y []float64) ([]float32, []float64) {
return x, y
}
+
+func WasiHttpHeadersRoundtrip(x []Tuple2[string, []uint8]) []Tuple2[string, []uint8] {
+ return x
+}
diff --git a/tests/runtime/lists/test.mbt b/tests/runtime/lists/test.mbt
index 2ae06a18c..6aa24a3fb 100644
--- a/tests/runtime/lists/test.mbt
+++ b/tests/runtime/lists/test.mbt
@@ -124,6 +124,11 @@ pub fn string_roundtrip(a : String) -> String {
a
}
+///|
+pub fn wasi_http_headers_roundtrip(a : Array[(String, FixedArray[Byte])]) -> Array[(String, FixedArray[Byte])] {
+ a
+}
+
///|
pub fn allocated_bytes() -> UInt {
// not quite sure about this
diff --git a/tests/runtime/lists/test.rs b/tests/runtime/lists/test.rs
index a0e858c87..000bfea95 100644
--- a/tests/runtime/lists/test.rs
+++ b/tests/runtime/lists/test.rs
@@ -96,4 +96,10 @@ impl exports::test::lists::to_test::Guest for Component {
fn list_minmax_float(a: Vec, b: Vec) -> (Vec, Vec) {
(a, b)
}
+
+ fn wasi_http_headers_roundtrip(
+ headers: Vec<(String, Vec)>,
+ ) -> Vec<(String, Vec)> {
+ headers
+ }
}
diff --git a/tests/runtime/lists/test.wit b/tests/runtime/lists/test.wit
index e77c8fccc..5b8179175 100644
--- a/tests/runtime/lists/test.wit
+++ b/tests/runtime/lists/test.wit
@@ -27,6 +27,8 @@ interface to-test {
string-roundtrip: func(a: string) -> string;
+ wasi-http-headers-roundtrip: func(a: list>>) -> list>>;
+
allocated-bytes: func() -> u32;
}