Skip to content

Commit

Permalink
wasm abi: impl __call_reducer__ & __describe_module__ using bytes_sin…
Browse files Browse the repository at this point in the history
…k_write
  • Loading branch information
Centril committed Aug 20, 2024
1 parent 9ea5a2f commit 2a9ae03
Show file tree
Hide file tree
Showing 19 changed files with 393 additions and 353 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions crates/bindings-csharp/Codegen/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -443,10 +443,10 @@ public static void Main() {
// Exports only work from the main assembly, so we need to generate forwarding methods.
#if EXPERIMENTAL_WASM_AOT
[UnmanagedCallersOnly(EntryPoint = "__describe_module__")]
public static SpacetimeDB.Internal.Buffer __describe_module__() => SpacetimeDB.Internal.Module.__describe_module__();
public static void __describe_module__(SpacetimeDB.Internal.BytesSink d) => SpacetimeDB.Internal.Module.__describe_module__(d);
[UnmanagedCallersOnly(EntryPoint = "__call_reducer__")]
public static SpacetimeDB.Internal.Buffer __call_reducer__(
public static short __call_reducer__(
uint id,
ulong sender_0,
ulong sender_1,
Expand All @@ -455,7 +455,8 @@ public static SpacetimeDB.Internal.Buffer __call_reducer__(
ulong address_0,
ulong address_1,
SpacetimeDB.Internal.DateTimeOffsetRepr timestamp,
SpacetimeDB.Internal.Buffer args
SpacetimeDB.Internal.BytesSource args,
SpacetimeDB.Internal.BytesSink error
) => SpacetimeDB.Internal.Module.__call_reducer__(
id,
sender_0,
Expand All @@ -465,7 +466,8 @@ SpacetimeDB.Internal.Buffer args
address_0,
address_0,
timestamp,
args
args,
error
);
#endif
}
Expand Down
9 changes: 7 additions & 2 deletions crates/bindings-csharp/Runtime/Exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,16 @@ public class NoSuchBytesException : StdbException
public override string Message => "The provided bytes source or sink does not exist";
}

public class NoSpaceException : StdbException
{
public override string Message => "The provided bytes sink has no more room left";
}

public class UnknownException : StdbException
{
private readonly FFI.CheckedStatus.Errno code;
private readonly FFI.Errno code;

internal UnknownException(FFI.CheckedStatus.Errno code) => this.code = code;
internal UnknownException(FFI.Errno code) => this.code = code;

public override string Message => $"SpacetimeDB error code {code}";
}
43 changes: 16 additions & 27 deletions crates/bindings-csharp/Runtime/Internal/FFI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,8 @@ public readonly record struct BytesSource(uint Handle)
// forwarding in the codegen for `__describe_module__` and `__call_reducer__` exports which both
// use this type.
[StructLayout(LayoutKind.Sequential)]
[NativeMarshalling(typeof(Marshaller))]
public readonly record struct Buffer(uint Handle)
public readonly record struct BytesSink(uint Handle)
{
public static readonly Buffer INVALID = new(uint.MaxValue);

// We need custom marshaller for `Buffer` because we return it by value
// instead of passing an `out` reference, and C# currently doesn't match
// the common Wasm C ABI in that a struct with a single field is supposed
// to have the same ABI as the field itself.
[CustomMarshaller(typeof(Buffer), MarshalMode.Default, typeof(Marshaller))]
internal static class Marshaller
{
public static Buffer ConvertToManaged(uint buf_handle) => new(buf_handle);

public static uint ConvertToUnmanaged(Buffer buf) => buf.Handle;
}
}

#pragma warning disable IDE1006 // Naming Styles - Not applicable to FFI stuff.
Expand All @@ -48,19 +34,21 @@ internal static partial class FFI
#endif
;

public enum Errno : ushort
{
OK = 0,
HOST_CALL_FAILURE = 1,
NO_SUCH_TABLE = 4,
LOOKUP_NOT_FOUND = 2,
NO_SUCH_BYTES = 8,
NO_SPACE = 9,
BUFFER_TOO_SMALL = 11,
UNIQUE_ALREADY_EXISTS = 12,
}

[NativeMarshalling(typeof(Marshaller))]
public struct CheckedStatus
{
public enum Errno : ushort
{
OK = 0,
NO_SUCH_TABLE = 1,
LOOKUP_NOT_FOUND = 2,
UNIQUE_ALREADY_EXISTS = 3,
BUFFER_TOO_SMALL = 4,
NO_SUCH_BYTES = 8,
}

// This custom marshaller takes care of checking the status code
// returned from the host and throwing an exception if it's not 0.
// The only reason it doesn't return `void` is because the C# compiler
Expand All @@ -86,6 +74,7 @@ public static CheckedStatus ConvertToManaged(Errno status)
Errno.UNIQUE_ALREADY_EXISTS => new UniqueAlreadyExistsException(),
Errno.BUFFER_TOO_SMALL => new BufferTooSmallException(),
Errno.NO_SUCH_BYTES => new NoSuchBytesException(),
Errno.NO_SPACE => new NoSpaceException(),
_ => new UnknownException(status),
};
}
Expand Down Expand Up @@ -197,9 +186,9 @@ uint message_len
public static partial short _bytes_source_read(
BytesSource source,
Span<byte> buffer,
ref uint buffer_len_ptr
ref uint buffer_len
);

[LibraryImport(StdbNamespace)]
public static partial Buffer _buffer_alloc([In] byte[] data, uint data_len);
public static partial CheckedStatus _bytes_sink_write(BytesSink sink, Span<byte> buffer, ref uint buffer_len);
}
37 changes: 27 additions & 10 deletions crates/bindings-csharp/Runtime/Internal/Module.cs
Original file line number Diff line number Diff line change
Expand Up @@ -252,15 +252,33 @@ private static byte[] Consume(this BytesSource source)
// but a module should be prepared for it.
case 0:
break;
case (short)(ushort)FFI.Errno.NO_SUCH_BYTES:
throw new NoSuchBytesException();
default:
throw new UnreachableException();
throw new UnknownException((FFI.Errno)(ushort)ret);
}
}
}

private static void Write(this BytesSink sink, byte[] bytes)
{
var start = 0U;
while (true)
{
var written = (uint)bytes.Length;
var buffer = bytes.AsSpan((int)start);
FFI._bytes_sink_write(sink, buffer, ref written);
start += written;
if (start == bytes.Length)
{
return;
}
}
}

#pragma warning disable IDE1006 // Naming Styles - methods below are meant for FFI.

public static Buffer __describe_module__()
public static void __describe_module__(BytesSink description)
{
// replace `module` with a temporary internal module that will register RawModuleDefV8, AlgebraicType and other internal types
// during the RawModuleDefV8.GetSatsTypeInfo() instead of exposing them via user's module.
Expand All @@ -269,17 +287,15 @@ public static Buffer __describe_module__()
// We need this explicit cast here to make `ToBytes` understand the types correctly.
RawModuleDef versioned = new RawModuleDef.V8BackCompat(moduleDef);
var moduleBytes = IStructuralReadWrite.ToBytes(new RawModuleDef.BSATN(), versioned);
var res = FFI._buffer_alloc(moduleBytes, (uint)moduleBytes.Length);
return res;
description.Write(moduleBytes);
}
catch (Exception e)
{
Runtime.Log($"Error while describing the module: {e}", Runtime.LogLevel.Error);
return Buffer.INVALID;
}
}

public static Buffer __call_reducer__(
public static short __call_reducer__(
uint id,
ulong sender_0,
ulong sender_1,
Expand All @@ -288,7 +304,8 @@ public static Buffer __call_reducer__(
ulong address_0,
ulong address_1,
DateTimeOffsetRepr timestamp,
BytesSource args
BytesSource args,
BytesSink error
)
{
// Piece together the sender identity.
Expand All @@ -308,14 +325,14 @@ BytesSource args
{
throw new Exception("Unrecognised extra bytes in the reducer arguments");
}
return /* no exception */
Buffer.INVALID;
return 0; /* no exception */
}
catch (Exception e)
{
var error_str = e.ToString();
var error_bytes = System.Text.Encoding.UTF8.GetBytes(error_str);
return FFI._buffer_alloc(error_bytes, (uint)error_bytes.Length);
error.Write(error_bytes);
return (short)(ushort)FFI.Errno.HOST_CALL_FAILURE;
}
}
}
23 changes: 15 additions & 8 deletions crates/bindings-csharp/Runtime/bindings.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ OPAQUE_TYPEDEF(TableId, uint32_t);
OPAQUE_TYPEDEF(ColId, uint16_t);
OPAQUE_TYPEDEF(IndexType, uint8_t);
OPAQUE_TYPEDEF(LogLevel, uint8_t);
OPAQUE_TYPEDEF(Buffer, uint32_t);
OPAQUE_TYPEDEF(BytesSink, uint32_t);
OPAQUE_TYPEDEF(BytesSource, uint32_t);
OPAQUE_TYPEDEF(RowIter, uint32_t);

Expand Down Expand Up @@ -71,7 +71,8 @@ IMPORT(Status, _iter_advance,
IMPORT(void, _iter_drop, (RowIter iter), (iter));
IMPORT(int16_t, _bytes_source_read, (BytesSource source, uint8_t* buffer_ptr, size_t* buffer_len_ptr),
(source, buffer_ptr, buffer_len_ptr));
IMPORT(Buffer, _buffer_alloc, (const uint8_t* data, uint32_t len), (data, len));
IMPORT(uint16_t, _bytes_sink_write, (BytesSink sink, uint8_t* buffer_ptr, size_t* buffer_len_ptr),
(sink, buffer_ptr, buffer_len_ptr));

#ifndef EXPERIMENTAL_WASM_AOT
static MonoClass* ffi_class;
Expand All @@ -94,7 +95,7 @@ PREINIT(10, startup) {
"FFI export class (SpacetimeDB.Internal.Module) not found");
}

#define EXPORT(ret, name, params, args...) \
#define EXPORT_WITH_MONO_RES(ret, res_code, name, params, args...) \
static MonoMethod* ffi_method_##name; \
PREINIT(20, find_##name) { \
ffi_method_##name = mono_wasm_assembly_find_method(ffi_class, #name, -1); \
Expand All @@ -104,20 +105,26 @@ PREINIT(10, startup) {
MonoObject* res; \
mono_wasm_invoke_method_ref(ffi_method_##name, NULL, (void*[]){args}, \
NULL, &res); \
return *(ret*)mono_object_unbox(res); \
res_code \
}

EXPORT(Buffer, __describe_module__, ());
#define EXPORT(ret, name, params, args...) \
EXPORT_WITH_MONO_RES(ret, return *(ret*)mono_object_unbox(res);, name, params, args) \

EXPORT(Buffer, __call_reducer__,
#define EXPORT_VOID(name, params, args...) \
EXPORT_WITH_MONO_RES(void, return;, name, params, args) \

EXPORT_VOID(__describe_module__, (BytesSink description), &description);

EXPORT(int16_t, __call_reducer__,
(uint32_t id,
uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,
uint64_t address_0, uint64_t address_1,
uint64_t timestamp, Buffer args),
uint64_t timestamp, BytesSource args, BytesSink error),
&id,
&sender_0, &sender_1, &sender_2, &sender_3,
&address_0, &address_1,
&timestamp, &args);
&timestamp, &args, &error);
#endif

// Shims to avoid dependency on WASI in the generated Wasm file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<WasmImport Include="$(SpacetimeNamespace)!_iter_next" />
<WasmImport Include="$(SpacetimeNamespace)!_iter_drop" />
<WasmImport Include="$(SpacetimeNamespace)!_bytes_source_read" />
<WasmImport Include="$(SpacetimeNamespace)!_buffer_alloc" />
<WasmImport Include="$(SpacetimeNamespace)!_bytes_sink_write" />

<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
Expand Down
2 changes: 1 addition & 1 deletion crates/bindings-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ fn gen_reducer(original_function: ItemFn, reducer_name: &str) -> syn::Result<Tok
let register_describer_symbol = format!("__preinit__20_register_describer_{reducer_name}");

let generated_function = quote! {
fn __reducer(__ctx: spacetimedb::ReducerContext, __args: &[u8]) -> spacetimedb::sys::Buffer {
fn __reducer(__ctx: spacetimedb::ReducerContext, __args: &[u8]) -> spacetimedb::ReducerResult {
#(spacetimedb::rt::assert_reducer_arg::<#arg_tys>();)*
#(spacetimedb::rt::assert_reducer_ret::<#ret_ty>();)*
spacetimedb::rt::invoke_reducer(#func_name, __ctx, __args)
Expand Down
Loading

0 comments on commit 2a9ae03

Please sign in to comment.