Skip to content
Open
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
28 changes: 22 additions & 6 deletions crates/csharp/src/csproj.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::Result;
use std::{fs, path::PathBuf};
use std::{env, fs, path::PathBuf};

use heck::ToUpperCamelCase;

Expand Down Expand Up @@ -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#"
<ItemGroup>
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
<PackageReference Include="runtime.{os}-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="{ilc_version}" />
<PackageReference Include="runtime.{os}-{arch}.Microsoft.DotNet.ILCompiler.LLVM" Version="{ilc_version}" />
</ItemGroup>"#),
);

let local_source = match env::var("ILC_PACKAGES_PATH") {
Ok(path) => format!(r#"<add key="local-ilc" value="{path}" />"#),
Err(_) => String::new(),
};

fs::write(
self.dir.join("nuget.config"),
r#"<?xml version="1.0" encoding="utf-8"?>
format!(
r#"<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<!-- Store the packages where they can be shared between tests -->
Expand All @@ -126,11 +141,12 @@ impl CSProjectLLVMBuilder {
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<clear />
{local_source}
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
<!--<add key="dotnet-experimental" value="C:\github\runtimelab\artifacts\packages\Debug\Shipping" />-->
</packageSources>
</configuration>"#,
</configuration>"#
),
)?;
}

Expand Down
527 changes: 362 additions & 165 deletions crates/csharp/src/function.rs

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions crates/csharp/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,12 +597,12 @@ impl InterfaceGenerator<'_> {
fn gen_import_src(
&mut self,
func: &Function,
results: &Vec<TypeId>,
results: &[TypeId],
parameter_type: ParameterType,
) -> (String, String) {
let mut bindgen = FunctionBindgen::new(
self,
&func.item_name(),
func.item_name(),
&func.kind,
func.params
.iter()
Expand All @@ -615,7 +615,7 @@ impl InterfaceGenerator<'_> {
}
})
.collect(),
results.clone(),
results.to_vec(),
parameter_type,
func.result,
);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
55 changes: 32 additions & 23 deletions crates/csharp/src/world_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, InterfaceTypeAndFragments>,
pub(crate) world_fragments: Vec<InterfaceFragment>,
pub(crate) sizes: SizeAlign,
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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)
}
};
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -878,7 +890,7 @@ impl WorldGenerator for CSharp {
.collect::<Vec<_>>()
.join("\n");

if body.len() > 0 {
if !body.is_empty() {
let body = format!(
"{header}

Expand Down Expand Up @@ -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),
}
}
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions crates/test/src/csharp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};

Expand Down
29 changes: 29 additions & 0 deletions tests/runtime/lists/runner.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
14 changes: 14 additions & 0 deletions tests/runtime/lists/runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,18 @@ void exports::runner::Run()
std::vector<double>{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL}
)
));

{
std::vector<uint8_t> val0 = {'t', 'e', 'x', 't', '/', 'p', 'l', 'a', 'i', 'n'};
std::vector<uint8_t> val1 = {'9'};
std::vector<std::tuple<std::string_view, std::span<const uint8_t>>> headers;
headers.push_back(std::make_tuple(std::string_view("Content-Type"), std::span<const uint8_t>(val0)));
headers.push_back(std::make_tuple(std::string_view("Content-Length"), std::span<const uint8_t>(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<uint8_t>{'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<uint8_t>{'9'}));
}
}
10 changes: 10 additions & 0 deletions tests/runtime/lists/runner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
}
13 changes: 13 additions & 0 deletions tests/runtime/lists/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
19 changes: 19 additions & 0 deletions tests/runtime/lists/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
);
}
}
}
4 changes: 4 additions & 0 deletions tests/runtime/lists/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
4 changes: 4 additions & 0 deletions tests/runtime/lists/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,7 @@ std::tuple<wit::vector<uint64_t>, wit::vector<int64_t>> exports::test::lists::to
std::tuple<wit::vector<float>, wit::vector<double>> exports::test::lists::to_test::ListMinmaxFloat(wit::vector<float> a, wit::vector<double> b) {
return std::make_tuple(std::move(a), std::move(b));
}

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) {
return a;
}
5 changes: 5 additions & 0 deletions tests/runtime/lists/test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,5 +158,10 @@ public static string StringRoundtrip(string a)
{
return a;
}

public static List<(string, byte[])> WasiHttpHeadersRoundtrip(List<(string, byte[])> a)
{
return a;
}
}
}
4 changes: 4 additions & 0 deletions tests/runtime/lists/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
5 changes: 5 additions & 0 deletions tests/runtime/lists/test.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading