Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
59 changes: 59 additions & 0 deletions crates/csharp/src/FutureCommonSupport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Helpers for future support.
*/

public readonly struct WaitableStatus (int status)
{
public int State => status & 0xf;
public int Count => (int)(status >> 4);
public bool IsBlocked => status == -1;
public bool IsCompleted => State == 0;
public bool IsDropped => State == 1;
}

public enum EventCode
{
None,
Subtask,
StreamRead,
StreamWrite,
FutureRead,
FutureWrite,
Cancel,
}

public readonly struct EventWaitable
{
public EventWaitable(EventCode eventCode, int waitable, int code)
{
Event = eventCode;
Waitable = waitable;
Status = new WaitableStatus(code);
}
public readonly EventCode Event;
public readonly int Waitable;
public readonly int Code;

public readonly WaitableStatus Status;
}

public partial class WaitableSet(int handle) : IDisposable
{
public int Handle { get; } = handle;

void Dispose(bool _disposing)
{
{{interop_name}}.WaitableSetDrop(Handle);
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

~WaitableSet()
{
Dispose(false);
}
}
49 changes: 49 additions & 0 deletions crates/csharp/src/FutureReaderSupport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* Helpers for future reader support.
*/

public abstract class FutureReader(int handle) : IDisposable // : TODO Waitable
{
public int Handle { get; } = handle;

// TODO: Generate per type for this instrinsic.
public Task Read()
{
// TODO: Generate for the interop name and the namespace.

var status = new WaitableStatus(ReadInternal());
if (status.IsBlocked)
{
//TODO: store somewhere so we can complete it later.
var tcs = new TaskCompletionSource();

return tcs.Task;
}
if (status.IsCompleted)
{
return Task.CompletedTask;
}

throw new NotImplementedException();
}

void Dispose(bool _disposing)
{
// Free unmanaged resources if any.
Drop();
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

~FutureReader()
{
Dispose(false);
}

protected abstract int ReadInternal();
protected abstract void Drop();
}
44 changes: 44 additions & 0 deletions crates/csharp/src/FutureWriterSupport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Helpers for future writer support.
*/

public abstract class FutureWriter(int handle) // : TODO Waitable
{
public int Handle { get; } = handle;

// TODO: Generate per type for this instrinsic.
public Task Write()
{
// TODO: Generate for the interop name.
var status = new WaitableStatus(Write(Handle, IntPtr.Zero));
if (status.IsBlocked)
{
//TODO: store somewhere so we can complete it later.
var tcs = new TaskCompletionSource();
return tcs.Task;
}

throw new NotImplementedException();
}

protected abstract void Drop();

void Dispose(bool _disposing)
{
// Free unmanaged resources if any.
Drop();
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

~FutureWriter()
{
Dispose(false);
}

protected abstract int Write(int handle, IntPtr buffer);
}
14 changes: 7 additions & 7 deletions crates/csharp/src/csproj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,10 @@ impl CSProjectLLVMBuilder {
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-*" />
</ItemGroup>
"#),
<ItemGroup>
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
<PackageReference Include="runtime.{os}-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
</ItemGroup>"#),
);

fs::write(
Expand Down Expand Up @@ -153,8 +152,9 @@ impl CSProjectLLVMBuilder {
}

csproj.push_str(
r#"</Project>
"#,
r#"
</Project>
"#,
);

fs::write(self.dir.join(format!("{camel}.csproj")), csproj)?;
Expand Down
48 changes: 43 additions & 5 deletions crates/csharp/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub(crate) struct FunctionBindgen<'a, 'b> {
fixed_statments: Vec<Fixed>,
parameter_type: ParameterType,
result_type: Option<Type>,
pub(crate) resource_type_name: Option<String>,
}

impl<'a, 'b> FunctionBindgen<'a, 'b> {
Expand Down Expand Up @@ -70,6 +71,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
fixed_statments: Vec::new(),
parameter_type: parameter_type,
result_type: result_type,
resource_type_name: None,
}
}

Expand Down Expand Up @@ -1062,9 +1064,32 @@ impl Bindgen for FunctionBindgen<'_, '_> {
None => operands.join(", "),
};

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());

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) => {
format!(
".{}",
self.interface_gen.csharp_gen.all_resources[resource_type_id]
.name
.to_upper_camel_case()
)
}
_ => String::new(),
};

uwriteln!(
self.src,
"{assignment} {func_name}WasmInterop.wasmImport{func_name}({operands});"
"{assignment} {interop_name}{resource_type_name}.{func_name}WasmInterop.wasmImport{func_name}({operands});"
);

if let Some(buffer) = async_return_buffer {
Expand Down Expand Up @@ -1364,6 +1389,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
} else {
uwriteln!(self.src, "var {resource} = ({export_name}) {export_name}.repTable.Get({op});");
}
self.resource_type_name = Some(export_name);
}
}
results.push(resource);
Expand All @@ -1375,15 +1401,27 @@ impl Bindgen for FunctionBindgen<'_, '_> {

Instruction::FutureLower { .. } => {
let op = &operands[0];
self.interface_gen.add_future(self.func_name);

results.push(format!("{op}.Handle"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FutureReader should probably have a TakeHandle() method that zeros out the handle field (and asserts that it wasn't already zero) before returning the original value so that the application won't accidentally try to use the no-longer-valid handle.

}

Instruction::AsyncTaskReturn { name: _, params: _ } => {
uwriteln!(self.src, "// TODO_task_cancel.forget();");
}

Instruction::FutureLift { .. }
| Instruction::StreamLower { .. }
Instruction::FutureLift { payload: _, ty: _ } => {
// TODO get the prefix for the type
let sig_type_name = "Void";
uwriteln!(self.src, "var reader = new {}.FutureReader{}({});", self.interface_gen.name, sig_type_name, operands[0]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend using e.g. self.locals.tmp("reader") here to generate a unique variable name so as to avoid clashes.

self.interface_gen.csharp_gen.needs_future_reader_support = true;
results.push("reader".to_string());

self.interface_gen.add_future(self.func_name);
self.interface_gen.csharp_gen.needs_future_reader_support = true;
}

Instruction::StreamLower { .. }
| Instruction::StreamLift { .. }
| Instruction::ErrorContextLower { .. }
| Instruction::ErrorContextLift { .. }
Expand Down Expand Up @@ -1418,7 +1456,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
uwrite!(
self.src,
"
var {ret_area} = stackalloc {element_type}[{array_size}+1];
var {ret_area} = stackalloc {element_type}[{array_size} + 1];
var {ptr} = ((int){ret_area}) + ({align} - 1) & -{align};
",
align = align.align_wasm32()
Expand Down Expand Up @@ -1487,7 +1525,7 @@ impl Bindgen for FunctionBindgen<'_, '_> {
}

/// Dereference any number `TypeDefKind::Type` aliases to retrieve the target type.
fn dealias(resolve: &Resolve, mut id: TypeId) -> TypeId {
pub fn dealias(resolve: &Resolve, mut id: TypeId) -> TypeId {
loop {
match &resolve.types[id].kind {
TypeDefKind::Type(Type::Id(that_id)) => id = *that_id,
Expand Down
Loading