Repository navigation
Conversation
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
|
I didn't look at this in detail yet, but I did notice it is not using the same component type elaboration infrastructure from the Rust macros. I think this is probably a mistake as it does not seem to me that most of the more complex cases are possible to get right without the explicit representation of type variables. I think pretty much the only thing in |
9ab4272 to
fcd4c0f
Compare
Thanks, reworked it this way. The C# generator now drives off read_wit_type and the elaborated component, with its own equivalents of rtypes/host emit_toplevel in types.rs and host.rs. State is parametrised on the root module (defaulting to Mod), and C# uses resolve_bound_var and emit_fn_hl_name from it. I left BindgenInputParams in the macro crate, since C# takes a WitSource and calls read_wit_type, so the source handling is already shared and the parser's only user is the macro syntax. |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
🟡 Changes recommended
Supported inputs can produce uncompilable bindings, and decoding and structural hashing defects remain.
11 open issues
Resolve CLI WIT paths before invoking the shared reader · New Hash byte arrays consistently with enumerable byte lists · New Allow zero-byte list elements with separate allocation limits · New Generate compilable C# for empty tuples · New Avoid enum and flags members matching enclosing types · New Prevent generated type parameter name collisions · New Compute empty and full flags masks safely · New Avoid imported method names matching interface types · New Prevent export names from conflicting with generated members · New Validate and escape namespace segments for valid C# · New Avoid variant case names matching enclosing record names · New
What changed in this PR
Adds C# host bindings for Hyperlight components, sharing WIT type resolution and wire naming with the Rust generators.
Changes:
- C# generation and runtime support for component values and calls.
- Little-endian encoding and interface-qualified wire names.
- Cross-language tests, documentation, CI and publishing integration.
| File | Description |
|---|---|
| src/tests/component_wire/wire.wit | Shared wire-format fixture. |
| src/tests/component_wire/vectors.txt | Cross-language encoding vectors. |
| src/tests/component_wire/csharp/WireTests.cs | Encoding and decoding checks. |
| src/tests/component_wire/csharp/RuntimeTests.cs | Runtime validation checks. |
| src/tests/component_wire/csharp/Program.cs | Test runner and assertions. |
| src/tests/component_wire/csharp/Fakes.cs | Fake guest and registry. |
| src/tests/component_wire/csharp/EdgeTests.cs | Naming and type checks. |
| src/tests/component_wire/csharp/edge.wit | Edge-case WIT fixture. |
| src/tests/component_wire/csharp/ComponentTests.csproj | Generates and compiles test bindings. |
| src/hyperlight_host/tests/component_wire_test.rs | Rust wire-vector checks. |
| src/hyperlight_component_util/src/util.rs | WIT path resolution fallback. |
| src/hyperlight_component_util/src/host.rs | Host instance naming context. |
| src/hyperlight_component_util/src/hl.rs | Wire names and little-endian encoding. |
| src/hyperlight_component_util/src/guest.rs | Guest instance naming context. |
| src/hyperlight_component_util/src/emit.rs | Shared generation state. |
| src/hyperlight_component_csharp/src/types.rs | Type discovery and validation. |
| src/hyperlight_component_csharp/src/tests.rs | Generator tests. |
| src/hyperlight_component_csharp/src/Runtime.cs | C# codecs and support types. |
| src/hyperlight_component_csharp/src/names.rs | C# identifier helpers. |
| src/hyperlight_component_csharp/src/lib.rs | Generator API and namespaces. |
| src/hyperlight_component_csharp/src/host.rs | Call wrappers and import registration. |
| src/hyperlight_component_csharp/src/codec.rs | Type and codec generation. |
| src/hyperlight_component_csharp/README.md | Generator usage documentation. |
| src/hyperlight_component_csharp/examples/csharp_bindgen.rs | Command-line generation example. |
| src/hyperlight_component_csharp/Cargo.toml | Crate definition. |
| Justfile | Build and test integration. |
| docs/README.md | Wire-format documentation link. |
| docs/how-to-make-releases.md | Published crate list. |
| docs/component-wire-format.md | Wire contract and C# mappings. |
| CHANGELOG.md | Features and breaking changes. |
| Cargo.toml | Workspace registration. |
| Cargo.lock | Crate dependency entry. |
| .github/workflows/dep_code_checks.yml | C# test execution. |
| .github/workflows/CargoPublish.yml | Crate publishing integration. |
Review effort: Balanced
| let result = generate_from_path(Path::new(wit), None, namespace) | ||
| .map_err(|e| e.to_string()) |
| case byte[] bytes: | ||
| { | ||
| var hash = new HashCode(); | ||
| hash.AddBytes(bytes); |
| // Every element takes at least one byte. | ||
| if (count > (uint)(_data.Length - _position)) | ||
| { | ||
| throw new WireFormatException($"list length {count} exceeds remaining input"); |
| Value::Tuple(ts) => { | ||
| let types = ts.iter().map(|t| ty(s, t)).collect::<Vec<_>>(); | ||
| format!("({})", types.join(", ")) |
| src.line(format!("{} = {i},", pascal(c.name))); | ||
| } | ||
| src.close(); | ||
| } | ||
| Value::Flags(flags) => { | ||
| src.line("[global::System.Flags]"); | ||
| src.line(format!("public enum {name} : uint")); | ||
| src.open(); | ||
| if flags.iter().all(|f| pascal(f.name) != "None") { | ||
| src.line("None = 0,"); | ||
| } | ||
| for (i, f) in flags.iter().enumerate() { | ||
| src.line(format!("{} = 1u << {i},", pascal(f.name))); |
| /// Hex literal with the low `count` bits set. WIT flags have at most 32 | ||
| /// members. | ||
| fn flags_mask(count: usize) -> String { | ||
| format!("0x{:X}", u32::MAX >> (32 - count)) |
| if i > 0 { | ||
| src.line(""); | ||
| } | ||
| src.line(format!("{};", signature(s, group, f, &pascal(function)))); |
| src.line(format!("public {name}({RT}.IGuestCaller caller)")); | ||
| src.open(); | ||
| src.line("_caller = caller;"); | ||
| let export_names = group_member_names(s, exports); |
| if namespace.is_empty() | ||
| || namespace | ||
| .split('.') | ||
| .any(|part| part.is_empty() || !part.chars().all(|c| c.is_alphanumeric() || c == '_')) |
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
State takes its output type as a parameter, which defaults to Mod. new, clone and resolve_bound_var work for any output, and so does emit_fn_hl_name. read_wit_type also works outside a cargo build. Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
fcd4c0f to
92cc474
Compare
Benchmark ResultsMeasured commit: kvm / amd (Linux) (➖ stable)No benchmark improved or regressed. Benchmark Resultsfunction_call_codec
payload_allocation
sandboxes
slot_pool
snapshot_files
virtq_readonly
virtq_readwrite
kvm / intel (Linux) (➖ stable)No benchmark improved or regressed. Benchmark Resultsfunction_call_codec
payload_allocation
sandboxes
slot_pool
snapshot_files
virtq_readonly
virtq_readwrite
mshv3 / amd (Linux) (➖ stable)No benchmark improved or regressed. Benchmark Resultsfunction_call_codec
payload_allocation
sandboxes
slot_pool
snapshot_files
virtq_readonly
virtq_readwrite
mshv3 / intel (Linux) (➖ stable)No benchmark improved or regressed. Benchmark Resultsfunction_call_codec
payload_allocation
sandboxes
slot_pool
snapshot_files
virtq_readonly
virtq_readwrite
hyperv-ws2025 / amd (Windows) (➖ stable)No benchmark improved or regressed. Benchmark Resultsfunction_call_codec
payload_allocation
sandboxes
slot_pool
snapshot_files
virtq_readonly
virtq_readwrite
hyperv-ws2025 / intel (Windows) (➖ stable)No benchmark improved or regressed. Benchmark Resultsfunction_call_codec
payload_allocation
sandboxes
slot_pool
snapshot_files
virtq_readonly
virtq_readwrite
Reported by |

Adds new crate hyperlight-component-csharp which generates c# host bindings from wit. The intended usage is a .NET hyperlight-wasm library, so .net hosts don't need to write rust.
The crate is in rust and not .net because it reuses the component type elaboration from hyperlight-component-util, the same one the rust bindings use. Both generators resolve types and name wire functions the same way. In the future the remaining core (encoding) could be shared too, so they cannot drift.
breakign changes:
<instance>#<function>, such aswasi:http/types@0.2.0#[method]fields.getrather thanwasi::http::[method]fields.get. The old name dropped the interface and version, so functions in different interfaces or versions of one package could collide.